From 679e476679d2d0b033c95e264b34b9946d5b101b Mon Sep 17 00:00:00 2001 From: Patrick Rohr Date: Mon, 21 Feb 2022 16:24:45 +0100 Subject: Fix testIpClientIsNotStartedWhenLinkIsDown This test is breaking when requesting a network with an EthernetNetworkSpecifier after aosp/1795391 was submitted (which fixed another bug that hid this one). Test: atest EthernetNetworkFactoryTest Change-Id: I31f10f48cd03cdef29f21bbce790db68add071a8 --- .../android/server/ethernet/EthernetNetworkFactoryTest.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java index 6cb13ef..33831a5 100644 --- a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java +++ b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java @@ -31,6 +31,7 @@ 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; @@ -455,16 +456,21 @@ public class EthernetNetworkFactoryTest { 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); - - verify(mDeps, never()).makeIpClient(any(), any(), any()); + mNetFactory.releaseNetworkFor(specificNetRequest); mNetFactory.updateInterfaceLinkState(TEST_IFACE, true, NULL_LISTENER); - verify(mDeps).makeIpClient(any(), eq(TEST_IFACE), any()); + // TODO: change to once when b/191854824 is fixed. + verify(mDeps, times(2)).makeIpClient(any(), eq(TEST_IFACE), any()); } @Test -- cgit v1.2.3 From 47b8ff501c0730f93c2c829bf8c531c9f046f8ef Mon Sep 17 00:00:00 2001 From: Patrick Rohr Date: Mon, 10 Jan 2022 14:39:39 +0100 Subject: Enable EthernetServiceTests in presubmit Test: run presubmit Bug: 213863997 Change-Id: I6736198539a37d538d5d53d64c16abe572bf3982 --- TEST_MAPPING | 5 +++++ tests/Android.bp | 1 + 2 files changed, 6 insertions(+) diff --git a/TEST_MAPPING b/TEST_MAPPING index a6a02d5..24c152b 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,4 +1,9 @@ { + "presubmit": [ + { + "name": "EthernetServiceTests" + } + ], "imports": [ { "path": "packages/modules/Connectivity" diff --git a/tests/Android.bp b/tests/Android.bp index 93a8f6c..28b13c5 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -40,4 +40,5 @@ android_test { "services.core", "services.net", ], + test_suites: ["general-tests"], } -- cgit v1.2.3 From fcc56fc1588a9f5f884cd8b3eaa63bcea610f8b5 Mon Sep 17 00:00:00 2001 From: James Mattis Date: Thu, 17 Feb 2022 13:08:20 -0800 Subject: Using a builder for eth requests Updating ethernet classes to use a builder when creating an EthernetNetworkUpdateRequest and also changing to use IpConfiguration instead of StaticIpConfiguration for the UpdateConfiguration API. Bug: 220017952 Bug: 210487893 Bug: 210485380 Test: atest EthernetServiceTests Change-Id: I2647115bf867dfaa3f3dadf00e3c875aa7e8d88f --- .../android/server/ethernet/EthernetServiceImpl.java | 2 +- java/com/android/server/ethernet/EthernetTracker.java | 18 +++++++----------- .../server/ethernet/EthernetServiceImplTest.java | 9 +++++---- .../android/server/ethernet/EthernetTrackerTest.java | 11 +++++++---- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java index c1c6d3a..fed500f 100644 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ b/java/com/android/server/ethernet/EthernetServiceImpl.java @@ -239,7 +239,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { // TODO: validate that iface is listed in overlay config_ethernet_interfaces mTracker.updateConfiguration( - iface, request.getIpConfig(), request.getNetworkCapabilities(), listener); + iface, request.getIpConfiguration(), request.getNetworkCapabilities(), listener); } @Override diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java index 2571fe6..794b5d1 100644 --- a/java/com/android/server/ethernet/EthernetTracker.java +++ b/java/com/android/server/ethernet/EthernetTracker.java @@ -229,18 +229,18 @@ public class EthernetTracker { @VisibleForTesting(visibility = PACKAGE) protected void updateConfiguration(@NonNull final String iface, - @NonNull final StaticIpConfiguration staticIpConfig, + @NonNull final IpConfiguration ipConfig, @NonNull final NetworkCapabilities capabilities, @Nullable final IEthernetNetworkManagementListener listener) { if (DBG) { Log.i(TAG, "updateConfiguration, iface: " + iface + ", capabilities: " + capabilities - + ", staticIpConfig: " + staticIpConfig); + + ", ipConfig: " + ipConfig); } - final IpConfiguration ipConfig = createIpConfiguration(staticIpConfig); - writeIpConfiguration(iface, ipConfig); + final IpConfiguration localIpConfig = new IpConfiguration(ipConfig); + writeIpConfiguration(iface, localIpConfig); mNetworkCapabilities.put(iface, capabilities); mHandler.post(() -> { - mFactory.updateInterface(iface, ipConfig, capabilities, listener); + mFactory.updateInterface(iface, localIpConfig, capabilities, listener); broadcastInterfaceStateChange(iface); }); } @@ -715,13 +715,9 @@ public class EthernetTracker { return createIpConfiguration(staticIpConfigBuilder.build()); } - static IpConfiguration createIpConfiguration( + private static IpConfiguration createIpConfiguration( @NonNull final StaticIpConfiguration staticIpConfig) { - final IpConfiguration ret = new IpConfiguration(); - ret.setIpAssignment(IpAssignment.STATIC); - ret.setProxySettings(ProxySettings.NONE); - ret.setStaticIpConfiguration(staticIpConfig); - return ret; + return new IpConfiguration.Builder().setStaticIpConfiguration(staticIpConfig).build(); } private IpConfiguration getOrCreateIpConfiguration(String iface) { diff --git a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java index 0ac28c4..2829753 100644 --- a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java +++ b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java @@ -32,7 +32,6 @@ import android.net.IEthernetNetworkManagementListener; import android.net.EthernetNetworkUpdateRequest; import android.net.IpConfiguration; import android.net.NetworkCapabilities; -import android.net.StaticIpConfiguration; import android.os.Handler; import androidx.test.filters.SmallTest; @@ -49,8 +48,10 @@ import org.mockito.MockitoAnnotations; public class EthernetServiceImplTest { private static final String TEST_IFACE = "test123"; private static final EthernetNetworkUpdateRequest UPDATE_REQUEST = - new EthernetNetworkUpdateRequest( - new StaticIpConfiguration(), new NetworkCapabilities.Builder().build()); + new EthernetNetworkUpdateRequest.Builder() + .setIpConfiguration(new IpConfiguration()) + .setNetworkCapabilities(new NetworkCapabilities.Builder().build()) + .build(); private static final IEthernetNetworkManagementListener NULL_LISTENER = null; private EthernetServiceImpl mEthernetServiceImpl; @Mock private Context mContext; @@ -214,7 +215,7 @@ public class EthernetServiceImplTest { mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); verify(mEthernetTracker).updateConfiguration( eq(TEST_IFACE), - eq(UPDATE_REQUEST.getIpConfig()), + eq(UPDATE_REQUEST.getIpConfiguration()), eq(UPDATE_REQUEST.getNetworkCapabilities()), eq(NULL_LISTENER)); } diff --git a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java index 14f34d0..e1a1a8e 100644 --- a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java +++ b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java @@ -324,15 +324,18 @@ public class EthernetTrackerTest { @Test public void testUpdateConfiguration() { final NetworkCapabilities capabilities = new NetworkCapabilities.Builder().build(); - final StaticIpConfiguration staticIpConfig = new StaticIpConfiguration(); + final LinkAddress linkAddr = new LinkAddress("192.0.2.2/25"); + final StaticIpConfiguration staticIpConfig = + new StaticIpConfiguration.Builder().setIpAddress(linkAddr).build(); + final IpConfiguration ipConfig = + new IpConfiguration.Builder().setStaticIpConfiguration(staticIpConfig).build(); final IEthernetNetworkManagementListener listener = null; - tracker.updateConfiguration(TEST_IFACE, staticIpConfig, capabilities, listener); + tracker.updateConfiguration(TEST_IFACE, ipConfig, capabilities, listener); waitForIdle(); verify(mFactory).updateInterface( - eq(TEST_IFACE), eq(EthernetTracker.createIpConfiguration(staticIpConfig)), - eq(capabilities), eq(listener)); + eq(TEST_IFACE), eq(ipConfig), eq(capabilities), eq(listener)); } @Test -- cgit v1.2.3 From b7c9537a271dca8917a0faf888afeaa5678770a1 Mon Sep 17 00:00:00 2001 From: James Mattis Date: Mon, 28 Feb 2022 14:08:12 -0800 Subject: Ethernet validate if iface is tracked on handler Removing synchronous validation of whether a particular interface is tracked or not and instead relying on asynchronous validation and callbacks. An interface can be in the midst of being provisioned and checking if it is tracked sychronously before provisioning is complete will erroneously throw an error for a call that would have been successful when executed on the ethernet handler thread. Bug: 210487893 Bug: 210485380 Test: atest EthernetServiceTests Change-Id: Ib70312a240cab412a54ca7f598893aa9b1e108fd --- .../server/ethernet/EthernetNetworkFactory.java | 26 ++++++++++++---------- .../server/ethernet/EthernetServiceImpl.java | 13 +++-------- .../ethernet/EthernetNetworkFactoryTest.java | 14 ++++++++++++ .../server/ethernet/EthernetServiceImplTest.java | 24 -------------------- 4 files changed, 31 insertions(+), 46 deletions(-) diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java index f266386..8ce27a6 100644 --- a/java/com/android/server/ethernet/EthernetNetworkFactory.java +++ b/java/com/android/server/ethernet/EthernetNetworkFactory.java @@ -220,20 +220,17 @@ public class EthernetNetworkFactory extends NetworkFactory { @NonNull final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, @Nullable final IEthernetNetworkManagementListener listener) { - enforceInterfaceIsTracked(ifaceName); + if (!hasInterface(ifaceName)) { + maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); + return; + } + final NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName); iface.updateInterface(ipConfig, capabilities, listener); 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); @@ -272,10 +269,8 @@ public class EthernetNetworkFactory extends NetworkFactory { @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) protected boolean updateInterfaceLinkState(@NonNull final String ifaceName, final boolean up, @Nullable final IEthernetNetworkManagementListener listener) { - if (!mTrackingInterfaces.containsKey(ifaceName)) { - maybeSendNetworkManagementCallback(listener, null, - new EthernetNetworkManagementException( - ifaceName + " can't be updated as it is not available.")); + if (!hasInterface(ifaceName)) { + maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); return false; } @@ -287,6 +282,13 @@ public class EthernetNetworkFactory extends NetworkFactory { return iface.updateLinkState(up, listener); } + private void maybeSendNetworkManagementCallbackForUntracked( + String ifaceName, IEthernetNetworkManagementListener listener) { + maybeSendNetworkManagementCallback(listener, null, + new EthernetNetworkManagementException( + ifaceName + " can't be updated as it is not available.")); + } + @VisibleForTesting protected boolean hasInterface(String ifaceName) { return mTrackingInterfaces.containsKey(ifaceName); diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java index fed500f..f80f6a0 100644 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ b/java/com/android/server/ethernet/EthernetServiceImpl.java @@ -66,12 +66,6 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { methodName + " is only available on automotive devices."); } - private void enforceInterfaceIsTracked(final @NonNull String iface) { - if(!mTracker.isTrackingInterface(iface)) { - throw new UnsupportedOperationException("The given iface is not currently tracked."); - } - } - private boolean checkUseRestrictedNetworksPermission() { return PermissionUtils.checkAnyPermissionOf(mContext, android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS); @@ -220,13 +214,12 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { */ private void validateNetworkManagementState(@NonNull final String iface, final @NonNull String methodName) { + Objects.requireNonNull(iface, "Pass a non-null iface."); + Objects.requireNonNull(methodName, "Pass a non-null methodName."); + enforceAutomotiveDevice(methodName); enforceNetworkManagementPermission(); logIfEthernetNotStarted(); - - Objects.requireNonNull(iface, "Pass a non-null iface."); - Objects.requireNonNull(methodName, "Pass a non-null methodName."); - enforceInterfaceIsTracked(iface); } @Override diff --git a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java index 33831a5..61425bf 100644 --- a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java +++ b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java @@ -763,4 +763,18 @@ public class EthernetNetworkFactoryTest { 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"); + } } diff --git a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java index 2829753..e74a5a3 100644 --- a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java +++ b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java @@ -156,30 +156,6 @@ public class EthernetServiceImplTest { }); } - @Test - public void testUpdateConfigurationRejectsWithUntrackedIface() { - shouldTrackIface(TEST_IFACE, false); - assertThrows(UnsupportedOperationException.class, () -> { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - @Test - public void testConnectNetworkRejectsWithUntrackedIface() { - shouldTrackIface(TEST_IFACE, false); - assertThrows(UnsupportedOperationException.class, () -> { - mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER); - }); - } - - @Test - public void testDisconnectNetworkRejectsWithUntrackedIface() { - shouldTrackIface(TEST_IFACE, false); - assertThrows(UnsupportedOperationException.class, () -> { - mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - }); - } - private void denyManageEthPermission() { doThrow(new SecurityException("")).when(mContext) .enforceCallingOrSelfPermission( -- cgit v1.2.3 From 9a47ae7fb8cfc56fa0ca89dbe95054855f81d5ea Mon Sep 17 00:00:00 2001 From: James Mattis Date: Sat, 26 Feb 2022 22:16:46 -0800 Subject: Eth Management APIs to Support TEST Interfaces Updating Ethernet Network Management APIs to allow support for test interfaces when the caller has the MANAGE_TEST_NETWORKS permission, test interfaces are being tracked in ethernet and if updating a network's capabilities, they include the TEST transport. Bug: 210487893 Test: atest EthernetServiceTests atest CtsNetTestCasesLatestSdk :android.net.cts.EthernetManagerTest Change-Id: I0e0bc9632d9b3d5d61f23e74150586f42c0b5bd2 --- .../server/ethernet/EthernetServiceImpl.java | 34 +++++++- .../android/server/ethernet/EthernetTracker.java | 14 ++++ .../server/ethernet/EthernetServiceImplTest.java | 92 ++++++++++++++++++++++ .../server/ethernet/EthernetTrackerTest.java | 41 +++++++++- 4 files changed, 176 insertions(+), 5 deletions(-) diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java index f80f6a0..7f77e5e 100644 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ b/java/com/android/server/ethernet/EthernetServiceImpl.java @@ -16,6 +16,8 @@ package com.android.server.ethernet; +import static android.net.NetworkCapabilities.TRANSPORT_TEST; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -26,6 +28,7 @@ import android.net.IEthernetNetworkManagementListener; import android.net.ITetheredInterfaceCallback; import android.net.EthernetNetworkUpdateRequest; import android.net.IpConfiguration; +import android.net.NetworkCapabilities; import android.os.Binder; import android.os.Handler; import android.os.RemoteException; @@ -206,6 +209,12 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { "EthernetServiceImpl"); } + private void enforceManageTestNetworksPermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.MANAGE_TEST_NETWORKS, + "EthernetServiceImpl"); + } + /** * Validate the state of ethernet for APIs tied to network management. * @@ -217,18 +226,35 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { Objects.requireNonNull(iface, "Pass a non-null iface."); Objects.requireNonNull(methodName, "Pass a non-null methodName."); - enforceAutomotiveDevice(methodName); - enforceNetworkManagementPermission(); + // Only bypass the permission/device checks if this is a valid test interface. + if (mTracker.isValidTestInterface(iface)) { + enforceManageTestNetworksPermission(); + Log.i(TAG, "Ethernet network management API used with test interface " + iface); + } else { + enforceAutomotiveDevice(methodName); + enforceNetworkManagementPermission(); + } logIfEthernetNotStarted(); } + private void validateTestCapabilities(@NonNull final NetworkCapabilities nc) { + if (nc.hasTransport(TRANSPORT_TEST)) { + return; + } + throw new IllegalArgumentException( + "Updates to test interfaces must have NetworkCapabilities.TRANSPORT_TEST."); + } + @Override public void updateConfiguration(@NonNull final String iface, @NonNull final EthernetNetworkUpdateRequest request, @Nullable final IEthernetNetworkManagementListener listener) { - Log.i(TAG, "updateConfiguration called with: iface=" + iface - + ", request=" + request + ", listener=" + listener); validateNetworkManagementState(iface, "updateConfiguration()"); + if (mTracker.isValidTestInterface(iface)) { + validateTestCapabilities(request.getNetworkCapabilities()); + // TODO: use NetworkCapabilities#restrictCapabilitiesForTestNetwork when available on a + // local NetworkCapabilities copy to pass to mTracker.updateConfiguration. + } // TODO: validate that iface is listed in overlay config_ethernet_interfaces mTracker.updateConfiguration( diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java index 794b5d1..9070a7e 100644 --- a/java/com/android/server/ethernet/EthernetTracker.java +++ b/java/com/android/server/ethernet/EthernetTracker.java @@ -86,6 +86,9 @@ public class EthernetTracker { * if setIncludeTestInterfaces is true, any test interfaces. */ private String mIfaceMatch; + /** + * Track test interfaces if true, don't track otherwise. + */ private boolean mIncludeTestInterfaces = false; /** Mapping between {iface name | mac address} -> {NetworkCapabilities} */ @@ -738,6 +741,17 @@ public class EthernetTracker { Log.d(TAG, "Interface match regexp set to '" + mIfaceMatch + "'"); } + /** + * Validate if a given interface is valid for testing. + * + * @param iface the name of the interface to validate. + * @return {@code true} if test interfaces are enabled and the given {@code iface} has a test + * interface prefix, {@code false} otherwise. + */ + public boolean isValidTestInterface(@NonNull final String iface) { + return mIncludeTestInterfaces && iface.matches(TEST_IFACE_REGEXP); + } + private void postAndWaitForRunnable(Runnable r) { final ConditionVariable cv = new ConditionVariable(); if (mHandler.post(() -> { diff --git a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java index e74a5a3..012f07a 100644 --- a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java +++ b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java @@ -16,6 +16,8 @@ package com.android.server.ethernet; +import static android.net.NetworkCapabilities.TRANSPORT_TEST; + import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.anyString; @@ -23,6 +25,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.Manifest; import android.annotation.NonNull; @@ -162,6 +165,12 @@ public class EthernetServiceImplTest { eq(Manifest.permission.MANAGE_ETHERNET_NETWORKS), anyString()); } + private void denyManageTestNetworksPermission() { + doThrow(new SecurityException("")).when(mContext) + .enforceCallingOrSelfPermission( + eq(Manifest.permission.MANAGE_TEST_NETWORKS), anyString()); + } + @Test public void testUpdateConfigurationRejectsWithoutManageEthPermission() { denyManageEthPermission(); @@ -186,6 +195,37 @@ public class EthernetServiceImplTest { }); } + private void enableTestInterface() { + when(mEthernetTracker.isValidTestInterface(eq(TEST_IFACE))).thenReturn(true); + } + + @Test + public void testUpdateConfigurationRejectsTestRequestWithoutTestPermission() { + enableTestInterface(); + denyManageTestNetworksPermission(); + assertThrows(SecurityException.class, () -> { + mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); + }); + } + + @Test + public void testConnectNetworkRejectsTestRequestWithoutTestPermission() { + enableTestInterface(); + denyManageTestNetworksPermission(); + assertThrows(SecurityException.class, () -> { + mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER); + }); + } + + @Test + public void testDisconnectNetworkRejectsTestRequestWithoutTestPermission() { + enableTestInterface(); + denyManageTestNetworksPermission(); + assertThrows(SecurityException.class, () -> { + mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); + }); + } + @Test public void testUpdateConfiguration() { mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); @@ -206,4 +246,56 @@ public class EthernetServiceImplTest { mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); verify(mEthernetTracker).disconnectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); } + + @Test + public void testUpdateConfigurationRejectsInvalidTestRequest() { + enableTestInterface(); + assertThrows(IllegalArgumentException.class, () -> { + mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); + }); + } + + private EthernetNetworkUpdateRequest createTestNetworkUpdateRequest() { + final NetworkCapabilities nc = new NetworkCapabilities + .Builder(UPDATE_REQUEST.getNetworkCapabilities()) + .addTransportType(TRANSPORT_TEST).build(); + + return new EthernetNetworkUpdateRequest + .Builder(UPDATE_REQUEST) + .setNetworkCapabilities(nc).build(); + } + + @Test + public void testUpdateConfigurationForTestRequestDoesNotRequireAutoOrEthernetPermission() { + enableTestInterface(); + toggleAutomotiveFeature(false); + denyManageEthPermission(); + final EthernetNetworkUpdateRequest request = createTestNetworkUpdateRequest(); + + mEthernetServiceImpl.updateConfiguration(TEST_IFACE, request, NULL_LISTENER); + verify(mEthernetTracker).updateConfiguration( + eq(TEST_IFACE), + eq(request.getIpConfiguration()), + eq(request.getNetworkCapabilities()), eq(NULL_LISTENER)); + } + + @Test + public void testConnectNetworkForTestRequestDoesNotRequireAutoOrNetPermission() { + enableTestInterface(); + toggleAutomotiveFeature(false); + denyManageEthPermission(); + + mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER); + verify(mEthernetTracker).connectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); + } + + @Test + public void testDisconnectNetworkForTestRequestDoesNotRequireAutoOrNetPermission() { + enableTestInterface(); + toggleAutomotiveFeature(false); + denyManageEthPermission(); + + mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); + verify(mEthernetTracker).disconnectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); + } } diff --git a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java index e1a1a8e..d86cc0f 100644 --- a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java +++ b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java @@ -16,8 +16,12 @@ package com.android.server.ethernet; +import static android.net.TestNetworkManager.TEST_TAP_PREFIX; + import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -39,6 +43,7 @@ import android.net.LinkAddress; import android.net.NetworkCapabilities; import android.net.StaticIpConfiguration; import android.os.HandlerThread; +import android.os.RemoteException; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -71,10 +76,11 @@ public class EthernetTrackerTest { @Mock Resources mResources; @Before - public void setUp() { + public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); initMockResources(); when(mFactory.updateInterfaceLinkState(anyString(), anyBoolean(), any())).thenReturn(false); + when(mNetd.interfaceGetList()).thenReturn(new String[0]); mHandlerThread = new HandlerThread(THREAD_NAME); mHandlerThread.start(); tracker = new EthernetTracker(mContext, mHandlerThread.getThreadHandler(), mFactory, mNetd); @@ -355,4 +361,37 @@ public class EthernetTrackerTest { verify(mFactory).updateInterfaceLinkState(eq(TEST_IFACE), eq(false /* up */), eq(NULL_LISTENER)); } + + @Test + public void testIsValidTestInterfaceIsFalseWhenTestInterfacesAreNotIncluded() { + final String validIfaceName = TEST_TAP_PREFIX + "123"; + tracker.setIncludeTestInterfaces(false); + waitForIdle(); + + final boolean isValidTestInterface = tracker.isValidTestInterface(validIfaceName); + + assertFalse(isValidTestInterface); + } + + @Test + public void testIsValidTestInterfaceIsFalseWhenTestInterfaceNameIsInvalid() { + final String invalidIfaceName = "123" + TEST_TAP_PREFIX; + tracker.setIncludeTestInterfaces(true); + waitForIdle(); + + final boolean isValidTestInterface = tracker.isValidTestInterface(invalidIfaceName); + + assertFalse(isValidTestInterface); + } + + @Test + public void testIsValidTestInterfaceIsTrueWhenTestInterfacesIncludedAndValidName() { + final String validIfaceName = TEST_TAP_PREFIX + "123"; + tracker.setIncludeTestInterfaces(true); + waitForIdle(); + + final boolean isValidTestInterface = tracker.isValidTestInterface(validIfaceName); + + assertTrue(isValidTestInterface); + } } -- cgit v1.2.3 From 4856dd02ceb688bdd1bc0acde6b1a65a97747a7a Mon Sep 17 00:00:00 2001 From: James Mattis Date: Thu, 3 Mar 2022 16:19:04 -0800 Subject: Allowing for null net caps in updateConfiguration Marking NetworkCapabilities as nullable in updateConfiguration and updating where needed to support this. This will allow callers of the ethernet network management updateConfiguration API to use it primarily for setting an ethernet network's IP configuration. Bug: 222565654 Bug: 220017952 Bug: 210485380 Test: atest EthernetServiceTests Change-Id: Ifd908639a00470e599fe1a15487cc6383a56b2f5 --- java/com/android/server/ethernet/EthernetNetworkFactory.java | 4 +++- java/com/android/server/ethernet/EthernetServiceImpl.java | 4 ++-- java/com/android/server/ethernet/EthernetTracker.java | 6 ++++-- .../com/android/server/ethernet/EthernetServiceImplTest.java | 12 ++++++++++++ 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java index 8ce27a6..875fc10 100644 --- a/java/com/android/server/ethernet/EthernetNetworkFactory.java +++ b/java/com/android/server/ethernet/EthernetNetworkFactory.java @@ -485,7 +485,9 @@ public class EthernetNetworkFactory extends NetworkFactory { } mIpConfig = ipConfig; - setCapabilities(capabilities); + if (null != capabilities) { + 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 diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java index 7f77e5e..9987b3e 100644 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ b/java/com/android/server/ethernet/EthernetServiceImpl.java @@ -237,8 +237,8 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { logIfEthernetNotStarted(); } - private void validateTestCapabilities(@NonNull final NetworkCapabilities nc) { - if (nc.hasTransport(TRANSPORT_TEST)) { + private void validateTestCapabilities(@Nullable final NetworkCapabilities nc) { + if (null != nc && nc.hasTransport(TRANSPORT_TEST)) { return; } throw new IllegalArgumentException( diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java index 9070a7e..ea241e1 100644 --- a/java/com/android/server/ethernet/EthernetTracker.java +++ b/java/com/android/server/ethernet/EthernetTracker.java @@ -233,7 +233,7 @@ public class EthernetTracker { @VisibleForTesting(visibility = PACKAGE) protected void updateConfiguration(@NonNull final String iface, @NonNull final IpConfiguration ipConfig, - @NonNull final NetworkCapabilities capabilities, + @Nullable final NetworkCapabilities capabilities, @Nullable final IEthernetNetworkManagementListener listener) { if (DBG) { Log.i(TAG, "updateConfiguration, iface: " + iface + ", capabilities: " + capabilities @@ -241,7 +241,9 @@ public class EthernetTracker { } final IpConfiguration localIpConfig = new IpConfiguration(ipConfig); writeIpConfiguration(iface, localIpConfig); - mNetworkCapabilities.put(iface, capabilities); + if (null != capabilities) { + mNetworkCapabilities.put(iface, capabilities); + } mHandler.post(() -> { mFactory.updateInterface(iface, localIpConfig, capabilities, listener); broadcastInterfaceStateChange(iface); diff --git a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java index 012f07a..e814c84 100644 --- a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java +++ b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java @@ -247,6 +247,18 @@ public class EthernetServiceImplTest { verify(mEthernetTracker).disconnectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); } + @Test + public void testUpdateConfigurationRejectsTestRequestWithNullCapabilities() { + enableTestInterface(); + final EthernetNetworkUpdateRequest request = + new EthernetNetworkUpdateRequest + .Builder() + .setIpConfiguration(new IpConfiguration()).build(); + assertThrows(IllegalArgumentException.class, () -> { + mEthernetServiceImpl.updateConfiguration(TEST_IFACE, request, NULL_LISTENER); + }); + } + @Test public void testUpdateConfigurationRejectsInvalidTestRequest() { enableTestInterface(); -- cgit v1.2.3 From f95a2982864df88692d1fa99220ac8774f3b3199 Mon Sep 17 00:00:00 2001 From: Patrick Rohr Date: Tue, 8 Mar 2022 13:10:18 +0100 Subject: Allow all device types to call updateConfiguration Usage of this API should not be limited to Automotive devices as TvSettings also needs to update the IpConfiguration. Test: TH Change-Id: I838a0a8684e9f944801718a4d688666de45f42fb --- .../server/ethernet/EthernetServiceImpl.java | 67 ++++++++++++---------- .../server/ethernet/EthernetServiceImplTest.java | 26 +++++++-- 2 files changed, 59 insertions(+), 34 deletions(-) diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java index 9987b3e..782ee0f 100644 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ b/java/com/android/server/ethernet/EthernetServiceImpl.java @@ -215,45 +215,34 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { "EthernetServiceImpl"); } - /** - * Validate the state of ethernet for APIs tied to network management. - * - * @param iface the ethernet interface name to operate on. - * @param methodName the name of the calling method. - */ - private void validateNetworkManagementState(@NonNull final String iface, - final @NonNull String methodName) { - Objects.requireNonNull(iface, "Pass a non-null iface."); - Objects.requireNonNull(methodName, "Pass a non-null methodName."); - - // Only bypass the permission/device checks if this is a valid test interface. - if (mTracker.isValidTestInterface(iface)) { - enforceManageTestNetworksPermission(); - Log.i(TAG, "Ethernet network management API used with test interface " + iface); - } else { - enforceAutomotiveDevice(methodName); - enforceNetworkManagementPermission(); - } - logIfEthernetNotStarted(); - } - private void validateTestCapabilities(@Nullable final NetworkCapabilities nc) { - if (null != nc && nc.hasTransport(TRANSPORT_TEST)) { - return; + // For test capabilities, only null or capabilities that include TRANSPORT_TEST are allowed. + if (nc != null && !nc.hasTransport(TRANSPORT_TEST)) { + throw new IllegalArgumentException( + "Updates to test interfaces must have NetworkCapabilities.TRANSPORT_TEST."); } - throw new IllegalArgumentException( - "Updates to test interfaces must have NetworkCapabilities.TRANSPORT_TEST."); } @Override public void updateConfiguration(@NonNull final String iface, @NonNull final EthernetNetworkUpdateRequest request, @Nullable final IEthernetNetworkManagementListener listener) { - validateNetworkManagementState(iface, "updateConfiguration()"); + Objects.requireNonNull(iface); + Objects.requireNonNull(request); + // TODO: rename to throwIfEthernetNotStarted. + logIfEthernetNotStarted(); + if (mTracker.isValidTestInterface(iface)) { + enforceManageTestNetworksPermission(); validateTestCapabilities(request.getNetworkCapabilities()); // TODO: use NetworkCapabilities#restrictCapabilitiesForTestNetwork when available on a // local NetworkCapabilities copy to pass to mTracker.updateConfiguration. + } else { + enforceNetworkManagementPermission(); + if (request.getNetworkCapabilities() != null) { + // only automotive devices are allowed to set the NetworkCapabilities using this API + enforceAutomotiveDevice("updateConfiguration() with non-null capabilities"); + } } // TODO: validate that iface is listed in overlay config_ethernet_interfaces @@ -265,7 +254,17 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { public void connectNetwork(@NonNull final String iface, @Nullable final IEthernetNetworkManagementListener listener) { Log.i(TAG, "connectNetwork called with: iface=" + iface + ", listener=" + listener); - validateNetworkManagementState(iface, "connectNetwork()"); + Objects.requireNonNull(iface); + logIfEthernetNotStarted(); + + if (mTracker.isValidTestInterface(iface)) { + enforceManageTestNetworksPermission(); + } else { + // only automotive devices are allowed to use this API. + enforceNetworkManagementPermission(); + enforceAutomotiveDevice("connectNetwork()"); + } + mTracker.connectNetwork(iface, listener); } @@ -273,7 +272,17 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { public void disconnectNetwork(@NonNull final String iface, @Nullable final IEthernetNetworkManagementListener listener) { Log.i(TAG, "disconnectNetwork called with: iface=" + iface + ", listener=" + listener); - validateNetworkManagementState(iface, "disconnectNetwork()"); + Objects.requireNonNull(iface); + logIfEthernetNotStarted(); + + if (mTracker.isValidTestInterface(iface)) { + enforceManageTestNetworksPermission(); + } else { + // only automotive devices are allowed to use this API. + enforceNetworkManagementPermission(); + enforceAutomotiveDevice("disconnectNetwork()"); + } + mTracker.disconnectNetwork(iface, listener); } } diff --git a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java index e814c84..175a97d 100644 --- a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java +++ b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; @@ -55,6 +56,10 @@ public class EthernetServiceImplTest { .setIpConfiguration(new IpConfiguration()) .setNetworkCapabilities(new NetworkCapabilities.Builder().build()) .build(); + private static final EthernetNetworkUpdateRequest UPDATE_REQUEST_WITHOUT_CAPABILITIES = + new EthernetNetworkUpdateRequest.Builder() + .setIpConfiguration(new IpConfiguration()) + .build(); private static final IEthernetNetworkManagementListener NULL_LISTENER = null; private EthernetServiceImpl mEthernetServiceImpl; @Mock private Context mContext; @@ -136,13 +141,23 @@ public class EthernetServiceImplTest { } @Test - public void testUpdateConfigurationRejectsWithoutAutomotiveFeature() { + public void testUpdateConfigurationWithCapabilitiesRejectsWithoutAutomotiveFeature() { toggleAutomotiveFeature(false); assertThrows(UnsupportedOperationException.class, () -> { mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); }); } + @Test + public void testUpdateConfigurationWithCapabilitiesWithAutomotiveFeature() { + toggleAutomotiveFeature(false); + mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST_WITHOUT_CAPABILITIES, + NULL_LISTENER); + verify(mEthernetTracker).updateConfiguration(eq(TEST_IFACE), + eq(UPDATE_REQUEST_WITHOUT_CAPABILITIES.getIpConfiguration()), + eq(UPDATE_REQUEST_WITHOUT_CAPABILITIES.getNetworkCapabilities()), isNull()); + } + @Test public void testConnectNetworkRejectsWithoutAutomotiveFeature() { toggleAutomotiveFeature(false); @@ -248,15 +263,16 @@ public class EthernetServiceImplTest { } @Test - public void testUpdateConfigurationRejectsTestRequestWithNullCapabilities() { + public void testUpdateConfigurationAcceptsTestRequestWithNullCapabilities() { enableTestInterface(); final EthernetNetworkUpdateRequest request = new EthernetNetworkUpdateRequest .Builder() .setIpConfiguration(new IpConfiguration()).build(); - assertThrows(IllegalArgumentException.class, () -> { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, request, NULL_LISTENER); - }); + mEthernetServiceImpl.updateConfiguration(TEST_IFACE, request, NULL_LISTENER); + verify(mEthernetTracker).updateConfiguration(eq(TEST_IFACE), + eq(request.getIpConfiguration()), + eq(request.getNetworkCapabilities()), isNull()); } @Test -- cgit v1.2.3 From efe300c812ac3cd6cae8c6be0a8ca1d877cd640b Mon Sep 17 00:00:00 2001 From: Patrick Rohr Date: Wed, 9 Mar 2022 13:58:28 +0100 Subject: Rename logIfEthernetNotStarted to throwIfEthernetNotStarted The functions does not log but throws an exception instead. Changing the name to reflect that. Test: TH Change-Id: I6207aababaccc9bc553f7f731e3b8a1d26eb16a1 --- java/com/android/server/ethernet/EthernetServiceImpl.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java index 782ee0f..89ac6e4 100644 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ b/java/com/android/server/ethernet/EthernetServiceImpl.java @@ -80,7 +80,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { mStarted.set(true); } - private void logIfEthernetNotStarted() { + private void throwIfEthernetNotStarted() { if (!mStarted.get()) { throw new IllegalStateException("System isn't ready to change ethernet configurations"); } @@ -111,7 +111,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { */ @Override public void setConfiguration(String iface, IpConfiguration config) { - logIfEthernetNotStarted(); + throwIfEthernetNotStarted(); PermissionUtils.enforceNetworkStackPermission(mContext); if (mTracker.isRestrictedInterface(iface)) { @@ -229,8 +229,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { @Nullable final IEthernetNetworkManagementListener listener) { Objects.requireNonNull(iface); Objects.requireNonNull(request); - // TODO: rename to throwIfEthernetNotStarted. - logIfEthernetNotStarted(); + throwIfEthernetNotStarted(); if (mTracker.isValidTestInterface(iface)) { enforceManageTestNetworksPermission(); @@ -255,7 +254,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { @Nullable final IEthernetNetworkManagementListener listener) { Log.i(TAG, "connectNetwork called with: iface=" + iface + ", listener=" + listener); Objects.requireNonNull(iface); - logIfEthernetNotStarted(); + throwIfEthernetNotStarted(); if (mTracker.isValidTestInterface(iface)) { enforceManageTestNetworksPermission(); @@ -273,7 +272,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { @Nullable final IEthernetNetworkManagementListener listener) { Log.i(TAG, "disconnectNetwork called with: iface=" + iface + ", listener=" + listener); Objects.requireNonNull(iface); - logIfEthernetNotStarted(); + throwIfEthernetNotStarted(); if (mTracker.isValidTestInterface(iface)) { enforceManageTestNetworksPermission(); -- cgit v1.2.3 From 145b155f14a1379c9f290ad7b2f91d5daccb1cab Mon Sep 17 00:00:00 2001 From: Patrick Rohr Date: Wed, 9 Mar 2022 21:37:14 +0100 Subject: Clean up permission validation in EthernetServiceImpl Test: atest EthernetServiceImplTest Change-Id: I0ca54e09dd98cab348fc855e8a0bf70a703fffed --- .../server/ethernet/EthernetServiceImpl.java | 53 ++++++++++------------ 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java index 89ac6e4..50b4684 100644 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ b/java/com/android/server/ethernet/EthernetServiceImpl.java @@ -215,14 +215,31 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { "EthernetServiceImpl"); } - private void validateTestCapabilities(@Nullable final NetworkCapabilities nc) { - // For test capabilities, only null or capabilities that include TRANSPORT_TEST are allowed. + private void maybeValidateTestCapabilities(final String iface, + @Nullable final NetworkCapabilities nc) { + if (!mTracker.isValidTestInterface(iface)) { + return; + } + // For test interfaces, only null or capabilities that include TRANSPORT_TEST are + // allowed. if (nc != null && !nc.hasTransport(TRANSPORT_TEST)) { throw new IllegalArgumentException( "Updates to test interfaces must have NetworkCapabilities.TRANSPORT_TEST."); } } + private void enforceAdminPermission(final String iface, boolean enforceAutomotive, + final String logMessage) { + if (mTracker.isValidTestInterface(iface)) { + enforceManageTestNetworksPermission(); + } else { + enforceNetworkManagementPermission(); + if (enforceAutomotive) { + enforceAutomotiveDevice(logMessage); + } + } + } + @Override public void updateConfiguration(@NonNull final String iface, @NonNull final EthernetNetworkUpdateRequest request, @@ -231,19 +248,11 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { Objects.requireNonNull(request); throwIfEthernetNotStarted(); - if (mTracker.isValidTestInterface(iface)) { - enforceManageTestNetworksPermission(); - validateTestCapabilities(request.getNetworkCapabilities()); - // TODO: use NetworkCapabilities#restrictCapabilitiesForTestNetwork when available on a - // local NetworkCapabilities copy to pass to mTracker.updateConfiguration. - } else { - enforceNetworkManagementPermission(); - if (request.getNetworkCapabilities() != null) { - // only automotive devices are allowed to set the NetworkCapabilities using this API - enforceAutomotiveDevice("updateConfiguration() with non-null capabilities"); - } - } // TODO: validate that iface is listed in overlay config_ethernet_interfaces + // only automotive devices are allowed to set the NetworkCapabilities using this API + enforceAdminPermission(iface, request.getNetworkCapabilities() != null, + "updateConfiguration() with non-null capabilities"); + maybeValidateTestCapabilities(iface, request.getNetworkCapabilities()); mTracker.updateConfiguration( iface, request.getIpConfiguration(), request.getNetworkCapabilities(), listener); @@ -256,13 +265,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { Objects.requireNonNull(iface); throwIfEthernetNotStarted(); - if (mTracker.isValidTestInterface(iface)) { - enforceManageTestNetworksPermission(); - } else { - // only automotive devices are allowed to use this API. - enforceNetworkManagementPermission(); - enforceAutomotiveDevice("connectNetwork()"); - } + enforceAdminPermission(iface, true, "connectNetwork()"); mTracker.connectNetwork(iface, listener); } @@ -274,13 +277,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { Objects.requireNonNull(iface); throwIfEthernetNotStarted(); - if (mTracker.isValidTestInterface(iface)) { - enforceManageTestNetworksPermission(); - } else { - // only automotive devices are allowed to use this API. - enforceNetworkManagementPermission(); - enforceAutomotiveDevice("disconnectNetwork()"); - } + enforceAdminPermission(iface, true, "connectNetwork()"); mTracker.disconnectNetwork(iface, listener); } -- cgit v1.2.3 From be24c99542454e671612bb109063826a95c9e200 Mon Sep 17 00:00:00 2001 From: Xiao Ma Date: Mon, 24 Jan 2022 03:54:55 +0000 Subject: Build ethernet service into service-connectivity-tiramisu-pre-jarjar. Along with ethernet service is going to be moved into Connectivity module, this CL contains the corresponding changes as below: 1. deprecate the etherent-service lib and create a new filegroup: When the ethernet mainline code migration completes, ethernet-service lib won't be kept any more, instead the ethernet service should be started from ConnectivityServiceInitializer. So deprecate the current ethernet-service lib and cleanup the usages on other places later. Create a new filegroup which includes all ethernet service code, that will be built into service-connectivity-tiramisu-pre-jarjar. 2. Move the implementation of starting ethernet service: ConnectivityServiceInitializer should take responsibility to start ethernet service and EthernetTracker on boot phase. Move this code to ConnectivityServiceInitializer and remove the current onStart and onBootPhase implemenation. 3. Move below ethernet service related config resources: - config_ethernet_tcp_buffers - config_ethernet_interfaces - config_ethernet_iface_regex Move the definition of these resource from frameworks/base/core to p/m/Connectivity/service/ServiceConnectivityResources, and import the ServiceConnectivityResources lib to adapt the ethernet update. 4. Update the EthernetServiceTests dependencies and refactor the code which uses ConnectivityResources instead of internal Resource to make the tests pass. Bug: 210586283 Test: m Test: atest FrameworksNetTests atest EthernetServiceTests Change-Id: I0bbecfb64f720213ee2b02417bc8357ccf4725b6 --- Android.bp | 18 +++--- .../server/ethernet/EthernetNetworkFactory.java | 29 +++++++++- .../android/server/ethernet/EthernetService.java | 38 ++++--------- .../android/server/ethernet/EthernetTracker.java | 64 ++++++++++++++++++++-- tests/Android.bp | 13 ++++- .../ethernet/EthernetNetworkFactoryTest.java | 5 +- .../server/ethernet/EthernetTrackerTest.java | 13 ++--- 7 files changed, 123 insertions(+), 57 deletions(-) diff --git a/Android.bp b/Android.bp index c3393bc..c429a6f 100644 --- a/Android.bp +++ b/Android.bp @@ -19,18 +19,14 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], } -java_library { - name: "ethernet-service", - installable: true, - - aidl: { - local_include_dirs: ["java"], - }, +filegroup { + name: "ethernet-service-updatable-sources", srcs: [ "java/**/*.java", - "java/**/I*.aidl", - "java/**/*.logtags", ], - - libs: ["services"], + path: "java", + visibility: [ + "//frameworks/opt/net/ethernet/tests", + "//packages/modules/Connectivity/service-t", + ], } diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java index 875fc10..ce0d77c 100644 --- a/java/com/android/server/ethernet/EthernetNetworkFactory.java +++ b/java/com/android/server/ethernet/EthernetNetworkFactory.java @@ -19,7 +19,9 @@ package com.android.server.ethernet; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.res.Resources; import android.net.ConnectivityManager; +import android.net.ConnectivityResources; import android.net.EthernetManager; import android.net.EthernetNetworkSpecifier; import android.net.IEthernetNetworkManagementListener; @@ -49,6 +51,7 @@ import android.util.AndroidRuntimeException; import android.util.Log; import android.util.SparseArray; +import com.android.connectivity.resources.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.net.module.util.InterfaceParams; @@ -69,6 +72,8 @@ public class EthernetNetworkFactory extends NetworkFactory { private final static int NETWORK_SCORE = 70; private static final String NETWORK_TYPE = "Ethernet"; + private static final String LEGACY_TCP_BUFFER_SIZES = + "524288,1048576,3145728,524288,1048576,2097152"; private final ConcurrentHashMap mTrackingInterfaces = new ConcurrentHashMap<>(); @@ -94,6 +99,27 @@ public class EthernetNetworkFactory extends NetworkFactory { public InterfaceParams getNetworkInterfaceByName(String name) { return InterfaceParams.getByName(name); } + + // TODO: remove legacy resource fallback after migrating its overlays. + private String getPlatformTcpBufferSizes(Context context) { + final Resources r = context.getResources(); + final int resId = r.getIdentifier("config_ethernet_tcp_buffers", "string", + context.getPackageName()); + return r.getString(resId); + } + + public String getTcpBufferSizesFromResource(Context context) { + final String tcpBufferSizes; + final String platformTcpBufferSizes = getPlatformTcpBufferSizes(context); + if (!LEGACY_TCP_BUFFER_SIZES.equals(platformTcpBufferSizes)) { + // Platform resource is not the historical default: use the overlay. + tcpBufferSizes = platformTcpBufferSizes; + } else { + final ConnectivityResources resources = new ConnectivityResources(context); + tcpBufferSizes = resources.get().getString(R.string.config_ethernet_tcp_buffers); + } + return tcpBufferSizes; + } } public static class ConfigurationException extends AndroidRuntimeException { @@ -518,8 +544,7 @@ public class EthernetNetworkFactory extends NetworkFactory { mIpClientCallback.awaitIpClientStart(); if (sTcpBufferSizes == null) { - sTcpBufferSizes = mContext.getResources().getString( - com.android.internal.R.string.config_ethernet_tcp_buffers); + sTcpBufferSizes = mDeps.getTcpBufferSizesFromResource(mContext); } provisionIpClient(mIpClient, mIpConfig, sTcpBufferSizes); } diff --git a/java/com/android/server/ethernet/EthernetService.java b/java/com/android/server/ethernet/EthernetService.java index e6fee9e..d405fd5 100644 --- a/java/com/android/server/ethernet/EthernetService.java +++ b/java/com/android/server/ethernet/EthernetService.java @@ -21,45 +21,27 @@ import android.net.INetd; 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 { - +// TODO: consider renaming EthernetServiceImpl to EthernetService and deleting this file. +public final class EthernetService { private static final String TAG = "EthernetService"; private static final String THREAD_NAME = "EthernetServiceThread"; - private final EthernetServiceImpl mImpl; - - public EthernetService(Context context) { - 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, handler, - new EthernetTracker(context, handler, factory, getNetd(context))); - } - private INetd getNetd(Context context) { + private static INetd getNetd(Context context) { final INetd netd = INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)); Objects.requireNonNull(netd, "could not get netd instance"); return netd; } - @Override - public void onStart() { - Log.i(TAG, "Registering service " + Context.ETHERNET_SERVICE); - publishBinderService(Context.ETHERNET_SERVICE, mImpl); - } - - @Override - public void onBootPhase(int phase) { - if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { - mImpl.start(); - } + public static EthernetServiceImpl create(Context context) { + final HandlerThread handlerThread = new HandlerThread(THREAD_NAME); + handlerThread.start(); + final Handler handler = new Handler(handlerThread.getLooper()); + final EthernetNetworkFactory factory = new EthernetNetworkFactory(handler, context); + return new EthernetServiceImpl(context, handler, + new EthernetTracker(context, handler, factory, getNetd(context))); } } diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java index ea241e1..1b696a4 100644 --- a/java/com/android/server/ethernet/EthernetTracker.java +++ b/java/com/android/server/ethernet/EthernetTracker.java @@ -23,6 +23,8 @@ import static com.android.internal.annotations.VisibleForTesting.Visibility.PACK import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.res.Resources; +import android.net.ConnectivityResources; import android.net.EthernetManager; import android.net.IEthernetServiceListener; import android.net.IEthernetNetworkManagementListener; @@ -80,6 +82,7 @@ public class EthernetTracker { private static final boolean DBG = EthernetNetworkFactory.DBG; private static final String TEST_IFACE_REGEXP = TEST_TAP_PREFIX + "\\d+"; + private static final String LEGACY_IFACE_REGEXP = "eth\\d"; /** * Interface names we track. This is a product-dependent regular expression, plus, @@ -102,6 +105,7 @@ public class EthernetTracker { private final Handler mHandler; private final EthernetNetworkFactory mFactory; private final EthernetConfigStore mConfigStore; + private final Dependencies mDeps; private final RemoteCallbackList mListeners = new RemoteCallbackList<>(); @@ -123,19 +127,72 @@ public class EthernetTracker { } } + public static class Dependencies { + // TODO: remove legacy resource fallback after migrating its overlays. + private String getPlatformRegexResource(Context context) { + final Resources r = context.getResources(); + final int resId = + r.getIdentifier("config_ethernet_iface_regex", "string", context.getPackageName()); + return r.getString(resId); + } + + // TODO: remove legacy resource fallback after migrating its overlays. + private String[] getPlatformInterfaceConfigs(Context context) { + final Resources r = context.getResources(); + final int resId = r.getIdentifier("config_ethernet_interfaces", "array", + context.getPackageName()); + return r.getStringArray(resId); + } + + public String getInterfaceRegexFromResource(Context context) { + final String platformRegex = getPlatformRegexResource(context); + final String match; + if (!LEGACY_IFACE_REGEXP.equals(platformRegex)) { + // Platform resource is not the historical default: use the overlay + match = platformRegex; + } else { + final ConnectivityResources resources = new ConnectivityResources(context); + match = resources.get().getString( + com.android.connectivity.resources.R.string.config_ethernet_iface_regex); + } + return match; + } + + public String[] getInterfaceConfigFromResource(Context context) { + final String[] platformInterfaceConfigs = getPlatformInterfaceConfigs(context); + final String[] interfaceConfigs; + if (platformInterfaceConfigs.length != 0) { + // Platform resource is not the historical default: use the overlay + interfaceConfigs = platformInterfaceConfigs; + } else { + final ConnectivityResources resources = new ConnectivityResources(context); + interfaceConfigs = resources.get().getStringArray( + com.android.connectivity.resources.R.array.config_ethernet_interfaces); + } + return interfaceConfigs; + } + } + EthernetTracker(@NonNull final Context context, @NonNull final Handler handler, @NonNull final EthernetNetworkFactory factory, @NonNull final INetd netd) { + this(context, handler, factory, netd, new Dependencies()); + } + + @VisibleForTesting + EthernetTracker(@NonNull final Context context, @NonNull final Handler handler, + @NonNull final EthernetNetworkFactory factory, @NonNull final INetd netd, + @NonNull final Dependencies deps) { mContext = context; mHandler = handler; mFactory = factory; mNetd = netd; + mDeps = deps; // Interface match regex. updateIfaceMatchRegexp(); // Read default Ethernet interface configuration from resources - final String[] interfaceConfigs = context.getResources().getStringArray( - com.android.internal.R.array.config_ethernet_interfaces); + final String[] interfaceConfigs = mDeps.getInterfaceConfigFromResource(context); for (String strConfig : interfaceConfigs) { parseEthernetConfig(strConfig); } @@ -735,8 +792,7 @@ public class EthernetTracker { } private void updateIfaceMatchRegexp() { - final String match = mContext.getResources().getString( - com.android.internal.R.string.config_ethernet_iface_regex); + final String match = mDeps.getInterfaceRegexFromResource(mContext); mIfaceMatch = mIncludeTestInterfaces ? "(" + match + "|" + TEST_IFACE_REGEXP + ")" : match; diff --git a/tests/Android.bp b/tests/Android.bp index 28b13c5..6cfebdc 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -17,10 +17,17 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], } +// TODO: merge the tests into service-connectivity tests after +// ethernet service migration completes. So far just import the +// ethernet service source to fix the dependencies. android_test { name: "EthernetServiceTests", - srcs: ["java/**/*.java"], + srcs: [ + ":ethernet-service-updatable-sources", + ":services.connectivity-ethernet-sources", + "java/**/*.java", + ], certificate: "platform", platform_apis: true, @@ -29,11 +36,13 @@ android_test { "android.test.runner", "android.test.base", "android.test.mock", + "framework-connectivity.impl", + "framework-connectivity-t.impl", + "ServiceConnectivityResources", ], static_libs: [ "androidx.test.rules", - "ethernet-service", "frameworks-base-testutils", "mockito-target-minus-junit4", "net-tests-utils", diff --git a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java index 61425bf..501324a 100644 --- a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java +++ b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java @@ -63,7 +63,7 @@ import android.util.Pair; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.R; +import com.android.connectivity.resources.R; import com.android.net.module.util.InterfaceParams; import com.android.testutils.DevSdkIgnoreRule; @@ -174,8 +174,7 @@ public class EthernetNetworkFactoryTest { } private void setupContext() { - when(mContext.getResources()).thenReturn(mResources); - when(mResources.getString(R.string.config_ethernet_tcp_buffers)).thenReturn(""); + when(mDeps.getTcpBufferSizesFromResource(eq(mContext))).thenReturn(""); } @After diff --git a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java index d86cc0f..ef70d94 100644 --- a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java +++ b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java @@ -27,7 +27,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -48,7 +47,7 @@ import android.os.RemoteException; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.R; +import com.android.connectivity.resources.R; import com.android.testutils.HandlerUtils; import org.junit.After; @@ -73,7 +72,7 @@ public class EthernetTrackerTest { @Mock private Context mContext; @Mock private EthernetNetworkFactory mFactory; @Mock private INetd mNetd; - @Mock Resources mResources; + @Mock private EthernetTracker.Dependencies mDeps; @Before public void setUp() throws RemoteException { @@ -83,7 +82,8 @@ public class EthernetTrackerTest { when(mNetd.interfaceGetList()).thenReturn(new String[0]); mHandlerThread = new HandlerThread(THREAD_NAME); mHandlerThread.start(); - tracker = new EthernetTracker(mContext, mHandlerThread.getThreadHandler(), mFactory, mNetd); + tracker = new EthernetTracker(mContext, mHandlerThread.getThreadHandler(), mFactory, mNetd, + mDeps); } @After @@ -92,9 +92,8 @@ public class EthernetTrackerTest { } 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(); + when(mDeps.getInterfaceRegexFromResource(eq(mContext))).thenReturn(""); + when(mDeps.getInterfaceConfigFromResource(eq(mContext))).thenReturn(new String[0]); } private void waitForIdle() { -- cgit v1.2.3 From c976c676374518c99be54fce008b657dd334944e Mon Sep 17 00:00:00 2001 From: Xiao Ma Date: Mon, 24 Jan 2022 03:54:55 +0000 Subject: Build ethernet service into service-connectivity-tiramisu-pre-jarjar. Along with ethernet service is going to be moved into Connectivity module, this CL contains the corresponding changes as below: 1. deprecate the etherent-service lib and create a new filegroup: When the ethernet mainline code migration completes, ethernet-service lib won't be kept any more, instead the ethernet service should be started from ConnectivityServiceInitializer. So deprecate the current ethernet-service lib and cleanup the usages on other places later. Create a new filegroup which includes all ethernet service code, that will be built into service-connectivity-tiramisu-pre-jarjar. 2. Move the implementation of starting ethernet service: ConnectivityServiceInitializer should take responsibility to start ethernet service and EthernetTracker on boot phase. Move this code to ConnectivityServiceInitializer and remove the current onStart and onBootPhase implemenation. 3. Move below ethernet service related config resources: - config_ethernet_tcp_buffers - config_ethernet_interfaces - config_ethernet_iface_regex Move the definition of these resource from frameworks/base/core to p/m/Connectivity/service/ServiceConnectivityResources, and import the ServiceConnectivityResources lib to adapt the ethernet update. 4. Update the EthernetServiceTests dependencies and refactor the code which uses ConnectivityResources instead of internal Resource to make the tests pass. Bug: 210586283 Test: m Test: atest FrameworksNetTests atest EthernetServiceTests Change-Id: I0bbecfb64f720213ee2b02417bc8357ccf4725b6 Merged-In: I0bbecfb64f720213ee2b02417bc8357ccf4725b6 --- Android.bp | 18 +++--- .../server/ethernet/EthernetNetworkFactory.java | 29 +++++++++- .../android/server/ethernet/EthernetService.java | 38 ++++--------- .../android/server/ethernet/EthernetTracker.java | 64 ++++++++++++++++++++-- tests/Android.bp | 13 ++++- .../ethernet/EthernetNetworkFactoryTest.java | 5 +- .../server/ethernet/EthernetTrackerTest.java | 13 ++--- 7 files changed, 123 insertions(+), 57 deletions(-) diff --git a/Android.bp b/Android.bp index c3393bc..c429a6f 100644 --- a/Android.bp +++ b/Android.bp @@ -19,18 +19,14 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], } -java_library { - name: "ethernet-service", - installable: true, - - aidl: { - local_include_dirs: ["java"], - }, +filegroup { + name: "ethernet-service-updatable-sources", srcs: [ "java/**/*.java", - "java/**/I*.aidl", - "java/**/*.logtags", ], - - libs: ["services"], + path: "java", + visibility: [ + "//frameworks/opt/net/ethernet/tests", + "//packages/modules/Connectivity/service-t", + ], } diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java index 875fc10..ce0d77c 100644 --- a/java/com/android/server/ethernet/EthernetNetworkFactory.java +++ b/java/com/android/server/ethernet/EthernetNetworkFactory.java @@ -19,7 +19,9 @@ package com.android.server.ethernet; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.res.Resources; import android.net.ConnectivityManager; +import android.net.ConnectivityResources; import android.net.EthernetManager; import android.net.EthernetNetworkSpecifier; import android.net.IEthernetNetworkManagementListener; @@ -49,6 +51,7 @@ import android.util.AndroidRuntimeException; import android.util.Log; import android.util.SparseArray; +import com.android.connectivity.resources.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.net.module.util.InterfaceParams; @@ -69,6 +72,8 @@ public class EthernetNetworkFactory extends NetworkFactory { private final static int NETWORK_SCORE = 70; private static final String NETWORK_TYPE = "Ethernet"; + private static final String LEGACY_TCP_BUFFER_SIZES = + "524288,1048576,3145728,524288,1048576,2097152"; private final ConcurrentHashMap mTrackingInterfaces = new ConcurrentHashMap<>(); @@ -94,6 +99,27 @@ public class EthernetNetworkFactory extends NetworkFactory { public InterfaceParams getNetworkInterfaceByName(String name) { return InterfaceParams.getByName(name); } + + // TODO: remove legacy resource fallback after migrating its overlays. + private String getPlatformTcpBufferSizes(Context context) { + final Resources r = context.getResources(); + final int resId = r.getIdentifier("config_ethernet_tcp_buffers", "string", + context.getPackageName()); + return r.getString(resId); + } + + public String getTcpBufferSizesFromResource(Context context) { + final String tcpBufferSizes; + final String platformTcpBufferSizes = getPlatformTcpBufferSizes(context); + if (!LEGACY_TCP_BUFFER_SIZES.equals(platformTcpBufferSizes)) { + // Platform resource is not the historical default: use the overlay. + tcpBufferSizes = platformTcpBufferSizes; + } else { + final ConnectivityResources resources = new ConnectivityResources(context); + tcpBufferSizes = resources.get().getString(R.string.config_ethernet_tcp_buffers); + } + return tcpBufferSizes; + } } public static class ConfigurationException extends AndroidRuntimeException { @@ -518,8 +544,7 @@ public class EthernetNetworkFactory extends NetworkFactory { mIpClientCallback.awaitIpClientStart(); if (sTcpBufferSizes == null) { - sTcpBufferSizes = mContext.getResources().getString( - com.android.internal.R.string.config_ethernet_tcp_buffers); + sTcpBufferSizes = mDeps.getTcpBufferSizesFromResource(mContext); } provisionIpClient(mIpClient, mIpConfig, sTcpBufferSizes); } diff --git a/java/com/android/server/ethernet/EthernetService.java b/java/com/android/server/ethernet/EthernetService.java index e6fee9e..d405fd5 100644 --- a/java/com/android/server/ethernet/EthernetService.java +++ b/java/com/android/server/ethernet/EthernetService.java @@ -21,45 +21,27 @@ import android.net.INetd; 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 { - +// TODO: consider renaming EthernetServiceImpl to EthernetService and deleting this file. +public final class EthernetService { private static final String TAG = "EthernetService"; private static final String THREAD_NAME = "EthernetServiceThread"; - private final EthernetServiceImpl mImpl; - - public EthernetService(Context context) { - 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, handler, - new EthernetTracker(context, handler, factory, getNetd(context))); - } - private INetd getNetd(Context context) { + private static INetd getNetd(Context context) { final INetd netd = INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)); Objects.requireNonNull(netd, "could not get netd instance"); return netd; } - @Override - public void onStart() { - Log.i(TAG, "Registering service " + Context.ETHERNET_SERVICE); - publishBinderService(Context.ETHERNET_SERVICE, mImpl); - } - - @Override - public void onBootPhase(int phase) { - if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { - mImpl.start(); - } + public static EthernetServiceImpl create(Context context) { + final HandlerThread handlerThread = new HandlerThread(THREAD_NAME); + handlerThread.start(); + final Handler handler = new Handler(handlerThread.getLooper()); + final EthernetNetworkFactory factory = new EthernetNetworkFactory(handler, context); + return new EthernetServiceImpl(context, handler, + new EthernetTracker(context, handler, factory, getNetd(context))); } } diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java index ea241e1..1b696a4 100644 --- a/java/com/android/server/ethernet/EthernetTracker.java +++ b/java/com/android/server/ethernet/EthernetTracker.java @@ -23,6 +23,8 @@ import static com.android.internal.annotations.VisibleForTesting.Visibility.PACK import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.res.Resources; +import android.net.ConnectivityResources; import android.net.EthernetManager; import android.net.IEthernetServiceListener; import android.net.IEthernetNetworkManagementListener; @@ -80,6 +82,7 @@ public class EthernetTracker { private static final boolean DBG = EthernetNetworkFactory.DBG; private static final String TEST_IFACE_REGEXP = TEST_TAP_PREFIX + "\\d+"; + private static final String LEGACY_IFACE_REGEXP = "eth\\d"; /** * Interface names we track. This is a product-dependent regular expression, plus, @@ -102,6 +105,7 @@ public class EthernetTracker { private final Handler mHandler; private final EthernetNetworkFactory mFactory; private final EthernetConfigStore mConfigStore; + private final Dependencies mDeps; private final RemoteCallbackList mListeners = new RemoteCallbackList<>(); @@ -123,19 +127,72 @@ public class EthernetTracker { } } + public static class Dependencies { + // TODO: remove legacy resource fallback after migrating its overlays. + private String getPlatformRegexResource(Context context) { + final Resources r = context.getResources(); + final int resId = + r.getIdentifier("config_ethernet_iface_regex", "string", context.getPackageName()); + return r.getString(resId); + } + + // TODO: remove legacy resource fallback after migrating its overlays. + private String[] getPlatformInterfaceConfigs(Context context) { + final Resources r = context.getResources(); + final int resId = r.getIdentifier("config_ethernet_interfaces", "array", + context.getPackageName()); + return r.getStringArray(resId); + } + + public String getInterfaceRegexFromResource(Context context) { + final String platformRegex = getPlatformRegexResource(context); + final String match; + if (!LEGACY_IFACE_REGEXP.equals(platformRegex)) { + // Platform resource is not the historical default: use the overlay + match = platformRegex; + } else { + final ConnectivityResources resources = new ConnectivityResources(context); + match = resources.get().getString( + com.android.connectivity.resources.R.string.config_ethernet_iface_regex); + } + return match; + } + + public String[] getInterfaceConfigFromResource(Context context) { + final String[] platformInterfaceConfigs = getPlatformInterfaceConfigs(context); + final String[] interfaceConfigs; + if (platformInterfaceConfigs.length != 0) { + // Platform resource is not the historical default: use the overlay + interfaceConfigs = platformInterfaceConfigs; + } else { + final ConnectivityResources resources = new ConnectivityResources(context); + interfaceConfigs = resources.get().getStringArray( + com.android.connectivity.resources.R.array.config_ethernet_interfaces); + } + return interfaceConfigs; + } + } + EthernetTracker(@NonNull final Context context, @NonNull final Handler handler, @NonNull final EthernetNetworkFactory factory, @NonNull final INetd netd) { + this(context, handler, factory, netd, new Dependencies()); + } + + @VisibleForTesting + EthernetTracker(@NonNull final Context context, @NonNull final Handler handler, + @NonNull final EthernetNetworkFactory factory, @NonNull final INetd netd, + @NonNull final Dependencies deps) { mContext = context; mHandler = handler; mFactory = factory; mNetd = netd; + mDeps = deps; // Interface match regex. updateIfaceMatchRegexp(); // Read default Ethernet interface configuration from resources - final String[] interfaceConfigs = context.getResources().getStringArray( - com.android.internal.R.array.config_ethernet_interfaces); + final String[] interfaceConfigs = mDeps.getInterfaceConfigFromResource(context); for (String strConfig : interfaceConfigs) { parseEthernetConfig(strConfig); } @@ -735,8 +792,7 @@ public class EthernetTracker { } private void updateIfaceMatchRegexp() { - final String match = mContext.getResources().getString( - com.android.internal.R.string.config_ethernet_iface_regex); + final String match = mDeps.getInterfaceRegexFromResource(mContext); mIfaceMatch = mIncludeTestInterfaces ? "(" + match + "|" + TEST_IFACE_REGEXP + ")" : match; diff --git a/tests/Android.bp b/tests/Android.bp index 28b13c5..6cfebdc 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -17,10 +17,17 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], } +// TODO: merge the tests into service-connectivity tests after +// ethernet service migration completes. So far just import the +// ethernet service source to fix the dependencies. android_test { name: "EthernetServiceTests", - srcs: ["java/**/*.java"], + srcs: [ + ":ethernet-service-updatable-sources", + ":services.connectivity-ethernet-sources", + "java/**/*.java", + ], certificate: "platform", platform_apis: true, @@ -29,11 +36,13 @@ android_test { "android.test.runner", "android.test.base", "android.test.mock", + "framework-connectivity.impl", + "framework-connectivity-t.impl", + "ServiceConnectivityResources", ], static_libs: [ "androidx.test.rules", - "ethernet-service", "frameworks-base-testutils", "mockito-target-minus-junit4", "net-tests-utils", diff --git a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java index 61425bf..501324a 100644 --- a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java +++ b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java @@ -63,7 +63,7 @@ import android.util.Pair; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.R; +import com.android.connectivity.resources.R; import com.android.net.module.util.InterfaceParams; import com.android.testutils.DevSdkIgnoreRule; @@ -174,8 +174,7 @@ public class EthernetNetworkFactoryTest { } private void setupContext() { - when(mContext.getResources()).thenReturn(mResources); - when(mResources.getString(R.string.config_ethernet_tcp_buffers)).thenReturn(""); + when(mDeps.getTcpBufferSizesFromResource(eq(mContext))).thenReturn(""); } @After diff --git a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java index d86cc0f..ef70d94 100644 --- a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java +++ b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java @@ -27,7 +27,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -48,7 +47,7 @@ import android.os.RemoteException; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.R; +import com.android.connectivity.resources.R; import com.android.testutils.HandlerUtils; import org.junit.After; @@ -73,7 +72,7 @@ public class EthernetTrackerTest { @Mock private Context mContext; @Mock private EthernetNetworkFactory mFactory; @Mock private INetd mNetd; - @Mock Resources mResources; + @Mock private EthernetTracker.Dependencies mDeps; @Before public void setUp() throws RemoteException { @@ -83,7 +82,8 @@ public class EthernetTrackerTest { when(mNetd.interfaceGetList()).thenReturn(new String[0]); mHandlerThread = new HandlerThread(THREAD_NAME); mHandlerThread.start(); - tracker = new EthernetTracker(mContext, mHandlerThread.getThreadHandler(), mFactory, mNetd); + tracker = new EthernetTracker(mContext, mHandlerThread.getThreadHandler(), mFactory, mNetd, + mDeps); } @After @@ -92,9 +92,8 @@ public class EthernetTrackerTest { } 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(); + when(mDeps.getInterfaceRegexFromResource(eq(mContext))).thenReturn(""); + when(mDeps.getInterfaceConfigFromResource(eq(mContext))).thenReturn(new String[0]); } private void waitForIdle() { -- cgit v1.2.3 From d67ab967851137398c48cbaef827a56bf88216f5 Mon Sep 17 00:00:00 2001 From: James Mattis Date: Tue, 8 Mar 2022 18:05:14 -0800 Subject: Cleaning EthernetNetworkFactoryTest Unprovisioned Clearing up the EthernetNetworkFactoryTest unprovisioned helped method a bit to accomplish the same thing with fewer lines of code and not rely on the ref count to stop an interface. Bug: 197548738 Test: atest EthernetServiceTests Change-Id: I0fb80b569b6f59ee82fbd528ffad044278ab80c8 --- .../server/ethernet/EthernetNetworkFactoryTest.java | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java index 61425bf..28fcf3e 100644 --- a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java +++ b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java @@ -280,19 +280,13 @@ public class EthernetNetworkFactoryTest { // creates an unprovisioned interface private void createUnprovisionedInterface(String iface) throws Exception { - // the only way to create an unprovisioned interface is by calling needNetworkFor - // followed by releaseNetworkFor which will stop the NetworkAgent and IpClient. When - // EthernetNetworkFactory#updateInterfaceLinkState(iface, true) is called, the interface - // is automatically provisioned even if nobody has ever called needNetworkFor + // 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); - // Interface is already provisioned, so startProvisioning / register should not be called - // again - mNetFactory.needNetworkFor(createDefaultRequest()); - verify(mIpClient, never()).startProvisioning(any()); - verify(mNetworkAgent, never()).register(); - - mNetFactory.releaseNetworkFor(createDefaultRequest()); + mNetworkAgent.getCallbacks().onNetworkUnwanted(); + mLooper.dispatchAll(); verifyStop(); clearInvocations(mIpClient); -- cgit v1.2.3 From 7e81787e0bf8ee33dec8c4bb7e474ca61c8b9ef1 Mon Sep 17 00:00:00 2001 From: Patrick Rohr Date: Mon, 14 Mar 2022 17:30:18 +0100 Subject: Add support for Nullable IpConfiguration Test: atest EthernetServiceTests Change-Id: I6b415ffb2f5825a9dffda1366b60c1e0d26f4e64 --- .../server/ethernet/EthernetNetworkFactory.java | 11 ++++++---- .../android/server/ethernet/EthernetTracker.java | 11 +++++++--- .../ethernet/EthernetNetworkFactoryTest.java | 24 ++++++++++++++++++++++ .../server/ethernet/EthernetServiceImplTest.java | 13 ++++++++++++ 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java index ce0d77c..ef3abba 100644 --- a/java/com/android/server/ethernet/EthernetNetworkFactory.java +++ b/java/com/android/server/ethernet/EthernetNetworkFactory.java @@ -233,7 +233,8 @@ public class EthernetNetworkFactory extends NetworkFactory { * 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 ipConfig the desired {@link IpConfiguration} for the given network or null. If + * {@code null} is passed, the existing IpConfiguration is not updated. * @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 @@ -243,7 +244,7 @@ public class EthernetNetworkFactory extends NetworkFactory { */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) protected void updateInterface(@NonNull final String ifaceName, - @NonNull final IpConfiguration ipConfig, + @Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, @Nullable final IEthernetNetworkManagementListener listener) { if (!hasInterface(ifaceName)) { @@ -499,7 +500,7 @@ public class EthernetNetworkFactory extends NetworkFactory { mLegacyType = getLegacyType(mCapabilities); } - void updateInterface(@NonNull final IpConfiguration ipConfig, + void updateInterface(@Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, @Nullable final IEthernetNetworkManagementListener listener) { if (DBG) { @@ -510,7 +511,9 @@ public class EthernetNetworkFactory extends NetworkFactory { ); } - mIpConfig = ipConfig; + if (null != ipConfig){ + mIpConfig = ipConfig; + } if (null != capabilities) { setCapabilities(capabilities); } diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java index 1b696a4..074c81b 100644 --- a/java/com/android/server/ethernet/EthernetTracker.java +++ b/java/com/android/server/ethernet/EthernetTracker.java @@ -289,15 +289,20 @@ public class EthernetTracker { @VisibleForTesting(visibility = PACKAGE) protected void updateConfiguration(@NonNull final String iface, - @NonNull final IpConfiguration ipConfig, + @Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, @Nullable final IEthernetNetworkManagementListener listener) { if (DBG) { Log.i(TAG, "updateConfiguration, iface: " + iface + ", capabilities: " + capabilities + ", ipConfig: " + ipConfig); } - final IpConfiguration localIpConfig = new IpConfiguration(ipConfig); - writeIpConfiguration(iface, localIpConfig); + + final IpConfiguration localIpConfig = ipConfig == null + ? null : new IpConfiguration(ipConfig); + if (ipConfig != null) { + writeIpConfiguration(iface, localIpConfig); + } + if (null != capabilities) { mNetworkCapabilities.put(iface, capabilities); } diff --git a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java index 501324a..726833f 100644 --- a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java +++ b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java @@ -776,4 +776,28 @@ public class EthernetNetworkFactoryTest { verifyNoStopOrStart(); assertFailedListener(listener, "can't be updated as it is not available"); } + + @Test + public void testUpdateInterfaceWithNullIpConfiguration() throws Exception { + initEthernetNetworkFactory(); + createAndVerifyProvisionedInterface(TEST_IFACE); + + final IpConfiguration initialIpConfig = createStaticIpConfig(); + mNetFactory.updateInterface(TEST_IFACE, initialIpConfig, null /*capabilities*/, + null /*listener*/); + triggerOnProvisioningSuccess(); + verifyRestart(initialIpConfig); + + // TODO: have verifyXyz functions clear invocations. + clearInvocations(mDeps); + clearInvocations(mIpClient); + clearInvocations(mNetworkAgent); + + + // verify that sending a null ipConfig does not update the current ipConfig. + mNetFactory.updateInterface(TEST_IFACE, null /*ipConfig*/, null /*capabilities*/, + null /*listener*/); + triggerOnProvisioningSuccess(); + verifyRestart(initialIpConfig); + } } diff --git a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java index 175a97d..2131f7f 100644 --- a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java +++ b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java @@ -60,6 +60,10 @@ public class EthernetServiceImplTest { new EthernetNetworkUpdateRequest.Builder() .setIpConfiguration(new IpConfiguration()) .build(); + private static final EthernetNetworkUpdateRequest UPDATE_REQUEST_WITHOUT_IP_CONFIG = + new EthernetNetworkUpdateRequest.Builder() + .setNetworkCapabilities(new NetworkCapabilities.Builder().build()) + .build(); private static final IEthernetNetworkManagementListener NULL_LISTENER = null; private EthernetServiceImpl mEthernetServiceImpl; @Mock private Context mContext; @@ -275,6 +279,15 @@ public class EthernetServiceImplTest { eq(request.getNetworkCapabilities()), isNull()); } + @Test + public void testUpdateConfigurationAcceptsRequestWithNullIpConfiguration() { + mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST_WITHOUT_IP_CONFIG, + NULL_LISTENER); + verify(mEthernetTracker).updateConfiguration(eq(TEST_IFACE), + eq(UPDATE_REQUEST_WITHOUT_IP_CONFIG.getIpConfiguration()), + eq(UPDATE_REQUEST_WITHOUT_IP_CONFIG.getNetworkCapabilities()), isNull()); + } + @Test public void testUpdateConfigurationRejectsInvalidTestRequest() { enableTestInterface(); -- cgit v1.2.3 From dedbb58eed25bf466d3139081f8ac4492065ea57 Mon Sep 17 00:00:00 2001 From: Patrick Rohr Date: Wed, 16 Mar 2022 10:49:18 +0100 Subject: Change network management listener to outcome receiver Adopts new API according to API review feedback. The outcome receiver can later be removed from most of the ethernet code; in a perfect world, a result can be generated and an exception captured directly from EthernetServiceImpl. This will greatly simplify the current implementation. Bug: 220017952 Test: atest EthernetServiceTests Change-Id: Id8fadfed9fcfd22f04f6d7c3460b5956e571e01f --- .../server/ethernet/EthernetNetworkFactory.java | 46 +++++++------- .../server/ethernet/EthernetServiceImpl.java | 8 +-- .../android/server/ethernet/EthernetTracker.java | 10 +-- .../ethernet/EthernetNetworkFactoryTest.java | 72 ++++++++++------------ .../server/ethernet/EthernetServiceImplTest.java | 4 +- .../server/ethernet/EthernetTrackerTest.java | 6 +- 6 files changed, 71 insertions(+), 75 deletions(-) diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java index ef3abba..4f15355 100644 --- a/java/com/android/server/ethernet/EthernetNetworkFactory.java +++ b/java/com/android/server/ethernet/EthernetNetworkFactory.java @@ -24,8 +24,8 @@ import android.net.ConnectivityManager; import android.net.ConnectivityResources; import android.net.EthernetManager; import android.net.EthernetNetworkSpecifier; -import android.net.IEthernetNetworkManagementListener; import android.net.EthernetNetworkManagementException; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.IpConfiguration; import android.net.IpConfiguration.IpAssignment; import android.net.IpConfiguration.ProxySettings; @@ -239,14 +239,14 @@ public class EthernetNetworkFactory extends NetworkFactory { * {@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 IEthernetNetworkManagementListener} to notify callers of + * @param listener an optional {@link INetworkInterfaceOutcomeReceiver} to notify callers of * completion. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) protected void updateInterface(@NonNull final String ifaceName, @Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (!hasInterface(ifaceName)) { maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); return; @@ -295,7 +295,7 @@ public class EthernetNetworkFactory extends NetworkFactory { /** Returns true if state has been modified */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) protected boolean updateInterfaceLinkState(@NonNull final String ifaceName, final boolean up, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (!hasInterface(ifaceName)) { maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); return false; @@ -310,7 +310,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } private void maybeSendNetworkManagementCallbackForUntracked( - String ifaceName, IEthernetNetworkManagementListener listener) { + String ifaceName, INetworkInterfaceOutcomeReceiver listener) { maybeSendNetworkManagementCallback(listener, null, new EthernetNetworkManagementException( ifaceName + " can't be updated as it is not available.")); @@ -353,15 +353,19 @@ public class EthernetNetworkFactory extends NetworkFactory { } private static void maybeSendNetworkManagementCallback( - @Nullable final IEthernetNetworkManagementListener listener, - @Nullable final Network network, + @Nullable final INetworkInterfaceOutcomeReceiver listener, + @Nullable final String iface, @Nullable final EthernetNetworkManagementException e) { if (null == listener) { return; } try { - listener.onComplete(network, e); + if (iface != null) { + listener.onResult(iface); + } else { + listener.onError(e); + } } catch (RemoteException re) { Log.e(TAG, "Can't send onComplete for network management callback", re); } @@ -415,9 +419,9 @@ public class EthernetNetworkFactory extends NetworkFactory { private class EthernetIpClientCallback extends IpClientCallbacks { private final ConditionVariable mIpClientStartCv = new ConditionVariable(false); private final ConditionVariable mIpClientShutdownCv = new ConditionVariable(false); - @Nullable IEthernetNetworkManagementListener mNetworkManagementListener; + @Nullable INetworkInterfaceOutcomeReceiver mNetworkManagementListener; - EthernetIpClientCallback(@Nullable final IEthernetNetworkManagementListener listener) { + EthernetIpClientCallback(@Nullable final INetworkInterfaceOutcomeReceiver listener) { mNetworkManagementListener = listener; } @@ -502,7 +506,7 @@ public class EthernetNetworkFactory extends NetworkFactory { void updateInterface(@Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (DBG) { Log.d(TAG, "updateInterface, iface: " + name + ", ipConfig: " + ipConfig + ", old ipConfig: " + mIpConfig @@ -533,7 +537,7 @@ public class EthernetNetworkFactory extends NetworkFactory { start(null); } - private void start(@Nullable final IEthernetNetworkManagementListener listener) { + private void start(@Nullable final INetworkInterfaceOutcomeReceiver listener) { if (mIpClient != null) { if (DBG) Log.d(TAG, "IpClient already started"); return; @@ -553,7 +557,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } void onIpLayerStarted(@NonNull final LinkProperties linkProperties, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver 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 @@ -593,10 +597,10 @@ public class EthernetNetworkFactory extends NetworkFactory { }); mNetworkAgent.register(); mNetworkAgent.markConnected(); - realizeNetworkManagementCallback(mNetworkAgent.getNetwork(), null); + realizeNetworkManagementCallback(name, null); } - void onIpLayerStopped(@Nullable final IEthernetNetworkManagementListener listener) { + void onIpLayerStopped(@Nullable final INetworkInterfaceOutcomeReceiver 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) { @@ -622,7 +626,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } // Must be called on the handler thread - private void realizeNetworkManagementCallback(@Nullable final Network network, + private void realizeNetworkManagementCallback(@Nullable final String iface, @Nullable final EthernetNetworkManagementException e) { ensureRunningOnEthernetHandlerThread(); if (null == mIpClientCallback) { @@ -630,7 +634,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } EthernetNetworkFactory.maybeSendNetworkManagementCallback( - mIpClientCallback.mNetworkManagementListener, network, e); + mIpClientCallback.mNetworkManagementListener, iface, e); // Only send a single callback per listener. mIpClientCallback.mNetworkManagementListener = null; } @@ -671,7 +675,7 @@ public class EthernetNetworkFactory extends NetworkFactory { /** Returns true if state has been modified */ boolean updateLinkState(final boolean up, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (mLinkUp == up) { EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, null, new EthernetNetworkManagementException( @@ -681,13 +685,11 @@ public class EthernetNetworkFactory extends NetworkFactory { mLinkUp = up; if (!up) { // was up, goes down - // Save an instance of the current network to use with the callback before stop(). - final Network network = mNetworkAgent != null ? mNetworkAgent.getNetwork() : null; // Send an abort on a provisioning request callback if necessary before stopping. maybeSendNetworkManagementCallbackForAbort(); stop(); // If only setting the interface down, send a callback to signal completion. - EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, network, null); + EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, name, null); } else { // was down, goes up stop(); start(listener); @@ -742,7 +744,7 @@ public class EthernetNetworkFactory extends NetworkFactory { restart(null); } - void restart(@Nullable final IEthernetNetworkManagementListener listener){ + void restart(@Nullable final INetworkInterfaceOutcomeReceiver listener) { if (DBG) Log.d(TAG, "reconnecting Ethernet"); stop(); start(listener); diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java index 50b4684..edda321 100644 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ b/java/com/android/server/ethernet/EthernetServiceImpl.java @@ -24,7 +24,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.net.IEthernetManager; import android.net.IEthernetServiceListener; -import android.net.IEthernetNetworkManagementListener; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.ITetheredInterfaceCallback; import android.net.EthernetNetworkUpdateRequest; import android.net.IpConfiguration; @@ -243,7 +243,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { @Override public void updateConfiguration(@NonNull final String iface, @NonNull final EthernetNetworkUpdateRequest request, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { Objects.requireNonNull(iface); Objects.requireNonNull(request); throwIfEthernetNotStarted(); @@ -260,7 +260,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { @Override public void connectNetwork(@NonNull final String iface, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { Log.i(TAG, "connectNetwork called with: iface=" + iface + ", listener=" + listener); Objects.requireNonNull(iface); throwIfEthernetNotStarted(); @@ -272,7 +272,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { @Override public void disconnectNetwork(@NonNull final String iface, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { Log.i(TAG, "disconnectNetwork called with: iface=" + iface + ", listener=" + listener); Objects.requireNonNull(iface); throwIfEthernetNotStarted(); diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java index 074c81b..0a0e327 100644 --- a/java/com/android/server/ethernet/EthernetTracker.java +++ b/java/com/android/server/ethernet/EthernetTracker.java @@ -27,7 +27,7 @@ import android.content.res.Resources; import android.net.ConnectivityResources; import android.net.EthernetManager; import android.net.IEthernetServiceListener; -import android.net.IEthernetNetworkManagementListener; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.INetd; import android.net.ITetheredInterfaceCallback; import android.net.InterfaceConfigurationParcel; @@ -291,7 +291,7 @@ public class EthernetTracker { protected void updateConfiguration(@NonNull final String iface, @Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (DBG) { Log.i(TAG, "updateConfiguration, iface: " + iface + ", capabilities: " + capabilities + ", ipConfig: " + ipConfig); @@ -314,13 +314,13 @@ public class EthernetTracker { @VisibleForTesting(visibility = PACKAGE) protected void connectNetwork(@NonNull final String iface, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { mHandler.post(() -> updateInterfaceState(iface, true, listener)); } @VisibleForTesting(visibility = PACKAGE) protected void disconnectNetwork(@NonNull final String iface, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { mHandler.post(() -> updateInterfaceState(iface, false, listener)); } @@ -505,7 +505,7 @@ public class EthernetTracker { } private void updateInterfaceState(@NonNull final String iface, final boolean up, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { final int mode = getInterfaceMode(iface); final boolean factoryLinkStateUpdated = (mode == INTERFACE_MODE_CLIENT) && mFactory.updateInterfaceLinkState(iface, up, listener); diff --git a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java index 726833f..5d23aaf 100644 --- a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java +++ b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java @@ -41,8 +41,8 @@ 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.INetworkInterfaceOutcomeReceiver; import android.net.IpConfiguration; import android.net.LinkAddress; import android.net.LinkProperties; @@ -85,7 +85,7 @@ import java.util.concurrent.TimeUnit; 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 INetworkInterfaceOutcomeReceiver 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"; @@ -322,7 +322,7 @@ public class EthernetNetworkFactoryTest { assertTrue(ret); verify(mIpClient).shutdown(); - assertSuccessfulListener(listener, null); + assertEquals(listener.expectOnResult(), TEST_IFACE); } @Test @@ -336,7 +336,7 @@ public class EthernetNetworkFactoryTest { assertTrue(ret); verifyStop(); - assertSuccessfulListener(listener, mMockNetwork); + assertEquals(listener.expectOnResult(), TEST_IFACE); } @Test @@ -353,7 +353,7 @@ public class EthernetNetworkFactoryTest { verify(mDeps, never()).makeIpClient(any(), any(), any()); verify(mDeps, never()) .makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any()); - assertSuccessfulListener(listener, null); + assertEquals(listener.expectOnResult(), TEST_IFACE); } @Test @@ -367,7 +367,7 @@ public class EthernetNetworkFactoryTest { assertFalse(ret); verifyNoStopOrStart(); - assertFailedListener(listener, "can't be updated as it is not available"); + listener.expectOnErrorWithMessage("can't be updated as it is not available"); } @Test @@ -381,7 +381,7 @@ public class EthernetNetworkFactoryTest { assertFalse(ret); verifyNoStopOrStart(); - assertFailedListener(listener, "No changes"); + listener.expectOnErrorWithMessage("No changes"); } @Test @@ -638,18 +638,31 @@ public class EthernetNetworkFactoryTest { } private static final class TestNetworkManagementListener - implements IEthernetNetworkManagementListener { - private final CompletableFuture> mDone - = new CompletableFuture<>(); + implements INetworkInterfaceOutcomeReceiver { + private final CompletableFuture mResult = new CompletableFuture<>(); + private final CompletableFuture mError = + new CompletableFuture<>(); @Override - public void onComplete(final Network network, - final EthernetNetworkManagementException exception) { - mDone.complete(new Pair<>(network, exception)); + public void onResult(@NonNull String iface) { + mResult.complete(iface); } - Pair expectOnComplete() throws Exception { - return mDone.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + @Override + public void onError(@NonNull EthernetNetworkManagementException exception) { + mError.complete(exception); + } + + String expectOnResult() throws Exception { + return mResult.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + + EthernetNetworkManagementException expectOnError() throws Exception { + return mError.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + + void expectOnErrorWithMessage(String msg) throws Exception { + assertTrue(expectOnError().getMessage().contains(msg)); } @Override @@ -669,7 +682,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener); triggerOnProvisioningSuccess(); - assertSuccessfulListener(listener, mMockNetwork); + assertEquals(listener.expectOnResult(), TEST_IFACE); } @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available @@ -709,26 +722,7 @@ public class EthernetNetworkFactoryTest { triggerOnProvisioningSuccess(); }); - assertSuccessfulListener(successfulListener, mMockNetwork); - } - - private void assertSuccessfulListener( - @NonNull final TestNetworkManagementListener successfulListener, - @NonNull final Network expectedNetwork) throws Exception { - final Pair 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 failedResult = - failedListener.expectOnComplete(); - assertNull(failedResult.first); - assertNotNull(failedResult.second); - assertTrue(failedResult.second.getMessage().contains(errMsg)); + assertEquals(successfulListener.expectOnResult(), TEST_IFACE); } private void verifyNetworkManagementCallIsAbortedWhenInterrupted( @@ -743,7 +737,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(iface, ipConfiguration, capabilities, failedListener); interruptingRunnable.run(); - assertFailedListener(failedListener, "aborted"); + failedListener.expectOnErrorWithMessage("aborted"); } @Test @@ -757,7 +751,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener); triggerOnProvisioningSuccess(); - listener.expectOnComplete(); + assertEquals(listener.expectOnResult(), TEST_IFACE); verify(mDeps).makeEthernetNetworkAgent(any(), any(), eq(capabilities), any(), any(), any(), any()); verifyRestart(ipConfiguration); @@ -774,7 +768,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener); verifyNoStopOrStart(); - assertFailedListener(listener, "can't be updated as it is not available"); + listener.expectOnErrorWithMessage("can't be updated as it is not available"); } @Test diff --git a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java index 2131f7f..e67c4c8 100644 --- a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java +++ b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java @@ -32,7 +32,7 @@ import android.Manifest; import android.annotation.NonNull; import android.content.Context; import android.content.pm.PackageManager; -import android.net.IEthernetNetworkManagementListener; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.EthernetNetworkUpdateRequest; import android.net.IpConfiguration; import android.net.NetworkCapabilities; @@ -64,7 +64,7 @@ public class EthernetServiceImplTest { new EthernetNetworkUpdateRequest.Builder() .setNetworkCapabilities(new NetworkCapabilities.Builder().build()) .build(); - private static final IEthernetNetworkManagementListener NULL_LISTENER = null; + private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; private EthernetServiceImpl mEthernetServiceImpl; @Mock private Context mContext; @Mock private Handler mHandler; diff --git a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java index ef70d94..bab9643 100644 --- a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java +++ b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java @@ -33,7 +33,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; import android.net.InetAddresses; -import android.net.IEthernetNetworkManagementListener; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.INetd; import android.net.IpConfiguration; import android.net.IpConfiguration.IpAssignment; @@ -66,7 +66,7 @@ public class EthernetTrackerTest { private static final String TEST_IFACE = "test123"; private static final int TIMEOUT_MS = 1_000; private static final String THREAD_NAME = "EthernetServiceThread"; - private static final IEthernetNetworkManagementListener NULL_LISTENER = null; + private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; private EthernetTracker tracker; private HandlerThread mHandlerThread; @Mock private Context mContext; @@ -334,7 +334,7 @@ public class EthernetTrackerTest { new StaticIpConfiguration.Builder().setIpAddress(linkAddr).build(); final IpConfiguration ipConfig = new IpConfiguration.Builder().setStaticIpConfiguration(staticIpConfig).build(); - final IEthernetNetworkManagementListener listener = null; + final INetworkInterfaceOutcomeReceiver listener = null; tracker.updateConfiguration(TEST_IFACE, ipConfig, capabilities, listener); waitForIdle(); -- cgit v1.2.3 From 3ea0765ae6e5e789aaa1efd504c221e44bc3e86f Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Thu, 17 Mar 2022 15:20:53 +0000 Subject: Revert "Change network management listener to outcome receiver" Revert submission 2028203-ethernet-outcomereceiver Reason for revert: BuildMonitor investigating b/225169800 Reverted Changes: I4c204a848:Change Ethernet API to use OutcomeReceiver I7c46545a4:Change Ethernet API to use OutcomeReceiver Id8fadfed9:Change network management listener to outcome rece... Change-Id: I45ba68452b9dccedf72b68fdea6e31c07b86546d --- .../server/ethernet/EthernetNetworkFactory.java | 46 +++++++------- .../server/ethernet/EthernetServiceImpl.java | 8 +-- .../android/server/ethernet/EthernetTracker.java | 10 +-- .../ethernet/EthernetNetworkFactoryTest.java | 72 ++++++++++++---------- .../server/ethernet/EthernetServiceImplTest.java | 4 +- .../server/ethernet/EthernetTrackerTest.java | 6 +- 6 files changed, 75 insertions(+), 71 deletions(-) diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java index 4f15355..ef3abba 100644 --- a/java/com/android/server/ethernet/EthernetNetworkFactory.java +++ b/java/com/android/server/ethernet/EthernetNetworkFactory.java @@ -24,8 +24,8 @@ import android.net.ConnectivityManager; import android.net.ConnectivityResources; import android.net.EthernetManager; import android.net.EthernetNetworkSpecifier; +import android.net.IEthernetNetworkManagementListener; import android.net.EthernetNetworkManagementException; -import android.net.INetworkInterfaceOutcomeReceiver; import android.net.IpConfiguration; import android.net.IpConfiguration.IpAssignment; import android.net.IpConfiguration.ProxySettings; @@ -239,14 +239,14 @@ public class EthernetNetworkFactory extends NetworkFactory { * {@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 INetworkInterfaceOutcomeReceiver} to notify callers of + * @param listener an optional {@link IEthernetNetworkManagementListener} to notify callers of * completion. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) protected void updateInterface(@NonNull final String ifaceName, @Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { + @Nullable final IEthernetNetworkManagementListener listener) { if (!hasInterface(ifaceName)) { maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); return; @@ -295,7 +295,7 @@ public class EthernetNetworkFactory extends NetworkFactory { /** Returns true if state has been modified */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) protected boolean updateInterfaceLinkState(@NonNull final String ifaceName, final boolean up, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { + @Nullable final IEthernetNetworkManagementListener listener) { if (!hasInterface(ifaceName)) { maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); return false; @@ -310,7 +310,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } private void maybeSendNetworkManagementCallbackForUntracked( - String ifaceName, INetworkInterfaceOutcomeReceiver listener) { + String ifaceName, IEthernetNetworkManagementListener listener) { maybeSendNetworkManagementCallback(listener, null, new EthernetNetworkManagementException( ifaceName + " can't be updated as it is not available.")); @@ -353,19 +353,15 @@ public class EthernetNetworkFactory extends NetworkFactory { } private static void maybeSendNetworkManagementCallback( - @Nullable final INetworkInterfaceOutcomeReceiver listener, - @Nullable final String iface, + @Nullable final IEthernetNetworkManagementListener listener, + @Nullable final Network network, @Nullable final EthernetNetworkManagementException e) { if (null == listener) { return; } try { - if (iface != null) { - listener.onResult(iface); - } else { - listener.onError(e); - } + listener.onComplete(network, e); } catch (RemoteException re) { Log.e(TAG, "Can't send onComplete for network management callback", re); } @@ -419,9 +415,9 @@ public class EthernetNetworkFactory extends NetworkFactory { private class EthernetIpClientCallback extends IpClientCallbacks { private final ConditionVariable mIpClientStartCv = new ConditionVariable(false); private final ConditionVariable mIpClientShutdownCv = new ConditionVariable(false); - @Nullable INetworkInterfaceOutcomeReceiver mNetworkManagementListener; + @Nullable IEthernetNetworkManagementListener mNetworkManagementListener; - EthernetIpClientCallback(@Nullable final INetworkInterfaceOutcomeReceiver listener) { + EthernetIpClientCallback(@Nullable final IEthernetNetworkManagementListener listener) { mNetworkManagementListener = listener; } @@ -506,7 +502,7 @@ public class EthernetNetworkFactory extends NetworkFactory { void updateInterface(@Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { + @Nullable final IEthernetNetworkManagementListener listener) { if (DBG) { Log.d(TAG, "updateInterface, iface: " + name + ", ipConfig: " + ipConfig + ", old ipConfig: " + mIpConfig @@ -537,7 +533,7 @@ public class EthernetNetworkFactory extends NetworkFactory { start(null); } - private void start(@Nullable final INetworkInterfaceOutcomeReceiver listener) { + private void start(@Nullable final IEthernetNetworkManagementListener listener) { if (mIpClient != null) { if (DBG) Log.d(TAG, "IpClient already started"); return; @@ -557,7 +553,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } void onIpLayerStarted(@NonNull final LinkProperties linkProperties, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { + @Nullable final IEthernetNetworkManagementListener 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 @@ -597,10 +593,10 @@ public class EthernetNetworkFactory extends NetworkFactory { }); mNetworkAgent.register(); mNetworkAgent.markConnected(); - realizeNetworkManagementCallback(name, null); + realizeNetworkManagementCallback(mNetworkAgent.getNetwork(), null); } - void onIpLayerStopped(@Nullable final INetworkInterfaceOutcomeReceiver listener) { + void onIpLayerStopped(@Nullable final IEthernetNetworkManagementListener 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) { @@ -626,7 +622,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } // Must be called on the handler thread - private void realizeNetworkManagementCallback(@Nullable final String iface, + private void realizeNetworkManagementCallback(@Nullable final Network network, @Nullable final EthernetNetworkManagementException e) { ensureRunningOnEthernetHandlerThread(); if (null == mIpClientCallback) { @@ -634,7 +630,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } EthernetNetworkFactory.maybeSendNetworkManagementCallback( - mIpClientCallback.mNetworkManagementListener, iface, e); + mIpClientCallback.mNetworkManagementListener, network, e); // Only send a single callback per listener. mIpClientCallback.mNetworkManagementListener = null; } @@ -675,7 +671,7 @@ public class EthernetNetworkFactory extends NetworkFactory { /** Returns true if state has been modified */ boolean updateLinkState(final boolean up, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { + @Nullable final IEthernetNetworkManagementListener listener) { if (mLinkUp == up) { EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, null, new EthernetNetworkManagementException( @@ -685,11 +681,13 @@ public class EthernetNetworkFactory extends NetworkFactory { mLinkUp = up; if (!up) { // was up, goes down + // Save an instance of the current network to use with the callback before stop(). + final Network network = mNetworkAgent != null ? mNetworkAgent.getNetwork() : null; // Send an abort on a provisioning request callback if necessary before stopping. maybeSendNetworkManagementCallbackForAbort(); stop(); // If only setting the interface down, send a callback to signal completion. - EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, name, null); + EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, network, null); } else { // was down, goes up stop(); start(listener); @@ -744,7 +742,7 @@ public class EthernetNetworkFactory extends NetworkFactory { restart(null); } - void restart(@Nullable final INetworkInterfaceOutcomeReceiver listener) { + void restart(@Nullable final IEthernetNetworkManagementListener listener){ if (DBG) Log.d(TAG, "reconnecting Ethernet"); stop(); start(listener); diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java index edda321..50b4684 100644 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ b/java/com/android/server/ethernet/EthernetServiceImpl.java @@ -24,7 +24,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.net.IEthernetManager; import android.net.IEthernetServiceListener; -import android.net.INetworkInterfaceOutcomeReceiver; +import android.net.IEthernetNetworkManagementListener; import android.net.ITetheredInterfaceCallback; import android.net.EthernetNetworkUpdateRequest; import android.net.IpConfiguration; @@ -243,7 +243,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { @Override public void updateConfiguration(@NonNull final String iface, @NonNull final EthernetNetworkUpdateRequest request, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { + @Nullable final IEthernetNetworkManagementListener listener) { Objects.requireNonNull(iface); Objects.requireNonNull(request); throwIfEthernetNotStarted(); @@ -260,7 +260,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { @Override public void connectNetwork(@NonNull final String iface, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { + @Nullable final IEthernetNetworkManagementListener listener) { Log.i(TAG, "connectNetwork called with: iface=" + iface + ", listener=" + listener); Objects.requireNonNull(iface); throwIfEthernetNotStarted(); @@ -272,7 +272,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { @Override public void disconnectNetwork(@NonNull final String iface, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { + @Nullable final IEthernetNetworkManagementListener listener) { Log.i(TAG, "disconnectNetwork called with: iface=" + iface + ", listener=" + listener); Objects.requireNonNull(iface); throwIfEthernetNotStarted(); diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java index 0a0e327..074c81b 100644 --- a/java/com/android/server/ethernet/EthernetTracker.java +++ b/java/com/android/server/ethernet/EthernetTracker.java @@ -27,7 +27,7 @@ import android.content.res.Resources; import android.net.ConnectivityResources; import android.net.EthernetManager; import android.net.IEthernetServiceListener; -import android.net.INetworkInterfaceOutcomeReceiver; +import android.net.IEthernetNetworkManagementListener; import android.net.INetd; import android.net.ITetheredInterfaceCallback; import android.net.InterfaceConfigurationParcel; @@ -291,7 +291,7 @@ public class EthernetTracker { protected void updateConfiguration(@NonNull final String iface, @Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { + @Nullable final IEthernetNetworkManagementListener listener) { if (DBG) { Log.i(TAG, "updateConfiguration, iface: " + iface + ", capabilities: " + capabilities + ", ipConfig: " + ipConfig); @@ -314,13 +314,13 @@ public class EthernetTracker { @VisibleForTesting(visibility = PACKAGE) protected void connectNetwork(@NonNull final String iface, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { + @Nullable final IEthernetNetworkManagementListener listener) { mHandler.post(() -> updateInterfaceState(iface, true, listener)); } @VisibleForTesting(visibility = PACKAGE) protected void disconnectNetwork(@NonNull final String iface, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { + @Nullable final IEthernetNetworkManagementListener listener) { mHandler.post(() -> updateInterfaceState(iface, false, listener)); } @@ -505,7 +505,7 @@ public class EthernetTracker { } private void updateInterfaceState(@NonNull final String iface, final boolean up, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { + @Nullable final IEthernetNetworkManagementListener listener) { final int mode = getInterfaceMode(iface); final boolean factoryLinkStateUpdated = (mode == INTERFACE_MODE_CLIENT) && mFactory.updateInterfaceLinkState(iface, up, listener); diff --git a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java index 5d23aaf..726833f 100644 --- a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java +++ b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java @@ -41,8 +41,8 @@ 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.INetworkInterfaceOutcomeReceiver; import android.net.IpConfiguration; import android.net.LinkAddress; import android.net.LinkProperties; @@ -85,7 +85,7 @@ import java.util.concurrent.TimeUnit; public class EthernetNetworkFactoryTest { private static final int TIMEOUT_MS = 2_000; private static final String TEST_IFACE = "test123"; - private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; + 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"; @@ -322,7 +322,7 @@ public class EthernetNetworkFactoryTest { assertTrue(ret); verify(mIpClient).shutdown(); - assertEquals(listener.expectOnResult(), TEST_IFACE); + assertSuccessfulListener(listener, null); } @Test @@ -336,7 +336,7 @@ public class EthernetNetworkFactoryTest { assertTrue(ret); verifyStop(); - assertEquals(listener.expectOnResult(), TEST_IFACE); + assertSuccessfulListener(listener, mMockNetwork); } @Test @@ -353,7 +353,7 @@ public class EthernetNetworkFactoryTest { verify(mDeps, never()).makeIpClient(any(), any(), any()); verify(mDeps, never()) .makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any()); - assertEquals(listener.expectOnResult(), TEST_IFACE); + assertSuccessfulListener(listener, null); } @Test @@ -367,7 +367,7 @@ public class EthernetNetworkFactoryTest { assertFalse(ret); verifyNoStopOrStart(); - listener.expectOnErrorWithMessage("can't be updated as it is not available"); + assertFailedListener(listener, "can't be updated as it is not available"); } @Test @@ -381,7 +381,7 @@ public class EthernetNetworkFactoryTest { assertFalse(ret); verifyNoStopOrStart(); - listener.expectOnErrorWithMessage("No changes"); + assertFailedListener(listener, "No changes"); } @Test @@ -638,31 +638,18 @@ public class EthernetNetworkFactoryTest { } private static final class TestNetworkManagementListener - implements INetworkInterfaceOutcomeReceiver { - private final CompletableFuture mResult = new CompletableFuture<>(); - private final CompletableFuture mError = - new CompletableFuture<>(); + implements IEthernetNetworkManagementListener { + private final CompletableFuture> mDone + = new CompletableFuture<>(); @Override - public void onResult(@NonNull String iface) { - mResult.complete(iface); + public void onComplete(final Network network, + final EthernetNetworkManagementException exception) { + mDone.complete(new Pair<>(network, exception)); } - @Override - public void onError(@NonNull EthernetNetworkManagementException exception) { - mError.complete(exception); - } - - String expectOnResult() throws Exception { - return mResult.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); - } - - EthernetNetworkManagementException expectOnError() throws Exception { - return mError.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); - } - - void expectOnErrorWithMessage(String msg) throws Exception { - assertTrue(expectOnError().getMessage().contains(msg)); + Pair expectOnComplete() throws Exception { + return mDone.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); } @Override @@ -682,7 +669,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener); triggerOnProvisioningSuccess(); - assertEquals(listener.expectOnResult(), TEST_IFACE); + assertSuccessfulListener(listener, mMockNetwork); } @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available @@ -722,7 +709,26 @@ public class EthernetNetworkFactoryTest { triggerOnProvisioningSuccess(); }); - assertEquals(successfulListener.expectOnResult(), TEST_IFACE); + assertSuccessfulListener(successfulListener, mMockNetwork); + } + + private void assertSuccessfulListener( + @NonNull final TestNetworkManagementListener successfulListener, + @NonNull final Network expectedNetwork) throws Exception { + final Pair 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 failedResult = + failedListener.expectOnComplete(); + assertNull(failedResult.first); + assertNotNull(failedResult.second); + assertTrue(failedResult.second.getMessage().contains(errMsg)); } private void verifyNetworkManagementCallIsAbortedWhenInterrupted( @@ -737,7 +743,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(iface, ipConfiguration, capabilities, failedListener); interruptingRunnable.run(); - failedListener.expectOnErrorWithMessage("aborted"); + assertFailedListener(failedListener, "aborted"); } @Test @@ -751,7 +757,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener); triggerOnProvisioningSuccess(); - assertEquals(listener.expectOnResult(), TEST_IFACE); + listener.expectOnComplete(); verify(mDeps).makeEthernetNetworkAgent(any(), any(), eq(capabilities), any(), any(), any(), any()); verifyRestart(ipConfiguration); @@ -768,7 +774,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener); verifyNoStopOrStart(); - listener.expectOnErrorWithMessage("can't be updated as it is not available"); + assertFailedListener(listener, "can't be updated as it is not available"); } @Test diff --git a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java index e67c4c8..2131f7f 100644 --- a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java +++ b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java @@ -32,7 +32,7 @@ import android.Manifest; import android.annotation.NonNull; import android.content.Context; import android.content.pm.PackageManager; -import android.net.INetworkInterfaceOutcomeReceiver; +import android.net.IEthernetNetworkManagementListener; import android.net.EthernetNetworkUpdateRequest; import android.net.IpConfiguration; import android.net.NetworkCapabilities; @@ -64,7 +64,7 @@ public class EthernetServiceImplTest { new EthernetNetworkUpdateRequest.Builder() .setNetworkCapabilities(new NetworkCapabilities.Builder().build()) .build(); - private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; + private static final IEthernetNetworkManagementListener NULL_LISTENER = null; private EthernetServiceImpl mEthernetServiceImpl; @Mock private Context mContext; @Mock private Handler mHandler; diff --git a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java index bab9643..ef70d94 100644 --- a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java +++ b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java @@ -33,7 +33,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; import android.net.InetAddresses; -import android.net.INetworkInterfaceOutcomeReceiver; +import android.net.IEthernetNetworkManagementListener; import android.net.INetd; import android.net.IpConfiguration; import android.net.IpConfiguration.IpAssignment; @@ -66,7 +66,7 @@ public class EthernetTrackerTest { private static final String TEST_IFACE = "test123"; private static final int TIMEOUT_MS = 1_000; private static final String THREAD_NAME = "EthernetServiceThread"; - private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; + private static final IEthernetNetworkManagementListener NULL_LISTENER = null; private EthernetTracker tracker; private HandlerThread mHandlerThread; @Mock private Context mContext; @@ -334,7 +334,7 @@ public class EthernetTrackerTest { new StaticIpConfiguration.Builder().setIpAddress(linkAddr).build(); final IpConfiguration ipConfig = new IpConfiguration.Builder().setStaticIpConfiguration(staticIpConfig).build(); - final INetworkInterfaceOutcomeReceiver listener = null; + final IEthernetNetworkManagementListener listener = null; tracker.updateConfiguration(TEST_IFACE, ipConfig, capabilities, listener); waitForIdle(); -- cgit v1.2.3 From e8fdf6ae92998fd4a82d906a76083dae5ed5efbd Mon Sep 17 00:00:00 2001 From: Patrick Rohr Date: Thu, 17 Mar 2022 19:19:21 +0000 Subject: Revert "Revert "Change network management listener to outcome re..." Revert submission 2030087-revert-2028203-ethernet-outcomereceiver-WFKWDORBPY Reason for revert: Reinstate changes Reverted Changes: Ibd1d70ac3:Revert "Change Ethernet API to use OutcomeReceiver... I45af594f7:Revert "Change Ethernet API to use OutcomeReceiver... I45ba68452:Revert "Change network management listener to outc... Merged-In: Id8fadfed9fcfd22f04f6d7c3460b5956e571e01f Change-Id: If3854c1c3a39aa6de84807c6bde267cf88f3d277 --- .../server/ethernet/EthernetNetworkFactory.java | 46 +++++++------- .../server/ethernet/EthernetServiceImpl.java | 8 +-- .../android/server/ethernet/EthernetTracker.java | 10 +-- .../ethernet/EthernetNetworkFactoryTest.java | 72 ++++++++++------------ .../server/ethernet/EthernetServiceImplTest.java | 4 +- .../server/ethernet/EthernetTrackerTest.java | 6 +- 6 files changed, 71 insertions(+), 75 deletions(-) diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java index ef3abba..4f15355 100644 --- a/java/com/android/server/ethernet/EthernetNetworkFactory.java +++ b/java/com/android/server/ethernet/EthernetNetworkFactory.java @@ -24,8 +24,8 @@ import android.net.ConnectivityManager; import android.net.ConnectivityResources; import android.net.EthernetManager; import android.net.EthernetNetworkSpecifier; -import android.net.IEthernetNetworkManagementListener; import android.net.EthernetNetworkManagementException; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.IpConfiguration; import android.net.IpConfiguration.IpAssignment; import android.net.IpConfiguration.ProxySettings; @@ -239,14 +239,14 @@ public class EthernetNetworkFactory extends NetworkFactory { * {@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 IEthernetNetworkManagementListener} to notify callers of + * @param listener an optional {@link INetworkInterfaceOutcomeReceiver} to notify callers of * completion. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) protected void updateInterface(@NonNull final String ifaceName, @Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (!hasInterface(ifaceName)) { maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); return; @@ -295,7 +295,7 @@ public class EthernetNetworkFactory extends NetworkFactory { /** Returns true if state has been modified */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) protected boolean updateInterfaceLinkState(@NonNull final String ifaceName, final boolean up, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (!hasInterface(ifaceName)) { maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); return false; @@ -310,7 +310,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } private void maybeSendNetworkManagementCallbackForUntracked( - String ifaceName, IEthernetNetworkManagementListener listener) { + String ifaceName, INetworkInterfaceOutcomeReceiver listener) { maybeSendNetworkManagementCallback(listener, null, new EthernetNetworkManagementException( ifaceName + " can't be updated as it is not available.")); @@ -353,15 +353,19 @@ public class EthernetNetworkFactory extends NetworkFactory { } private static void maybeSendNetworkManagementCallback( - @Nullable final IEthernetNetworkManagementListener listener, - @Nullable final Network network, + @Nullable final INetworkInterfaceOutcomeReceiver listener, + @Nullable final String iface, @Nullable final EthernetNetworkManagementException e) { if (null == listener) { return; } try { - listener.onComplete(network, e); + if (iface != null) { + listener.onResult(iface); + } else { + listener.onError(e); + } } catch (RemoteException re) { Log.e(TAG, "Can't send onComplete for network management callback", re); } @@ -415,9 +419,9 @@ public class EthernetNetworkFactory extends NetworkFactory { private class EthernetIpClientCallback extends IpClientCallbacks { private final ConditionVariable mIpClientStartCv = new ConditionVariable(false); private final ConditionVariable mIpClientShutdownCv = new ConditionVariable(false); - @Nullable IEthernetNetworkManagementListener mNetworkManagementListener; + @Nullable INetworkInterfaceOutcomeReceiver mNetworkManagementListener; - EthernetIpClientCallback(@Nullable final IEthernetNetworkManagementListener listener) { + EthernetIpClientCallback(@Nullable final INetworkInterfaceOutcomeReceiver listener) { mNetworkManagementListener = listener; } @@ -502,7 +506,7 @@ public class EthernetNetworkFactory extends NetworkFactory { void updateInterface(@Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (DBG) { Log.d(TAG, "updateInterface, iface: " + name + ", ipConfig: " + ipConfig + ", old ipConfig: " + mIpConfig @@ -533,7 +537,7 @@ public class EthernetNetworkFactory extends NetworkFactory { start(null); } - private void start(@Nullable final IEthernetNetworkManagementListener listener) { + private void start(@Nullable final INetworkInterfaceOutcomeReceiver listener) { if (mIpClient != null) { if (DBG) Log.d(TAG, "IpClient already started"); return; @@ -553,7 +557,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } void onIpLayerStarted(@NonNull final LinkProperties linkProperties, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver 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 @@ -593,10 +597,10 @@ public class EthernetNetworkFactory extends NetworkFactory { }); mNetworkAgent.register(); mNetworkAgent.markConnected(); - realizeNetworkManagementCallback(mNetworkAgent.getNetwork(), null); + realizeNetworkManagementCallback(name, null); } - void onIpLayerStopped(@Nullable final IEthernetNetworkManagementListener listener) { + void onIpLayerStopped(@Nullable final INetworkInterfaceOutcomeReceiver 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) { @@ -622,7 +626,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } // Must be called on the handler thread - private void realizeNetworkManagementCallback(@Nullable final Network network, + private void realizeNetworkManagementCallback(@Nullable final String iface, @Nullable final EthernetNetworkManagementException e) { ensureRunningOnEthernetHandlerThread(); if (null == mIpClientCallback) { @@ -630,7 +634,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } EthernetNetworkFactory.maybeSendNetworkManagementCallback( - mIpClientCallback.mNetworkManagementListener, network, e); + mIpClientCallback.mNetworkManagementListener, iface, e); // Only send a single callback per listener. mIpClientCallback.mNetworkManagementListener = null; } @@ -671,7 +675,7 @@ public class EthernetNetworkFactory extends NetworkFactory { /** Returns true if state has been modified */ boolean updateLinkState(final boolean up, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (mLinkUp == up) { EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, null, new EthernetNetworkManagementException( @@ -681,13 +685,11 @@ public class EthernetNetworkFactory extends NetworkFactory { mLinkUp = up; if (!up) { // was up, goes down - // Save an instance of the current network to use with the callback before stop(). - final Network network = mNetworkAgent != null ? mNetworkAgent.getNetwork() : null; // Send an abort on a provisioning request callback if necessary before stopping. maybeSendNetworkManagementCallbackForAbort(); stop(); // If only setting the interface down, send a callback to signal completion. - EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, network, null); + EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, name, null); } else { // was down, goes up stop(); start(listener); @@ -742,7 +744,7 @@ public class EthernetNetworkFactory extends NetworkFactory { restart(null); } - void restart(@Nullable final IEthernetNetworkManagementListener listener){ + void restart(@Nullable final INetworkInterfaceOutcomeReceiver listener) { if (DBG) Log.d(TAG, "reconnecting Ethernet"); stop(); start(listener); diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java index 50b4684..edda321 100644 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ b/java/com/android/server/ethernet/EthernetServiceImpl.java @@ -24,7 +24,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.net.IEthernetManager; import android.net.IEthernetServiceListener; -import android.net.IEthernetNetworkManagementListener; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.ITetheredInterfaceCallback; import android.net.EthernetNetworkUpdateRequest; import android.net.IpConfiguration; @@ -243,7 +243,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { @Override public void updateConfiguration(@NonNull final String iface, @NonNull final EthernetNetworkUpdateRequest request, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { Objects.requireNonNull(iface); Objects.requireNonNull(request); throwIfEthernetNotStarted(); @@ -260,7 +260,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { @Override public void connectNetwork(@NonNull final String iface, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { Log.i(TAG, "connectNetwork called with: iface=" + iface + ", listener=" + listener); Objects.requireNonNull(iface); throwIfEthernetNotStarted(); @@ -272,7 +272,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { @Override public void disconnectNetwork(@NonNull final String iface, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { Log.i(TAG, "disconnectNetwork called with: iface=" + iface + ", listener=" + listener); Objects.requireNonNull(iface); throwIfEthernetNotStarted(); diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java index 074c81b..0a0e327 100644 --- a/java/com/android/server/ethernet/EthernetTracker.java +++ b/java/com/android/server/ethernet/EthernetTracker.java @@ -27,7 +27,7 @@ import android.content.res.Resources; import android.net.ConnectivityResources; import android.net.EthernetManager; import android.net.IEthernetServiceListener; -import android.net.IEthernetNetworkManagementListener; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.INetd; import android.net.ITetheredInterfaceCallback; import android.net.InterfaceConfigurationParcel; @@ -291,7 +291,7 @@ public class EthernetTracker { protected void updateConfiguration(@NonNull final String iface, @Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (DBG) { Log.i(TAG, "updateConfiguration, iface: " + iface + ", capabilities: " + capabilities + ", ipConfig: " + ipConfig); @@ -314,13 +314,13 @@ public class EthernetTracker { @VisibleForTesting(visibility = PACKAGE) protected void connectNetwork(@NonNull final String iface, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { mHandler.post(() -> updateInterfaceState(iface, true, listener)); } @VisibleForTesting(visibility = PACKAGE) protected void disconnectNetwork(@NonNull final String iface, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { mHandler.post(() -> updateInterfaceState(iface, false, listener)); } @@ -505,7 +505,7 @@ public class EthernetTracker { } private void updateInterfaceState(@NonNull final String iface, final boolean up, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { final int mode = getInterfaceMode(iface); final boolean factoryLinkStateUpdated = (mode == INTERFACE_MODE_CLIENT) && mFactory.updateInterfaceLinkState(iface, up, listener); diff --git a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java index 726833f..5d23aaf 100644 --- a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java +++ b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java @@ -41,8 +41,8 @@ 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.INetworkInterfaceOutcomeReceiver; import android.net.IpConfiguration; import android.net.LinkAddress; import android.net.LinkProperties; @@ -85,7 +85,7 @@ import java.util.concurrent.TimeUnit; 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 INetworkInterfaceOutcomeReceiver 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"; @@ -322,7 +322,7 @@ public class EthernetNetworkFactoryTest { assertTrue(ret); verify(mIpClient).shutdown(); - assertSuccessfulListener(listener, null); + assertEquals(listener.expectOnResult(), TEST_IFACE); } @Test @@ -336,7 +336,7 @@ public class EthernetNetworkFactoryTest { assertTrue(ret); verifyStop(); - assertSuccessfulListener(listener, mMockNetwork); + assertEquals(listener.expectOnResult(), TEST_IFACE); } @Test @@ -353,7 +353,7 @@ public class EthernetNetworkFactoryTest { verify(mDeps, never()).makeIpClient(any(), any(), any()); verify(mDeps, never()) .makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any()); - assertSuccessfulListener(listener, null); + assertEquals(listener.expectOnResult(), TEST_IFACE); } @Test @@ -367,7 +367,7 @@ public class EthernetNetworkFactoryTest { assertFalse(ret); verifyNoStopOrStart(); - assertFailedListener(listener, "can't be updated as it is not available"); + listener.expectOnErrorWithMessage("can't be updated as it is not available"); } @Test @@ -381,7 +381,7 @@ public class EthernetNetworkFactoryTest { assertFalse(ret); verifyNoStopOrStart(); - assertFailedListener(listener, "No changes"); + listener.expectOnErrorWithMessage("No changes"); } @Test @@ -638,18 +638,31 @@ public class EthernetNetworkFactoryTest { } private static final class TestNetworkManagementListener - implements IEthernetNetworkManagementListener { - private final CompletableFuture> mDone - = new CompletableFuture<>(); + implements INetworkInterfaceOutcomeReceiver { + private final CompletableFuture mResult = new CompletableFuture<>(); + private final CompletableFuture mError = + new CompletableFuture<>(); @Override - public void onComplete(final Network network, - final EthernetNetworkManagementException exception) { - mDone.complete(new Pair<>(network, exception)); + public void onResult(@NonNull String iface) { + mResult.complete(iface); } - Pair expectOnComplete() throws Exception { - return mDone.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + @Override + public void onError(@NonNull EthernetNetworkManagementException exception) { + mError.complete(exception); + } + + String expectOnResult() throws Exception { + return mResult.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + + EthernetNetworkManagementException expectOnError() throws Exception { + return mError.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + + void expectOnErrorWithMessage(String msg) throws Exception { + assertTrue(expectOnError().getMessage().contains(msg)); } @Override @@ -669,7 +682,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener); triggerOnProvisioningSuccess(); - assertSuccessfulListener(listener, mMockNetwork); + assertEquals(listener.expectOnResult(), TEST_IFACE); } @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available @@ -709,26 +722,7 @@ public class EthernetNetworkFactoryTest { triggerOnProvisioningSuccess(); }); - assertSuccessfulListener(successfulListener, mMockNetwork); - } - - private void assertSuccessfulListener( - @NonNull final TestNetworkManagementListener successfulListener, - @NonNull final Network expectedNetwork) throws Exception { - final Pair 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 failedResult = - failedListener.expectOnComplete(); - assertNull(failedResult.first); - assertNotNull(failedResult.second); - assertTrue(failedResult.second.getMessage().contains(errMsg)); + assertEquals(successfulListener.expectOnResult(), TEST_IFACE); } private void verifyNetworkManagementCallIsAbortedWhenInterrupted( @@ -743,7 +737,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(iface, ipConfiguration, capabilities, failedListener); interruptingRunnable.run(); - assertFailedListener(failedListener, "aborted"); + failedListener.expectOnErrorWithMessage("aborted"); } @Test @@ -757,7 +751,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener); triggerOnProvisioningSuccess(); - listener.expectOnComplete(); + assertEquals(listener.expectOnResult(), TEST_IFACE); verify(mDeps).makeEthernetNetworkAgent(any(), any(), eq(capabilities), any(), any(), any(), any()); verifyRestart(ipConfiguration); @@ -774,7 +768,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener); verifyNoStopOrStart(); - assertFailedListener(listener, "can't be updated as it is not available"); + listener.expectOnErrorWithMessage("can't be updated as it is not available"); } @Test diff --git a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java index 2131f7f..e67c4c8 100644 --- a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java +++ b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java @@ -32,7 +32,7 @@ import android.Manifest; import android.annotation.NonNull; import android.content.Context; import android.content.pm.PackageManager; -import android.net.IEthernetNetworkManagementListener; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.EthernetNetworkUpdateRequest; import android.net.IpConfiguration; import android.net.NetworkCapabilities; @@ -64,7 +64,7 @@ public class EthernetServiceImplTest { new EthernetNetworkUpdateRequest.Builder() .setNetworkCapabilities(new NetworkCapabilities.Builder().build()) .build(); - private static final IEthernetNetworkManagementListener NULL_LISTENER = null; + private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; private EthernetServiceImpl mEthernetServiceImpl; @Mock private Context mContext; @Mock private Handler mHandler; diff --git a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java index ef70d94..bab9643 100644 --- a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java +++ b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java @@ -33,7 +33,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; import android.net.InetAddresses; -import android.net.IEthernetNetworkManagementListener; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.INetd; import android.net.IpConfiguration; import android.net.IpConfiguration.IpAssignment; @@ -66,7 +66,7 @@ public class EthernetTrackerTest { private static final String TEST_IFACE = "test123"; private static final int TIMEOUT_MS = 1_000; private static final String THREAD_NAME = "EthernetServiceThread"; - private static final IEthernetNetworkManagementListener NULL_LISTENER = null; + private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; private EthernetTracker tracker; private HandlerThread mHandlerThread; @Mock private Context mContext; @@ -334,7 +334,7 @@ public class EthernetTrackerTest { new StaticIpConfiguration.Builder().setIpAddress(linkAddr).build(); final IpConfiguration ipConfig = new IpConfiguration.Builder().setStaticIpConfiguration(staticIpConfig).build(); - final IEthernetNetworkManagementListener listener = null; + final INetworkInterfaceOutcomeReceiver listener = null; tracker.updateConfiguration(TEST_IFACE, ipConfig, capabilities, listener); waitForIdle(); -- cgit v1.2.3 From eee66d8c376b6f5827e088b4ba9d0ba1b8a45ceb Mon Sep 17 00:00:00 2001 From: Patrick Rohr Date: Wed, 16 Mar 2022 10:49:18 +0100 Subject: Change network management listener to outcome receiver Adopts new API according to API review feedback. The outcome receiver can later be removed from most of the ethernet code; in a perfect world, a result can be generated and an exception captured directly from EthernetServiceImpl. This will greatly simplify the current implementation. Bug: 220017952 Test: atest EthernetServiceTests Ignore-AOSP-First: CP to make automerger happy. Change-Id: Id8fadfed9fcfd22f04f6d7c3460b5956e571e01f --- .../server/ethernet/EthernetNetworkFactory.java | 46 +++++++------- .../server/ethernet/EthernetServiceImpl.java | 8 +-- .../android/server/ethernet/EthernetTracker.java | 10 +-- .../ethernet/EthernetNetworkFactoryTest.java | 72 ++++++++++------------ .../server/ethernet/EthernetServiceImplTest.java | 4 +- .../server/ethernet/EthernetTrackerTest.java | 6 +- 6 files changed, 71 insertions(+), 75 deletions(-) diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java index ef3abba..4f15355 100644 --- a/java/com/android/server/ethernet/EthernetNetworkFactory.java +++ b/java/com/android/server/ethernet/EthernetNetworkFactory.java @@ -24,8 +24,8 @@ import android.net.ConnectivityManager; import android.net.ConnectivityResources; import android.net.EthernetManager; import android.net.EthernetNetworkSpecifier; -import android.net.IEthernetNetworkManagementListener; import android.net.EthernetNetworkManagementException; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.IpConfiguration; import android.net.IpConfiguration.IpAssignment; import android.net.IpConfiguration.ProxySettings; @@ -239,14 +239,14 @@ public class EthernetNetworkFactory extends NetworkFactory { * {@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 IEthernetNetworkManagementListener} to notify callers of + * @param listener an optional {@link INetworkInterfaceOutcomeReceiver} to notify callers of * completion. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) protected void updateInterface(@NonNull final String ifaceName, @Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (!hasInterface(ifaceName)) { maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); return; @@ -295,7 +295,7 @@ public class EthernetNetworkFactory extends NetworkFactory { /** Returns true if state has been modified */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) protected boolean updateInterfaceLinkState(@NonNull final String ifaceName, final boolean up, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (!hasInterface(ifaceName)) { maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); return false; @@ -310,7 +310,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } private void maybeSendNetworkManagementCallbackForUntracked( - String ifaceName, IEthernetNetworkManagementListener listener) { + String ifaceName, INetworkInterfaceOutcomeReceiver listener) { maybeSendNetworkManagementCallback(listener, null, new EthernetNetworkManagementException( ifaceName + " can't be updated as it is not available.")); @@ -353,15 +353,19 @@ public class EthernetNetworkFactory extends NetworkFactory { } private static void maybeSendNetworkManagementCallback( - @Nullable final IEthernetNetworkManagementListener listener, - @Nullable final Network network, + @Nullable final INetworkInterfaceOutcomeReceiver listener, + @Nullable final String iface, @Nullable final EthernetNetworkManagementException e) { if (null == listener) { return; } try { - listener.onComplete(network, e); + if (iface != null) { + listener.onResult(iface); + } else { + listener.onError(e); + } } catch (RemoteException re) { Log.e(TAG, "Can't send onComplete for network management callback", re); } @@ -415,9 +419,9 @@ public class EthernetNetworkFactory extends NetworkFactory { private class EthernetIpClientCallback extends IpClientCallbacks { private final ConditionVariable mIpClientStartCv = new ConditionVariable(false); private final ConditionVariable mIpClientShutdownCv = new ConditionVariable(false); - @Nullable IEthernetNetworkManagementListener mNetworkManagementListener; + @Nullable INetworkInterfaceOutcomeReceiver mNetworkManagementListener; - EthernetIpClientCallback(@Nullable final IEthernetNetworkManagementListener listener) { + EthernetIpClientCallback(@Nullable final INetworkInterfaceOutcomeReceiver listener) { mNetworkManagementListener = listener; } @@ -502,7 +506,7 @@ public class EthernetNetworkFactory extends NetworkFactory { void updateInterface(@Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (DBG) { Log.d(TAG, "updateInterface, iface: " + name + ", ipConfig: " + ipConfig + ", old ipConfig: " + mIpConfig @@ -533,7 +537,7 @@ public class EthernetNetworkFactory extends NetworkFactory { start(null); } - private void start(@Nullable final IEthernetNetworkManagementListener listener) { + private void start(@Nullable final INetworkInterfaceOutcomeReceiver listener) { if (mIpClient != null) { if (DBG) Log.d(TAG, "IpClient already started"); return; @@ -553,7 +557,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } void onIpLayerStarted(@NonNull final LinkProperties linkProperties, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver 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 @@ -593,10 +597,10 @@ public class EthernetNetworkFactory extends NetworkFactory { }); mNetworkAgent.register(); mNetworkAgent.markConnected(); - realizeNetworkManagementCallback(mNetworkAgent.getNetwork(), null); + realizeNetworkManagementCallback(name, null); } - void onIpLayerStopped(@Nullable final IEthernetNetworkManagementListener listener) { + void onIpLayerStopped(@Nullable final INetworkInterfaceOutcomeReceiver 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) { @@ -622,7 +626,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } // Must be called on the handler thread - private void realizeNetworkManagementCallback(@Nullable final Network network, + private void realizeNetworkManagementCallback(@Nullable final String iface, @Nullable final EthernetNetworkManagementException e) { ensureRunningOnEthernetHandlerThread(); if (null == mIpClientCallback) { @@ -630,7 +634,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } EthernetNetworkFactory.maybeSendNetworkManagementCallback( - mIpClientCallback.mNetworkManagementListener, network, e); + mIpClientCallback.mNetworkManagementListener, iface, e); // Only send a single callback per listener. mIpClientCallback.mNetworkManagementListener = null; } @@ -671,7 +675,7 @@ public class EthernetNetworkFactory extends NetworkFactory { /** Returns true if state has been modified */ boolean updateLinkState(final boolean up, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (mLinkUp == up) { EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, null, new EthernetNetworkManagementException( @@ -681,13 +685,11 @@ public class EthernetNetworkFactory extends NetworkFactory { mLinkUp = up; if (!up) { // was up, goes down - // Save an instance of the current network to use with the callback before stop(). - final Network network = mNetworkAgent != null ? mNetworkAgent.getNetwork() : null; // Send an abort on a provisioning request callback if necessary before stopping. maybeSendNetworkManagementCallbackForAbort(); stop(); // If only setting the interface down, send a callback to signal completion. - EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, network, null); + EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, name, null); } else { // was down, goes up stop(); start(listener); @@ -742,7 +744,7 @@ public class EthernetNetworkFactory extends NetworkFactory { restart(null); } - void restart(@Nullable final IEthernetNetworkManagementListener listener){ + void restart(@Nullable final INetworkInterfaceOutcomeReceiver listener) { if (DBG) Log.d(TAG, "reconnecting Ethernet"); stop(); start(listener); diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java index 50b4684..edda321 100644 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ b/java/com/android/server/ethernet/EthernetServiceImpl.java @@ -24,7 +24,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.net.IEthernetManager; import android.net.IEthernetServiceListener; -import android.net.IEthernetNetworkManagementListener; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.ITetheredInterfaceCallback; import android.net.EthernetNetworkUpdateRequest; import android.net.IpConfiguration; @@ -243,7 +243,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { @Override public void updateConfiguration(@NonNull final String iface, @NonNull final EthernetNetworkUpdateRequest request, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { Objects.requireNonNull(iface); Objects.requireNonNull(request); throwIfEthernetNotStarted(); @@ -260,7 +260,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { @Override public void connectNetwork(@NonNull final String iface, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { Log.i(TAG, "connectNetwork called with: iface=" + iface + ", listener=" + listener); Objects.requireNonNull(iface); throwIfEthernetNotStarted(); @@ -272,7 +272,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { @Override public void disconnectNetwork(@NonNull final String iface, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { Log.i(TAG, "disconnectNetwork called with: iface=" + iface + ", listener=" + listener); Objects.requireNonNull(iface); throwIfEthernetNotStarted(); diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java index 074c81b..0a0e327 100644 --- a/java/com/android/server/ethernet/EthernetTracker.java +++ b/java/com/android/server/ethernet/EthernetTracker.java @@ -27,7 +27,7 @@ import android.content.res.Resources; import android.net.ConnectivityResources; import android.net.EthernetManager; import android.net.IEthernetServiceListener; -import android.net.IEthernetNetworkManagementListener; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.INetd; import android.net.ITetheredInterfaceCallback; import android.net.InterfaceConfigurationParcel; @@ -291,7 +291,7 @@ public class EthernetTracker { protected void updateConfiguration(@NonNull final String iface, @Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (DBG) { Log.i(TAG, "updateConfiguration, iface: " + iface + ", capabilities: " + capabilities + ", ipConfig: " + ipConfig); @@ -314,13 +314,13 @@ public class EthernetTracker { @VisibleForTesting(visibility = PACKAGE) protected void connectNetwork(@NonNull final String iface, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { mHandler.post(() -> updateInterfaceState(iface, true, listener)); } @VisibleForTesting(visibility = PACKAGE) protected void disconnectNetwork(@NonNull final String iface, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { mHandler.post(() -> updateInterfaceState(iface, false, listener)); } @@ -505,7 +505,7 @@ public class EthernetTracker { } private void updateInterfaceState(@NonNull final String iface, final boolean up, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { final int mode = getInterfaceMode(iface); final boolean factoryLinkStateUpdated = (mode == INTERFACE_MODE_CLIENT) && mFactory.updateInterfaceLinkState(iface, up, listener); diff --git a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java index 726833f..5d23aaf 100644 --- a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java +++ b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java @@ -41,8 +41,8 @@ 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.INetworkInterfaceOutcomeReceiver; import android.net.IpConfiguration; import android.net.LinkAddress; import android.net.LinkProperties; @@ -85,7 +85,7 @@ import java.util.concurrent.TimeUnit; 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 INetworkInterfaceOutcomeReceiver 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"; @@ -322,7 +322,7 @@ public class EthernetNetworkFactoryTest { assertTrue(ret); verify(mIpClient).shutdown(); - assertSuccessfulListener(listener, null); + assertEquals(listener.expectOnResult(), TEST_IFACE); } @Test @@ -336,7 +336,7 @@ public class EthernetNetworkFactoryTest { assertTrue(ret); verifyStop(); - assertSuccessfulListener(listener, mMockNetwork); + assertEquals(listener.expectOnResult(), TEST_IFACE); } @Test @@ -353,7 +353,7 @@ public class EthernetNetworkFactoryTest { verify(mDeps, never()).makeIpClient(any(), any(), any()); verify(mDeps, never()) .makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any()); - assertSuccessfulListener(listener, null); + assertEquals(listener.expectOnResult(), TEST_IFACE); } @Test @@ -367,7 +367,7 @@ public class EthernetNetworkFactoryTest { assertFalse(ret); verifyNoStopOrStart(); - assertFailedListener(listener, "can't be updated as it is not available"); + listener.expectOnErrorWithMessage("can't be updated as it is not available"); } @Test @@ -381,7 +381,7 @@ public class EthernetNetworkFactoryTest { assertFalse(ret); verifyNoStopOrStart(); - assertFailedListener(listener, "No changes"); + listener.expectOnErrorWithMessage("No changes"); } @Test @@ -638,18 +638,31 @@ public class EthernetNetworkFactoryTest { } private static final class TestNetworkManagementListener - implements IEthernetNetworkManagementListener { - private final CompletableFuture> mDone - = new CompletableFuture<>(); + implements INetworkInterfaceOutcomeReceiver { + private final CompletableFuture mResult = new CompletableFuture<>(); + private final CompletableFuture mError = + new CompletableFuture<>(); @Override - public void onComplete(final Network network, - final EthernetNetworkManagementException exception) { - mDone.complete(new Pair<>(network, exception)); + public void onResult(@NonNull String iface) { + mResult.complete(iface); } - Pair expectOnComplete() throws Exception { - return mDone.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + @Override + public void onError(@NonNull EthernetNetworkManagementException exception) { + mError.complete(exception); + } + + String expectOnResult() throws Exception { + return mResult.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + + EthernetNetworkManagementException expectOnError() throws Exception { + return mError.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + + void expectOnErrorWithMessage(String msg) throws Exception { + assertTrue(expectOnError().getMessage().contains(msg)); } @Override @@ -669,7 +682,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener); triggerOnProvisioningSuccess(); - assertSuccessfulListener(listener, mMockNetwork); + assertEquals(listener.expectOnResult(), TEST_IFACE); } @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available @@ -709,26 +722,7 @@ public class EthernetNetworkFactoryTest { triggerOnProvisioningSuccess(); }); - assertSuccessfulListener(successfulListener, mMockNetwork); - } - - private void assertSuccessfulListener( - @NonNull final TestNetworkManagementListener successfulListener, - @NonNull final Network expectedNetwork) throws Exception { - final Pair 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 failedResult = - failedListener.expectOnComplete(); - assertNull(failedResult.first); - assertNotNull(failedResult.second); - assertTrue(failedResult.second.getMessage().contains(errMsg)); + assertEquals(successfulListener.expectOnResult(), TEST_IFACE); } private void verifyNetworkManagementCallIsAbortedWhenInterrupted( @@ -743,7 +737,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(iface, ipConfiguration, capabilities, failedListener); interruptingRunnable.run(); - assertFailedListener(failedListener, "aborted"); + failedListener.expectOnErrorWithMessage("aborted"); } @Test @@ -757,7 +751,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener); triggerOnProvisioningSuccess(); - listener.expectOnComplete(); + assertEquals(listener.expectOnResult(), TEST_IFACE); verify(mDeps).makeEthernetNetworkAgent(any(), any(), eq(capabilities), any(), any(), any(), any()); verifyRestart(ipConfiguration); @@ -774,7 +768,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener); verifyNoStopOrStart(); - assertFailedListener(listener, "can't be updated as it is not available"); + listener.expectOnErrorWithMessage("can't be updated as it is not available"); } @Test diff --git a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java index 2131f7f..e67c4c8 100644 --- a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java +++ b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java @@ -32,7 +32,7 @@ import android.Manifest; import android.annotation.NonNull; import android.content.Context; import android.content.pm.PackageManager; -import android.net.IEthernetNetworkManagementListener; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.EthernetNetworkUpdateRequest; import android.net.IpConfiguration; import android.net.NetworkCapabilities; @@ -64,7 +64,7 @@ public class EthernetServiceImplTest { new EthernetNetworkUpdateRequest.Builder() .setNetworkCapabilities(new NetworkCapabilities.Builder().build()) .build(); - private static final IEthernetNetworkManagementListener NULL_LISTENER = null; + private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; private EthernetServiceImpl mEthernetServiceImpl; @Mock private Context mContext; @Mock private Handler mHandler; diff --git a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java index ef70d94..bab9643 100644 --- a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java +++ b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java @@ -33,7 +33,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; import android.net.InetAddresses; -import android.net.IEthernetNetworkManagementListener; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.INetd; import android.net.IpConfiguration; import android.net.IpConfiguration.IpAssignment; @@ -66,7 +66,7 @@ public class EthernetTrackerTest { private static final String TEST_IFACE = "test123"; private static final int TIMEOUT_MS = 1_000; private static final String THREAD_NAME = "EthernetServiceThread"; - private static final IEthernetNetworkManagementListener NULL_LISTENER = null; + private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; private EthernetTracker tracker; private HandlerThread mHandlerThread; @Mock private Context mContext; @@ -334,7 +334,7 @@ public class EthernetTrackerTest { new StaticIpConfiguration.Builder().setIpAddress(linkAddr).build(); final IpConfiguration ipConfig = new IpConfiguration.Builder().setStaticIpConfiguration(staticIpConfig).build(); - final IEthernetNetworkManagementListener listener = null; + final INetworkInterfaceOutcomeReceiver listener = null; tracker.updateConfiguration(TEST_IFACE, ipConfig, capabilities, listener); waitForIdle(); -- cgit v1.2.3 From 8a4c9c356459f55a854c55fc09ac9e0ec9c0bb58 Mon Sep 17 00:00:00 2001 From: markchien Date: Wed, 16 Mar 2022 15:59:33 +0800 Subject: Add global change ethernet state API Provide a new API to enable or disable ethernet. Also have a listener for call to check whether enable state is sucessful. Bug: 171872016 Test: atest EthernetServiceTests Change-Id: Iee4b48511ff668a2a7df90fd9bfe563d7ff23940 --- .../server/ethernet/EthernetNetworkFactory.java | 9 ++-- .../server/ethernet/EthernetServiceImpl.java | 8 +++ .../android/server/ethernet/EthernetTracker.java | 53 +++++++++++++++++++ .../server/ethernet/EthernetServiceImplTest.java | 30 +++++++++++ .../server/ethernet/EthernetTrackerTest.java | 60 ++++++++++++++++++++++ 5 files changed, 157 insertions(+), 3 deletions(-) diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java index 4f15355..342d507 100644 --- a/java/com/android/server/ethernet/EthernetNetworkFactory.java +++ b/java/com/android/server/ethernet/EthernetNetworkFactory.java @@ -183,7 +183,8 @@ public class EthernetNetworkFactory extends NetworkFactory { * Returns an array of available interface names. The array is sorted: unrestricted interfaces * goes first, then sorted by name. */ - String[] getAvailableInterfaces(boolean includeRestricted) { + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + protected String[] getAvailableInterfaces(boolean includeRestricted) { return mTrackingInterfaces.values() .stream() .filter(iface -> !iface.isRestricted() || includeRestricted) @@ -195,7 +196,8 @@ public class EthernetNetworkFactory extends NetworkFactory { .toArray(String[]::new); } - void addInterface(@NonNull final String ifaceName, @NonNull final String hwAddress, + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + protected void addInterface(@NonNull final String ifaceName, @NonNull final String hwAddress, @NonNull final IpConfiguration ipConfig, @NonNull final NetworkCapabilities capabilities) { if (mTrackingInterfaces.containsKey(ifaceName)) { @@ -282,7 +284,8 @@ public class EthernetNetworkFactory extends NetworkFactory { .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET).build(); } - void removeInterface(String interfaceName) { + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + protected void removeInterface(String interfaceName) { NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName); if (iface != null) { iface.maybeSendNetworkManagementCallbackForAbort(); diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java index edda321..afed01a 100644 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ b/java/com/android/server/ethernet/EthernetServiceImpl.java @@ -281,4 +281,12 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { mTracker.disconnectNetwork(iface, listener); } + + @Override + public void setEthernetEnabled(boolean enabled) { + PermissionUtils.enforceNetworkStackPermissionOr(mContext, + android.Manifest.permission.NETWORK_SETTINGS); + + mTracker.setEthernetEnabled(enabled); + } } diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java index 0a0e327..c38c900 100644 --- a/java/com/android/server/ethernet/EthernetTracker.java +++ b/java/com/android/server/ethernet/EthernetTracker.java @@ -16,6 +16,8 @@ package com.android.server.ethernet; +import static android.net.EthernetManager.ETHERNET_STATE_DISABLED; +import static android.net.EthernetManager.ETHERNET_STATE_ENABLED; import static android.net.TestNetworkManager.TEST_TAP_PREFIX; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; @@ -119,6 +121,8 @@ public class EthernetTracker { private boolean mTetheredInterfaceWasAvailable = false; private volatile IpConfiguration mIpConfigForDefaultInterface; + private int mEthernetState = ETHERNET_STATE_ENABLED; + private class TetheredInterfaceRequestList extends RemoteCallbackList { @Override @@ -355,6 +359,8 @@ public class EthernetTracker { for (String iface : getInterfaces(canUseRestrictedNetworks)) { unicastInterfaceStateChange(listener, iface); } + + unicastEthernetStateChange(listener, mEthernetState); }); } @@ -825,6 +831,53 @@ public class EthernetTracker { } } + @VisibleForTesting(visibility = PACKAGE) + protected void setEthernetEnabled(boolean enabled) { + mHandler.post(() -> { + int newState = enabled ? ETHERNET_STATE_ENABLED : ETHERNET_STATE_DISABLED; + if (mEthernetState == newState) return; + + mEthernetState = newState; + + if (enabled) { + trackAvailableInterfaces(); + } else { + // TODO: maybe also disable server mode interface as well. + untrackFactoryInterfaces(); + } + broadcastEthernetStateChange(mEthernetState); + }); + } + + private void untrackFactoryInterfaces() { + for (String iface : mFactory.getAvailableInterfaces(true /* includeRestricted */)) { + stopTrackingInterface(iface); + } + } + + private void unicastEthernetStateChange(@NonNull IEthernetServiceListener listener, + int state) { + ensureRunningOnEthernetServiceThread(); + try { + listener.onEthernetStateChanged(state); + } catch (RemoteException e) { + // Do nothing here. + } + } + + private void broadcastEthernetStateChange(int state) { + ensureRunningOnEthernetServiceThread(); + final int n = mListeners.beginBroadcast(); + for (int i = 0; i < n; i++) { + try { + mListeners.getBroadcastItem(i).onEthernetStateChanged(state); + } catch (RemoteException e) { + // Do nothing here. + } + } + mListeners.finishBroadcast(); + } + void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { postAndWaitForRunnable(() -> { pw.println(getClass().getSimpleName()); diff --git a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java index e67c4c8..dd1f1ed 100644 --- a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java +++ b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java @@ -19,12 +19,15 @@ package com.android.server.ethernet; import static android.net.NetworkCapabilities.TRANSPORT_TEST; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -339,4 +342,31 @@ public class EthernetServiceImplTest { mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); verify(mEthernetTracker).disconnectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); } + + private void denyPermissions(String... permissions) { + for (String permission: permissions) { + doReturn(PackageManager.PERMISSION_DENIED).when(mContext) + .checkCallingOrSelfPermission(eq(permission)); + } + } + + @Test + public void testSetEthernetEnabled() { + denyPermissions(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); + mEthernetServiceImpl.setEthernetEnabled(true); + verify(mEthernetTracker).setEthernetEnabled(true); + reset(mEthernetTracker); + + denyPermissions(Manifest.permission.NETWORK_STACK); + mEthernetServiceImpl.setEthernetEnabled(false); + verify(mEthernetTracker).setEthernetEnabled(false); + reset(mEthernetTracker); + + denyPermissions(Manifest.permission.NETWORK_SETTINGS); + try { + mEthernetServiceImpl.setEthernetEnabled(true); + fail("Should get SecurityException"); + } catch (SecurityException e) { } + verify(mEthernetTracker, never()).setEthernetEnabled(false); + } } diff --git a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java index bab9643..b1831c4 100644 --- a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java +++ b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java @@ -25,19 +25,26 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; +import android.net.EthernetManager; import android.net.InetAddresses; import android.net.INetworkInterfaceOutcomeReceiver; +import android.net.IEthernetServiceListener; import android.net.INetd; import android.net.IpConfiguration; import android.net.IpConfiguration.IpAssignment; import android.net.IpConfiguration.ProxySettings; +import android.net.InterfaceConfigurationParcel; import android.net.LinkAddress; import android.net.NetworkCapabilities; import android.net.StaticIpConfiguration; @@ -393,4 +400,57 @@ public class EthernetTrackerTest { assertTrue(isValidTestInterface); } + + public static class EthernetStateListener extends IEthernetServiceListener.Stub { + @Override + public void onEthernetStateChanged(int state) { } + + @Override + public void onInterfaceStateChanged(String iface, int state, int role, + IpConfiguration configuration) { } + } + + @Test + public void testListenEthernetStateChange() throws Exception { + final String testIface = "testtap123"; + final String testHwAddr = "11:22:33:44:55:66"; + final InterfaceConfigurationParcel ifaceParcel = new InterfaceConfigurationParcel(); + ifaceParcel.ifName = testIface; + ifaceParcel.hwAddr = testHwAddr; + ifaceParcel.flags = new String[] {INetd.IF_STATE_UP}; + + tracker.setIncludeTestInterfaces(true); + waitForIdle(); + + when(mNetd.interfaceGetList()).thenReturn(new String[] {testIface}); + when(mNetd.interfaceGetCfg(eq(testIface))).thenReturn(ifaceParcel); + doReturn(new String[] {testIface}).when(mFactory).getAvailableInterfaces(anyBoolean()); + doReturn(EthernetManager.STATE_LINK_UP).when(mFactory).getInterfaceState(eq(testIface)); + + final EthernetStateListener listener = spy(new EthernetStateListener()); + tracker.addListener(listener, true /* canUseRestrictedNetworks */); + // Check default state. + waitForIdle(); + verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_LINK_UP), + anyInt(), any()); + verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_ENABLED)); + reset(listener); + + doReturn(EthernetManager.STATE_ABSENT).when(mFactory).getInterfaceState(eq(testIface)); + tracker.setEthernetEnabled(false); + waitForIdle(); + verify(mFactory).removeInterface(eq(testIface)); + verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_DISABLED)); + verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_ABSENT), + anyInt(), any()); + reset(listener); + + doReturn(EthernetManager.STATE_LINK_UP).when(mFactory).getInterfaceState(eq(testIface)); + tracker.setEthernetEnabled(true); + waitForIdle(); + verify(mFactory).addInterface(eq(testIface), eq(testHwAddr), any(), any()); + verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_ENABLED)); + verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_LINK_UP), + anyInt(), any()); + } } -- cgit v1.2.3 From 613c01dacc967f3a65c33880d0fa70e0c334cea5 Mon Sep 17 00:00:00 2001 From: Xiao Ma Date: Fri, 18 Mar 2022 13:10:49 +0900 Subject: Add getInterfaceList API implementation in Ethernet service. Bug: 171872016 Test: m Change-Id: I8eeb2cd211c6a2ec6bc997c5e18995b585c6118a --- .../android/server/ethernet/EthernetServiceImpl.java | 7 +++++++ .../com/android/server/ethernet/EthernetTracker.java | 20 +++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java index afed01a..5e830ad 100644 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ b/java/com/android/server/ethernet/EthernetServiceImpl.java @@ -41,6 +41,7 @@ import com.android.net.module.util.PermissionUtils; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; @@ -289,4 +290,10 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { mTracker.setEthernetEnabled(enabled); } + + @Override + public List getInterfaceList() { + PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); + return mTracker.getInterfaceList(); + } } diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java index c38c900..abb1635 100644 --- a/java/com/android/server/ethernet/EthernetTracker.java +++ b/java/com/android/server/ethernet/EthernetTracker.java @@ -57,6 +57,7 @@ import com.android.net.module.util.PermissionUtils; import java.io.FileDescriptor; import java.net.InetAddress; import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; @@ -90,7 +91,8 @@ public class EthernetTracker { * Interface names we track. This is a product-dependent regular expression, plus, * if setIncludeTestInterfaces is true, any test interfaces. */ - private String mIfaceMatch; + private volatile String mIfaceMatch; + /** * Track test interfaces if true, don't track otherwise. */ @@ -341,6 +343,22 @@ public class EthernetTracker { return mFactory.getAvailableInterfaces(includeRestricted); } + List getInterfaceList() { + final List interfaceList = new ArrayList(); + final String[] ifaces; + try { + ifaces = mNetd.interfaceGetList(); + } catch (RemoteException e) { + Log.e(TAG, "Could not get list of interfaces " + e); + return interfaceList; + } + final String ifaceMatch = mIfaceMatch; + for (String iface : ifaces) { + if (iface.matches(ifaceMatch)) interfaceList.add(iface); + } + return interfaceList; + } + /** * Returns true if given interface was configured as restricted (doesn't have * NET_CAPABILITY_NOT_RESTRICTED) capability. Otherwise, returns false. -- cgit v1.2.3 From 5a608345df3ab1da8849d4ca26cf1a53d7b18d19 Mon Sep 17 00:00:00 2001 From: James Mattis Date: Tue, 15 Mar 2022 21:42:10 -0700 Subject: Fixing multithreading issues in Ethernet Factory IP client callbacks could be executed updating the state of an ethernet interface even if they were no longer the callbacks for the currently active interface. This can happen as IP client callbacks were being called from a thread separate from ethernet. Bug: 224890356 Test: atest EthernetServiceTests atest CtsNetTestCasesLatestSdk :android.net.cts.EthernetManagerTest --iterations 30 Change-Id: I238cb75caa01472bccc79db5bafa82bccdaeba52 --- .../server/ethernet/EthernetNetworkFactory.java | 50 +++++++++-------- .../ethernet/EthernetNetworkFactoryTest.java | 62 ++++++++++------------ 2 files changed, 51 insertions(+), 61 deletions(-) diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java index 4f15355..9c3148b 100644 --- a/java/com/android/server/ethernet/EthernetNetworkFactory.java +++ b/java/com/android/server/ethernet/EthernetNetworkFactory.java @@ -439,24 +439,44 @@ public class EthernetNetworkFactory extends NetworkFactory { mIpClientShutdownCv.block(); } + // At the time IpClient is stopped, an IpClient event may have already been posted on + // the back of the handler and is awaiting execution. Once that event is executed, the + // associated callback object may not be valid anymore + // (NetworkInterfaceState#mIpClientCallback points to a different object / null). + private boolean isCurrentCallback() { + return this == mIpClientCallback; + } + + private void handleIpEvent(final @NonNull Runnable r) { + mHandler.post(() -> { + if (!isCurrentCallback()) { + Log.i(TAG, "Ignoring stale IpClientCallbacks " + this); + return; + } + r.run(); + }); + } + @Override public void onProvisioningSuccess(LinkProperties newLp) { - mHandler.post(() -> onIpLayerStarted(newLp, mNetworkManagementListener)); + handleIpEvent(() -> onIpLayerStarted(newLp, mNetworkManagementListener)); } @Override public void onProvisioningFailure(LinkProperties newLp) { - mHandler.post(() -> onIpLayerStopped(mNetworkManagementListener)); + // This cannot happen due to provisioning timeout, because our timeout is 0. It can + // happen due to errors while provisioning or on provisioning loss. + handleIpEvent(() -> onIpLayerStopped(mNetworkManagementListener)); } @Override public void onLinkPropertiesChange(LinkProperties newLp) { - mHandler.post(() -> updateLinkProperties(newLp)); + handleIpEvent(() -> updateLinkProperties(newLp)); } @Override public void onReachabilityLost(String logMsg) { - mHandler.post(() -> updateNeighborLostEvent(logMsg)); + handleIpEvent(() -> updateNeighborLostEvent(logMsg)); } @Override @@ -558,14 +578,6 @@ public class EthernetNetworkFactory extends NetworkFactory { void onIpLayerStarted(@NonNull final LinkProperties linkProperties, @Nullable final INetworkInterfaceOutcomeReceiver 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; - } if (mNetworkAgent != null) { Log.e(TAG, "Already have a NetworkAgent - aborting new request"); stop(); @@ -601,12 +613,6 @@ public class EthernetNetworkFactory extends NetworkFactory { } void onIpLayerStopped(@Nullable final INetworkInterfaceOutcomeReceiver 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; - } // 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. @@ -648,10 +654,6 @@ public class EthernetNetworkFactory extends NetworkFactory { } void updateLinkProperties(LinkProperties linkProperties) { - if(mIpClient == null) { - if (DBG) Log.d(TAG, "IpClient is not initialized."); - return; - } mLinkProperties = linkProperties; if (mNetworkAgent != null) { mNetworkAgent.sendLinkPropertiesImpl(linkProperties); @@ -659,10 +661,6 @@ public class EthernetNetworkFactory extends NetworkFactory { } void updateNeighborLostEvent(String logMsg) { - if(mIpClient == null) { - if (DBG) Log.d(TAG, "IpClient is not initialized."); - return; - } Log.i(TAG, "updateNeighborLostEvent " + logMsg); // Reachability lost will be seen only if the gateway is not reachable. // Since ethernet FW doesn't have the mechanism to scan for new networks diff --git a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java index 2d5bd1d..4d3e4d3 100644 --- a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java +++ b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java @@ -21,6 +21,7 @@ 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.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -543,68 +544,59 @@ public class EthernetNetworkFactoryTest { verifyRestart(createDefaultIpConfig()); } + private IpClientCallbacks getStaleIpClientCallbacks() throws Exception { + createAndVerifyProvisionedInterface(TEST_IFACE); + final IpClientCallbacks staleIpClientCallbacks = mIpClientCallbacks; + mNetFactory.removeInterface(TEST_IFACE); + verifyStop(); + assertNotSame(mIpClientCallbacks, staleIpClientCallbacks); + return staleIpClientCallbacks; + } + @Test - public void testIgnoreOnIpLayerStartedCallbackAfterIpClientHasStopped() throws Exception { + public void testIgnoreOnIpLayerStartedCallbackForStaleCallback() throws Exception { initEthernetNetworkFactory(); - createAndVerifyProvisionedInterface(TEST_IFACE); - mIpClientCallbacks.onProvisioningFailure(new LinkProperties()); - mIpClientCallbacks.onProvisioningSuccess(new LinkProperties()); + final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); + + staleIpClientCallbacks.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 { + public void testIgnoreOnIpLayerStoppedCallbackForStaleCallback() throws Exception { initEthernetNetworkFactory(); - createAndVerifyProvisionedInterface(TEST_IFACE); when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams); - mIpClientCallbacks.onProvisioningFailure(new LinkProperties()); - mIpClientCallbacks.onProvisioningFailure(new LinkProperties()); + final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); + + staleIpClientCallbacks.onProvisioningFailure(new LinkProperties()); mLooper.dispatchAll(); - verifyStop(); - // ipClient has been shut down first, we should not retry - verify(mIpClient).startProvisioning(any()); + verify(mIpClient, never()).startProvisioning(any()); } @Test - public void testIgnoreLinkPropertiesCallbackAfterIpClientHasStopped() throws Exception { + public void testIgnoreLinkPropertiesCallbackForStaleCallback() throws Exception { initEthernetNetworkFactory(); - createAndVerifyProvisionedInterface(TEST_IFACE); - LinkProperties lp = new LinkProperties(); + final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); + final 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); + staleIpClientCallbacks.onLinkPropertiesChange(lp); mLooper.dispatchAll(); - verifyStop(); - // ipClient has been shut down first, we should not update - verify(mNetworkAgent, never()).sendLinkPropertiesImpl(same(lp)); + verify(mNetworkAgent, never()).sendLinkPropertiesImpl(eq(lp)); } @Test - public void testIgnoreNeighborLossCallbackAfterIpClientHasStopped() throws Exception { + public void testIgnoreNeighborLossCallbackForStaleCallback() throws Exception { initEthernetNetworkFactory(); - createAndVerifyProvisionedInterface(TEST_IFACE); + final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); - // 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"); + staleIpClientCallbacks.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(); } -- cgit v1.2.3 From ee9bfdd7adc4002dd5651f0129fc112db266e59d Mon Sep 17 00:00:00 2001 From: Patrick Rohr Date: Wed, 16 Mar 2022 10:49:18 +0100 Subject: Change network management listener to outcome receiver Adopts new API according to API review feedback. The outcome receiver can later be removed from most of the ethernet code; in a perfect world, a result can be generated and an exception captured directly from EthernetServiceImpl. This will greatly simplify the current implementation. CP note: the Merged-In ID is not the actual Change-Id of this change in tm-dev. This change was originally merged in aosp, but was subsequently reverted and cherry-picked into tm-dev. The cherry-pick falsely reused the Change-Id of the (now reverted) original change. This causes automerger to skip this change on all branches between aosp-master and tm-dev when the correct Merged-In ID is used. Therefore, the Merged-In ID listed is the Change-Id of an unrelated change that is present in tm-dev but not aosp-master. Bug: 220017952 Test: atest EthernetServiceTests Ignore-AOSP-First: CP to make automerger happy. Merged-In: Iee4b48511ff668a2a7df90fd9bfe563d7ff23940 Change-Id: Id8fadfed9fcfd22f04f6d7c3460b5956e571e01f --- .../server/ethernet/EthernetNetworkFactory.java | 46 +++++++------- .../server/ethernet/EthernetServiceImpl.java | 8 +-- .../android/server/ethernet/EthernetTracker.java | 10 +-- .../ethernet/EthernetNetworkFactoryTest.java | 72 ++++++++++------------ .../server/ethernet/EthernetServiceImplTest.java | 4 +- .../server/ethernet/EthernetTrackerTest.java | 6 +- 6 files changed, 71 insertions(+), 75 deletions(-) diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java index ef3abba..4f15355 100644 --- a/java/com/android/server/ethernet/EthernetNetworkFactory.java +++ b/java/com/android/server/ethernet/EthernetNetworkFactory.java @@ -24,8 +24,8 @@ import android.net.ConnectivityManager; import android.net.ConnectivityResources; import android.net.EthernetManager; import android.net.EthernetNetworkSpecifier; -import android.net.IEthernetNetworkManagementListener; import android.net.EthernetNetworkManagementException; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.IpConfiguration; import android.net.IpConfiguration.IpAssignment; import android.net.IpConfiguration.ProxySettings; @@ -239,14 +239,14 @@ public class EthernetNetworkFactory extends NetworkFactory { * {@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 IEthernetNetworkManagementListener} to notify callers of + * @param listener an optional {@link INetworkInterfaceOutcomeReceiver} to notify callers of * completion. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) protected void updateInterface(@NonNull final String ifaceName, @Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (!hasInterface(ifaceName)) { maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); return; @@ -295,7 +295,7 @@ public class EthernetNetworkFactory extends NetworkFactory { /** Returns true if state has been modified */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) protected boolean updateInterfaceLinkState(@NonNull final String ifaceName, final boolean up, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (!hasInterface(ifaceName)) { maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); return false; @@ -310,7 +310,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } private void maybeSendNetworkManagementCallbackForUntracked( - String ifaceName, IEthernetNetworkManagementListener listener) { + String ifaceName, INetworkInterfaceOutcomeReceiver listener) { maybeSendNetworkManagementCallback(listener, null, new EthernetNetworkManagementException( ifaceName + " can't be updated as it is not available.")); @@ -353,15 +353,19 @@ public class EthernetNetworkFactory extends NetworkFactory { } private static void maybeSendNetworkManagementCallback( - @Nullable final IEthernetNetworkManagementListener listener, - @Nullable final Network network, + @Nullable final INetworkInterfaceOutcomeReceiver listener, + @Nullable final String iface, @Nullable final EthernetNetworkManagementException e) { if (null == listener) { return; } try { - listener.onComplete(network, e); + if (iface != null) { + listener.onResult(iface); + } else { + listener.onError(e); + } } catch (RemoteException re) { Log.e(TAG, "Can't send onComplete for network management callback", re); } @@ -415,9 +419,9 @@ public class EthernetNetworkFactory extends NetworkFactory { private class EthernetIpClientCallback extends IpClientCallbacks { private final ConditionVariable mIpClientStartCv = new ConditionVariable(false); private final ConditionVariable mIpClientShutdownCv = new ConditionVariable(false); - @Nullable IEthernetNetworkManagementListener mNetworkManagementListener; + @Nullable INetworkInterfaceOutcomeReceiver mNetworkManagementListener; - EthernetIpClientCallback(@Nullable final IEthernetNetworkManagementListener listener) { + EthernetIpClientCallback(@Nullable final INetworkInterfaceOutcomeReceiver listener) { mNetworkManagementListener = listener; } @@ -502,7 +506,7 @@ public class EthernetNetworkFactory extends NetworkFactory { void updateInterface(@Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (DBG) { Log.d(TAG, "updateInterface, iface: " + name + ", ipConfig: " + ipConfig + ", old ipConfig: " + mIpConfig @@ -533,7 +537,7 @@ public class EthernetNetworkFactory extends NetworkFactory { start(null); } - private void start(@Nullable final IEthernetNetworkManagementListener listener) { + private void start(@Nullable final INetworkInterfaceOutcomeReceiver listener) { if (mIpClient != null) { if (DBG) Log.d(TAG, "IpClient already started"); return; @@ -553,7 +557,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } void onIpLayerStarted(@NonNull final LinkProperties linkProperties, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver 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 @@ -593,10 +597,10 @@ public class EthernetNetworkFactory extends NetworkFactory { }); mNetworkAgent.register(); mNetworkAgent.markConnected(); - realizeNetworkManagementCallback(mNetworkAgent.getNetwork(), null); + realizeNetworkManagementCallback(name, null); } - void onIpLayerStopped(@Nullable final IEthernetNetworkManagementListener listener) { + void onIpLayerStopped(@Nullable final INetworkInterfaceOutcomeReceiver 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) { @@ -622,7 +626,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } // Must be called on the handler thread - private void realizeNetworkManagementCallback(@Nullable final Network network, + private void realizeNetworkManagementCallback(@Nullable final String iface, @Nullable final EthernetNetworkManagementException e) { ensureRunningOnEthernetHandlerThread(); if (null == mIpClientCallback) { @@ -630,7 +634,7 @@ public class EthernetNetworkFactory extends NetworkFactory { } EthernetNetworkFactory.maybeSendNetworkManagementCallback( - mIpClientCallback.mNetworkManagementListener, network, e); + mIpClientCallback.mNetworkManagementListener, iface, e); // Only send a single callback per listener. mIpClientCallback.mNetworkManagementListener = null; } @@ -671,7 +675,7 @@ public class EthernetNetworkFactory extends NetworkFactory { /** Returns true if state has been modified */ boolean updateLinkState(final boolean up, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (mLinkUp == up) { EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, null, new EthernetNetworkManagementException( @@ -681,13 +685,11 @@ public class EthernetNetworkFactory extends NetworkFactory { mLinkUp = up; if (!up) { // was up, goes down - // Save an instance of the current network to use with the callback before stop(). - final Network network = mNetworkAgent != null ? mNetworkAgent.getNetwork() : null; // Send an abort on a provisioning request callback if necessary before stopping. maybeSendNetworkManagementCallbackForAbort(); stop(); // If only setting the interface down, send a callback to signal completion. - EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, network, null); + EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, name, null); } else { // was down, goes up stop(); start(listener); @@ -742,7 +744,7 @@ public class EthernetNetworkFactory extends NetworkFactory { restart(null); } - void restart(@Nullable final IEthernetNetworkManagementListener listener){ + void restart(@Nullable final INetworkInterfaceOutcomeReceiver listener) { if (DBG) Log.d(TAG, "reconnecting Ethernet"); stop(); start(listener); diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java index 50b4684..edda321 100644 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ b/java/com/android/server/ethernet/EthernetServiceImpl.java @@ -24,7 +24,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.net.IEthernetManager; import android.net.IEthernetServiceListener; -import android.net.IEthernetNetworkManagementListener; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.ITetheredInterfaceCallback; import android.net.EthernetNetworkUpdateRequest; import android.net.IpConfiguration; @@ -243,7 +243,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { @Override public void updateConfiguration(@NonNull final String iface, @NonNull final EthernetNetworkUpdateRequest request, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { Objects.requireNonNull(iface); Objects.requireNonNull(request); throwIfEthernetNotStarted(); @@ -260,7 +260,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { @Override public void connectNetwork(@NonNull final String iface, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { Log.i(TAG, "connectNetwork called with: iface=" + iface + ", listener=" + listener); Objects.requireNonNull(iface); throwIfEthernetNotStarted(); @@ -272,7 +272,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { @Override public void disconnectNetwork(@NonNull final String iface, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { Log.i(TAG, "disconnectNetwork called with: iface=" + iface + ", listener=" + listener); Objects.requireNonNull(iface); throwIfEthernetNotStarted(); diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java index 074c81b..0a0e327 100644 --- a/java/com/android/server/ethernet/EthernetTracker.java +++ b/java/com/android/server/ethernet/EthernetTracker.java @@ -27,7 +27,7 @@ import android.content.res.Resources; import android.net.ConnectivityResources; import android.net.EthernetManager; import android.net.IEthernetServiceListener; -import android.net.IEthernetNetworkManagementListener; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.INetd; import android.net.ITetheredInterfaceCallback; import android.net.InterfaceConfigurationParcel; @@ -291,7 +291,7 @@ public class EthernetTracker { protected void updateConfiguration(@NonNull final String iface, @Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { if (DBG) { Log.i(TAG, "updateConfiguration, iface: " + iface + ", capabilities: " + capabilities + ", ipConfig: " + ipConfig); @@ -314,13 +314,13 @@ public class EthernetTracker { @VisibleForTesting(visibility = PACKAGE) protected void connectNetwork(@NonNull final String iface, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { mHandler.post(() -> updateInterfaceState(iface, true, listener)); } @VisibleForTesting(visibility = PACKAGE) protected void disconnectNetwork(@NonNull final String iface, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { mHandler.post(() -> updateInterfaceState(iface, false, listener)); } @@ -505,7 +505,7 @@ public class EthernetTracker { } private void updateInterfaceState(@NonNull final String iface, final boolean up, - @Nullable final IEthernetNetworkManagementListener listener) { + @Nullable final INetworkInterfaceOutcomeReceiver listener) { final int mode = getInterfaceMode(iface); final boolean factoryLinkStateUpdated = (mode == INTERFACE_MODE_CLIENT) && mFactory.updateInterfaceLinkState(iface, up, listener); diff --git a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java index e256add..2d5bd1d 100644 --- a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java +++ b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java @@ -41,8 +41,8 @@ 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.INetworkInterfaceOutcomeReceiver; import android.net.IpConfiguration; import android.net.LinkAddress; import android.net.LinkProperties; @@ -85,7 +85,7 @@ import java.util.concurrent.TimeUnit; 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 INetworkInterfaceOutcomeReceiver 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"; @@ -316,7 +316,7 @@ public class EthernetNetworkFactoryTest { assertTrue(ret); verify(mIpClient).shutdown(); - assertSuccessfulListener(listener, null); + assertEquals(listener.expectOnResult(), TEST_IFACE); } @Test @@ -330,7 +330,7 @@ public class EthernetNetworkFactoryTest { assertTrue(ret); verifyStop(); - assertSuccessfulListener(listener, mMockNetwork); + assertEquals(listener.expectOnResult(), TEST_IFACE); } @Test @@ -347,7 +347,7 @@ public class EthernetNetworkFactoryTest { verify(mDeps, never()).makeIpClient(any(), any(), any()); verify(mDeps, never()) .makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any()); - assertSuccessfulListener(listener, null); + assertEquals(listener.expectOnResult(), TEST_IFACE); } @Test @@ -361,7 +361,7 @@ public class EthernetNetworkFactoryTest { assertFalse(ret); verifyNoStopOrStart(); - assertFailedListener(listener, "can't be updated as it is not available"); + listener.expectOnErrorWithMessage("can't be updated as it is not available"); } @Test @@ -375,7 +375,7 @@ public class EthernetNetworkFactoryTest { assertFalse(ret); verifyNoStopOrStart(); - assertFailedListener(listener, "No changes"); + listener.expectOnErrorWithMessage("No changes"); } @Test @@ -632,18 +632,31 @@ public class EthernetNetworkFactoryTest { } private static final class TestNetworkManagementListener - implements IEthernetNetworkManagementListener { - private final CompletableFuture> mDone - = new CompletableFuture<>(); + implements INetworkInterfaceOutcomeReceiver { + private final CompletableFuture mResult = new CompletableFuture<>(); + private final CompletableFuture mError = + new CompletableFuture<>(); @Override - public void onComplete(final Network network, - final EthernetNetworkManagementException exception) { - mDone.complete(new Pair<>(network, exception)); + public void onResult(@NonNull String iface) { + mResult.complete(iface); } - Pair expectOnComplete() throws Exception { - return mDone.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + @Override + public void onError(@NonNull EthernetNetworkManagementException exception) { + mError.complete(exception); + } + + String expectOnResult() throws Exception { + return mResult.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + + EthernetNetworkManagementException expectOnError() throws Exception { + return mError.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + + void expectOnErrorWithMessage(String msg) throws Exception { + assertTrue(expectOnError().getMessage().contains(msg)); } @Override @@ -663,7 +676,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener); triggerOnProvisioningSuccess(); - assertSuccessfulListener(listener, mMockNetwork); + assertEquals(listener.expectOnResult(), TEST_IFACE); } @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available @@ -703,26 +716,7 @@ public class EthernetNetworkFactoryTest { triggerOnProvisioningSuccess(); }); - assertSuccessfulListener(successfulListener, mMockNetwork); - } - - private void assertSuccessfulListener( - @NonNull final TestNetworkManagementListener successfulListener, - @NonNull final Network expectedNetwork) throws Exception { - final Pair 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 failedResult = - failedListener.expectOnComplete(); - assertNull(failedResult.first); - assertNotNull(failedResult.second); - assertTrue(failedResult.second.getMessage().contains(errMsg)); + assertEquals(successfulListener.expectOnResult(), TEST_IFACE); } private void verifyNetworkManagementCallIsAbortedWhenInterrupted( @@ -737,7 +731,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(iface, ipConfiguration, capabilities, failedListener); interruptingRunnable.run(); - assertFailedListener(failedListener, "aborted"); + failedListener.expectOnErrorWithMessage("aborted"); } @Test @@ -751,7 +745,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener); triggerOnProvisioningSuccess(); - listener.expectOnComplete(); + assertEquals(listener.expectOnResult(), TEST_IFACE); verify(mDeps).makeEthernetNetworkAgent(any(), any(), eq(capabilities), any(), any(), any(), any()); verifyRestart(ipConfiguration); @@ -768,7 +762,7 @@ public class EthernetNetworkFactoryTest { mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener); verifyNoStopOrStart(); - assertFailedListener(listener, "can't be updated as it is not available"); + listener.expectOnErrorWithMessage("can't be updated as it is not available"); } @Test diff --git a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java index 2131f7f..e67c4c8 100644 --- a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java +++ b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java @@ -32,7 +32,7 @@ import android.Manifest; import android.annotation.NonNull; import android.content.Context; import android.content.pm.PackageManager; -import android.net.IEthernetNetworkManagementListener; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.EthernetNetworkUpdateRequest; import android.net.IpConfiguration; import android.net.NetworkCapabilities; @@ -64,7 +64,7 @@ public class EthernetServiceImplTest { new EthernetNetworkUpdateRequest.Builder() .setNetworkCapabilities(new NetworkCapabilities.Builder().build()) .build(); - private static final IEthernetNetworkManagementListener NULL_LISTENER = null; + private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; private EthernetServiceImpl mEthernetServiceImpl; @Mock private Context mContext; @Mock private Handler mHandler; diff --git a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java index ef70d94..bab9643 100644 --- a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java +++ b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java @@ -33,7 +33,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; import android.net.InetAddresses; -import android.net.IEthernetNetworkManagementListener; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.INetd; import android.net.IpConfiguration; import android.net.IpConfiguration.IpAssignment; @@ -66,7 +66,7 @@ public class EthernetTrackerTest { private static final String TEST_IFACE = "test123"; private static final int TIMEOUT_MS = 1_000; private static final String THREAD_NAME = "EthernetServiceThread"; - private static final IEthernetNetworkManagementListener NULL_LISTENER = null; + private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; private EthernetTracker tracker; private HandlerThread mHandlerThread; @Mock private Context mContext; @@ -334,7 +334,7 @@ public class EthernetTrackerTest { new StaticIpConfiguration.Builder().setIpAddress(linkAddr).build(); final IpConfiguration ipConfig = new IpConfiguration.Builder().setStaticIpConfiguration(staticIpConfig).build(); - final IEthernetNetworkManagementListener listener = null; + final INetworkInterfaceOutcomeReceiver listener = null; tracker.updateConfiguration(TEST_IFACE, ipConfig, capabilities, listener); waitForIdle(); -- cgit v1.2.3 From 61566c7d1a8b511e58423b85c6fc1f1b454caaba Mon Sep 17 00:00:00 2001 From: markchien Date: Wed, 16 Mar 2022 15:59:33 +0800 Subject: Add global change ethernet state API Provide a new API to enable or disable ethernet. Also have a listener for call to check whether enable state is sucessful. Bug: 171872016 Test: atest EthernetServiceTests Change-Id: Iee4b48511ff668a2a7df90fd9bfe563d7ff23940 Merged-In: Iee4b48511ff668a2a7df90fd9bfe563d7ff23940 --- .../server/ethernet/EthernetNetworkFactory.java | 9 ++-- .../server/ethernet/EthernetServiceImpl.java | 8 +++ .../android/server/ethernet/EthernetTracker.java | 53 +++++++++++++++++++ .../server/ethernet/EthernetServiceImplTest.java | 30 +++++++++++ .../server/ethernet/EthernetTrackerTest.java | 60 ++++++++++++++++++++++ 5 files changed, 157 insertions(+), 3 deletions(-) diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java index 4f15355..342d507 100644 --- a/java/com/android/server/ethernet/EthernetNetworkFactory.java +++ b/java/com/android/server/ethernet/EthernetNetworkFactory.java @@ -183,7 +183,8 @@ public class EthernetNetworkFactory extends NetworkFactory { * Returns an array of available interface names. The array is sorted: unrestricted interfaces * goes first, then sorted by name. */ - String[] getAvailableInterfaces(boolean includeRestricted) { + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + protected String[] getAvailableInterfaces(boolean includeRestricted) { return mTrackingInterfaces.values() .stream() .filter(iface -> !iface.isRestricted() || includeRestricted) @@ -195,7 +196,8 @@ public class EthernetNetworkFactory extends NetworkFactory { .toArray(String[]::new); } - void addInterface(@NonNull final String ifaceName, @NonNull final String hwAddress, + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + protected void addInterface(@NonNull final String ifaceName, @NonNull final String hwAddress, @NonNull final IpConfiguration ipConfig, @NonNull final NetworkCapabilities capabilities) { if (mTrackingInterfaces.containsKey(ifaceName)) { @@ -282,7 +284,8 @@ public class EthernetNetworkFactory extends NetworkFactory { .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET).build(); } - void removeInterface(String interfaceName) { + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + protected void removeInterface(String interfaceName) { NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName); if (iface != null) { iface.maybeSendNetworkManagementCallbackForAbort(); diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java index edda321..afed01a 100644 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ b/java/com/android/server/ethernet/EthernetServiceImpl.java @@ -281,4 +281,12 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { mTracker.disconnectNetwork(iface, listener); } + + @Override + public void setEthernetEnabled(boolean enabled) { + PermissionUtils.enforceNetworkStackPermissionOr(mContext, + android.Manifest.permission.NETWORK_SETTINGS); + + mTracker.setEthernetEnabled(enabled); + } } diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java index 0a0e327..c38c900 100644 --- a/java/com/android/server/ethernet/EthernetTracker.java +++ b/java/com/android/server/ethernet/EthernetTracker.java @@ -16,6 +16,8 @@ package com.android.server.ethernet; +import static android.net.EthernetManager.ETHERNET_STATE_DISABLED; +import static android.net.EthernetManager.ETHERNET_STATE_ENABLED; import static android.net.TestNetworkManager.TEST_TAP_PREFIX; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; @@ -119,6 +121,8 @@ public class EthernetTracker { private boolean mTetheredInterfaceWasAvailable = false; private volatile IpConfiguration mIpConfigForDefaultInterface; + private int mEthernetState = ETHERNET_STATE_ENABLED; + private class TetheredInterfaceRequestList extends RemoteCallbackList { @Override @@ -355,6 +359,8 @@ public class EthernetTracker { for (String iface : getInterfaces(canUseRestrictedNetworks)) { unicastInterfaceStateChange(listener, iface); } + + unicastEthernetStateChange(listener, mEthernetState); }); } @@ -825,6 +831,53 @@ public class EthernetTracker { } } + @VisibleForTesting(visibility = PACKAGE) + protected void setEthernetEnabled(boolean enabled) { + mHandler.post(() -> { + int newState = enabled ? ETHERNET_STATE_ENABLED : ETHERNET_STATE_DISABLED; + if (mEthernetState == newState) return; + + mEthernetState = newState; + + if (enabled) { + trackAvailableInterfaces(); + } else { + // TODO: maybe also disable server mode interface as well. + untrackFactoryInterfaces(); + } + broadcastEthernetStateChange(mEthernetState); + }); + } + + private void untrackFactoryInterfaces() { + for (String iface : mFactory.getAvailableInterfaces(true /* includeRestricted */)) { + stopTrackingInterface(iface); + } + } + + private void unicastEthernetStateChange(@NonNull IEthernetServiceListener listener, + int state) { + ensureRunningOnEthernetServiceThread(); + try { + listener.onEthernetStateChanged(state); + } catch (RemoteException e) { + // Do nothing here. + } + } + + private void broadcastEthernetStateChange(int state) { + ensureRunningOnEthernetServiceThread(); + final int n = mListeners.beginBroadcast(); + for (int i = 0; i < n; i++) { + try { + mListeners.getBroadcastItem(i).onEthernetStateChanged(state); + } catch (RemoteException e) { + // Do nothing here. + } + } + mListeners.finishBroadcast(); + } + void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { postAndWaitForRunnable(() -> { pw.println(getClass().getSimpleName()); diff --git a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java index e67c4c8..dd1f1ed 100644 --- a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java +++ b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java @@ -19,12 +19,15 @@ package com.android.server.ethernet; import static android.net.NetworkCapabilities.TRANSPORT_TEST; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -339,4 +342,31 @@ public class EthernetServiceImplTest { mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); verify(mEthernetTracker).disconnectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); } + + private void denyPermissions(String... permissions) { + for (String permission: permissions) { + doReturn(PackageManager.PERMISSION_DENIED).when(mContext) + .checkCallingOrSelfPermission(eq(permission)); + } + } + + @Test + public void testSetEthernetEnabled() { + denyPermissions(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); + mEthernetServiceImpl.setEthernetEnabled(true); + verify(mEthernetTracker).setEthernetEnabled(true); + reset(mEthernetTracker); + + denyPermissions(Manifest.permission.NETWORK_STACK); + mEthernetServiceImpl.setEthernetEnabled(false); + verify(mEthernetTracker).setEthernetEnabled(false); + reset(mEthernetTracker); + + denyPermissions(Manifest.permission.NETWORK_SETTINGS); + try { + mEthernetServiceImpl.setEthernetEnabled(true); + fail("Should get SecurityException"); + } catch (SecurityException e) { } + verify(mEthernetTracker, never()).setEthernetEnabled(false); + } } diff --git a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java index bab9643..b1831c4 100644 --- a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java +++ b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java @@ -25,19 +25,26 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; +import android.net.EthernetManager; import android.net.InetAddresses; import android.net.INetworkInterfaceOutcomeReceiver; +import android.net.IEthernetServiceListener; import android.net.INetd; import android.net.IpConfiguration; import android.net.IpConfiguration.IpAssignment; import android.net.IpConfiguration.ProxySettings; +import android.net.InterfaceConfigurationParcel; import android.net.LinkAddress; import android.net.NetworkCapabilities; import android.net.StaticIpConfiguration; @@ -393,4 +400,57 @@ public class EthernetTrackerTest { assertTrue(isValidTestInterface); } + + public static class EthernetStateListener extends IEthernetServiceListener.Stub { + @Override + public void onEthernetStateChanged(int state) { } + + @Override + public void onInterfaceStateChanged(String iface, int state, int role, + IpConfiguration configuration) { } + } + + @Test + public void testListenEthernetStateChange() throws Exception { + final String testIface = "testtap123"; + final String testHwAddr = "11:22:33:44:55:66"; + final InterfaceConfigurationParcel ifaceParcel = new InterfaceConfigurationParcel(); + ifaceParcel.ifName = testIface; + ifaceParcel.hwAddr = testHwAddr; + ifaceParcel.flags = new String[] {INetd.IF_STATE_UP}; + + tracker.setIncludeTestInterfaces(true); + waitForIdle(); + + when(mNetd.interfaceGetList()).thenReturn(new String[] {testIface}); + when(mNetd.interfaceGetCfg(eq(testIface))).thenReturn(ifaceParcel); + doReturn(new String[] {testIface}).when(mFactory).getAvailableInterfaces(anyBoolean()); + doReturn(EthernetManager.STATE_LINK_UP).when(mFactory).getInterfaceState(eq(testIface)); + + final EthernetStateListener listener = spy(new EthernetStateListener()); + tracker.addListener(listener, true /* canUseRestrictedNetworks */); + // Check default state. + waitForIdle(); + verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_LINK_UP), + anyInt(), any()); + verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_ENABLED)); + reset(listener); + + doReturn(EthernetManager.STATE_ABSENT).when(mFactory).getInterfaceState(eq(testIface)); + tracker.setEthernetEnabled(false); + waitForIdle(); + verify(mFactory).removeInterface(eq(testIface)); + verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_DISABLED)); + verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_ABSENT), + anyInt(), any()); + reset(listener); + + doReturn(EthernetManager.STATE_LINK_UP).when(mFactory).getInterfaceState(eq(testIface)); + tracker.setEthernetEnabled(true); + waitForIdle(); + verify(mFactory).addInterface(eq(testIface), eq(testHwAddr), any(), any()); + verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_ENABLED)); + verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_LINK_UP), + anyInt(), any()); + } } -- cgit v1.2.3 From 260521db426c2d70cf713c122be085919be59df2 Mon Sep 17 00:00:00 2001 From: Xiao Ma Date: Thu, 17 Mar 2022 05:57:25 +0000 Subject: Add getInterfaceList API implementation in Ethernet service. Bug: 171872016 Test: m Merged-In: I8eeb2cd211c6a2ec6bc997c5e18995b585c6118a Change-Id: Ic36d26be7dae5fd3f72abce3cea1ee845813a6e5 --- .../android/server/ethernet/EthernetServiceImpl.java | 7 +++++++ java/com/android/server/ethernet/EthernetTracker.java | 19 ++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java index afed01a..5e830ad 100644 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ b/java/com/android/server/ethernet/EthernetServiceImpl.java @@ -41,6 +41,7 @@ import com.android.net.module.util.PermissionUtils; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; @@ -289,4 +290,10 @@ public class EthernetServiceImpl extends IEthernetManager.Stub { mTracker.setEthernetEnabled(enabled); } + + @Override + public List getInterfaceList() { + PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); + return mTracker.getInterfaceList(); + } } diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java index c38c900..c291b3f 100644 --- a/java/com/android/server/ethernet/EthernetTracker.java +++ b/java/com/android/server/ethernet/EthernetTracker.java @@ -57,6 +57,7 @@ import com.android.net.module.util.PermissionUtils; import java.io.FileDescriptor; import java.net.InetAddress; import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; @@ -90,7 +91,7 @@ public class EthernetTracker { * Interface names we track. This is a product-dependent regular expression, plus, * if setIncludeTestInterfaces is true, any test interfaces. */ - private String mIfaceMatch; + private volatile String mIfaceMatch; /** * Track test interfaces if true, don't track otherwise. */ @@ -341,6 +342,22 @@ public class EthernetTracker { return mFactory.getAvailableInterfaces(includeRestricted); } + List getInterfaceList() { + final List interfaceList = new ArrayList(); + final String[] ifaces; + try { + ifaces = mNetd.interfaceGetList(); + } catch (RemoteException e) { + Log.e(TAG, "Could not get list of interfaces " + e); + return interfaceList; + } + final String ifaceMatch = mIfaceMatch; + for (String iface : ifaces) { + if (iface.matches(ifaceMatch)) interfaceList.add(iface); + } + return interfaceList; + } + /** * Returns true if given interface was configured as restricted (doesn't have * NET_CAPABILITY_NOT_RESTRICTED) capability. Otherwise, returns false. -- cgit v1.2.3 From e352d78b8edf4e5eb33deaf30aac5b0130316595 Mon Sep 17 00:00:00 2001 From: Patrick Rohr Date: Wed, 30 Mar 2022 15:15:34 +0200 Subject: Move f/o/n/ethernet to p/m/Connectivity Test: TH Bug: 222234190 Merged-In: I2a9b6d3141a6ff197093a607d9eeb0e7831f20ba Change-Id: Iea43a76d84002a5a587fa735b692d8b6ce6fd12e --- Android.bp | 32 - TEST_MAPPING | 12 - .../server/ethernet/EthernetConfigStore.java | 88 -- .../server/ethernet/EthernetNetworkAgent.java | 65 -- .../server/ethernet/EthernetNetworkFactory.java | 785 ----------------- .../android/server/ethernet/EthernetService.java | 47 - .../server/ethernet/EthernetServiceImpl.java | 299 ------- .../android/server/ethernet/EthernetTracker.java | 943 --------------------- tests/Android.bp | 53 -- tests/AndroidManifest.xml | 28 - .../ethernet/EthernetNetworkFactoryTest.java | 783 ----------------- .../server/ethernet/EthernetServiceImplTest.java | 372 -------- .../server/ethernet/EthernetTrackerTest.java | 456 ---------- 13 files changed, 3963 deletions(-) delete mode 100644 Android.bp delete mode 100644 TEST_MAPPING delete mode 100644 java/com/android/server/ethernet/EthernetConfigStore.java delete mode 100644 java/com/android/server/ethernet/EthernetNetworkAgent.java delete mode 100644 java/com/android/server/ethernet/EthernetNetworkFactory.java delete mode 100644 java/com/android/server/ethernet/EthernetService.java delete mode 100644 java/com/android/server/ethernet/EthernetServiceImpl.java delete mode 100644 java/com/android/server/ethernet/EthernetTracker.java delete mode 100644 tests/Android.bp delete mode 100644 tests/AndroidManifest.xml delete mode 100644 tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java delete mode 100644 tests/java/com/android/server/ethernet/EthernetServiceImplTest.java delete mode 100644 tests/java/com/android/server/ethernet/EthernetTrackerTest.java diff --git a/Android.bp b/Android.bp deleted file mode 100644 index c429a6f..0000000 --- a/Android.bp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2014 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. - -// Build the java code -// ============================================================ - -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -filegroup { - name: "ethernet-service-updatable-sources", - srcs: [ - "java/**/*.java", - ], - path: "java", - visibility: [ - "//frameworks/opt/net/ethernet/tests", - "//packages/modules/Connectivity/service-t", - ], -} diff --git a/TEST_MAPPING b/TEST_MAPPING deleted file mode 100644 index 24c152b..0000000 --- a/TEST_MAPPING +++ /dev/null @@ -1,12 +0,0 @@ -{ - "presubmit": [ - { - "name": "EthernetServiceTests" - } - ], - "imports": [ - { - "path": "packages/modules/Connectivity" - } - ] -} diff --git a/java/com/android/server/ethernet/EthernetConfigStore.java b/java/com/android/server/ethernet/EthernetConfigStore.java deleted file mode 100644 index 6b623f4..0000000 --- a/java/com/android/server/ethernet/EthernetConfigStore.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2014 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 android.annotation.Nullable; -import android.net.IpConfiguration; -import android.os.Environment; -import android.util.ArrayMap; - -import com.android.server.net.IpConfigStore; - - -/** - * This class provides an API to store and manage Ethernet network configuration. - */ -public class EthernetConfigStore { - private static final String ipConfigFile = Environment.getDataDirectory() + - "/misc/ethernet/ipconfig.txt"; - - private IpConfigStore mStore = new IpConfigStore(); - private ArrayMap mIpConfigurations; - private IpConfiguration mIpConfigurationForDefaultInterface; - private final Object mSync = new Object(); - - public EthernetConfigStore() { - mIpConfigurations = new ArrayMap<>(0); - } - - public void read() { - synchronized (mSync) { - ArrayMap configs = - IpConfigStore.readIpConfigurations(ipConfigFile); - - // This configuration may exist in old file versions when there was only a single active - // Ethernet interface. - if (configs.containsKey("0")) { - mIpConfigurationForDefaultInterface = configs.remove("0"); - } - - mIpConfigurations = configs; - } - } - - public void write(String iface, IpConfiguration config) { - boolean modified; - - synchronized (mSync) { - if (config == null) { - modified = mIpConfigurations.remove(iface) != null; - } else { - IpConfiguration oldConfig = mIpConfigurations.put(iface, config); - modified = !config.equals(oldConfig); - } - - if (modified) { - mStore.writeIpConfigurations(ipConfigFile, mIpConfigurations); - } - } - } - - public ArrayMap getIpConfigurations() { - synchronized (mSync) { - return new ArrayMap<>(mIpConfigurations); - } - } - - @Nullable - public IpConfiguration getIpConfigurationForDefaultInterface() { - synchronized (mSync) { - return mIpConfigurationForDefaultInterface == null - ? null : new IpConfiguration(mIpConfigurationForDefaultInterface); - } - } -} diff --git a/java/com/android/server/ethernet/EthernetNetworkAgent.java b/java/com/android/server/ethernet/EthernetNetworkAgent.java deleted file mode 100644 index 57fbce7..0000000 --- a/java/com/android/server/ethernet/EthernetNetworkAgent.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2021 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 android.content.Context; -import android.net.LinkProperties; -import android.net.NetworkAgent; -import android.net.NetworkAgentConfig; -import android.net.NetworkCapabilities; -import android.net.NetworkProvider; -import android.net.NetworkScore; -import android.os.Looper; -import android.annotation.NonNull; -import android.annotation.Nullable; - -public class EthernetNetworkAgent extends NetworkAgent { - - private static final String TAG = "EthernetNetworkAgent"; - - public interface Callbacks { - void onNetworkUnwanted(); - } - - private final Callbacks mCallbacks; - - EthernetNetworkAgent( - @NonNull Context context, - @NonNull Looper looper, - @NonNull NetworkCapabilities nc, - @NonNull LinkProperties lp, - @NonNull NetworkAgentConfig config, - @Nullable NetworkProvider provider, - @NonNull Callbacks cb) { - super(context, looper, TAG, nc, lp, new NetworkScore.Builder().build(), config, provider); - mCallbacks = cb; - } - - @Override - public void onNetworkUnwanted() { - mCallbacks.onNetworkUnwanted(); - } - - // sendLinkProperties is final in NetworkAgent, so it cannot be mocked. - public void sendLinkPropertiesImpl(LinkProperties lp) { - sendLinkProperties(lp); - } - - public Callbacks getCallbacks() { - return mCallbacks; - } -} diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java deleted file mode 100644 index d910629..0000000 --- a/java/com/android/server/ethernet/EthernetNetworkFactory.java +++ /dev/null @@ -1,785 +0,0 @@ -/* - * Copyright (C) 2014 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 android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.content.res.Resources; -import android.net.ConnectivityManager; -import android.net.ConnectivityResources; -import android.net.EthernetManager; -import android.net.EthernetNetworkSpecifier; -import android.net.EthernetNetworkManagementException; -import android.net.INetworkInterfaceOutcomeReceiver; -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; -import android.net.NetworkProvider; -import android.net.NetworkRequest; -import android.net.NetworkSpecifier; -import android.net.ip.IIpClient; -import android.net.ip.IpClientCallbacks; -import android.net.ip.IpClientManager; -import android.net.ip.IpClientUtil; -import android.net.shared.ProvisioningConfiguration; -import android.os.ConditionVariable; -import android.os.Handler; -import android.os.Looper; -import android.os.RemoteException; -import android.text.TextUtils; -import android.util.AndroidRuntimeException; -import android.util.Log; -import android.util.SparseArray; - -import com.android.connectivity.resources.R; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; -import com.android.net.module.util.InterfaceParams; - -import java.io.FileDescriptor; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; - -/** - * {@link NetworkFactory} that represents Ethernet networks. - * - * This class reports a static network score of 70 when it is tracking an interface and that - * interface's link is up, and a score of 0 otherwise. - */ -public class EthernetNetworkFactory extends NetworkFactory { - private final static String TAG = EthernetNetworkFactory.class.getSimpleName(); - final static boolean DBG = true; - - private final static int NETWORK_SCORE = 70; - private static final String NETWORK_TYPE = "Ethernet"; - private static final String LEGACY_TCP_BUFFER_SIZES = - "524288,1048576,3145728,524288,1048576,2097152"; - - private final ConcurrentHashMap mTrackingInterfaces = - new ConcurrentHashMap<>(); - private final Handler mHandler; - private final Context mContext; - final Dependencies mDeps; - - public static class Dependencies { - public void makeIpClient(Context context, String iface, IpClientCallbacks callbacks) { - IpClientUtil.makeIpClient(context, iface, callbacks); - } - - public IpClientManager makeIpClientManager(@NonNull final IIpClient ipClient) { - return new IpClientManager(ipClient, TAG); - } - - public EthernetNetworkAgent makeEthernetNetworkAgent(Context context, Looper looper, - NetworkCapabilities nc, LinkProperties lp, NetworkAgentConfig config, - NetworkProvider provider, EthernetNetworkAgent.Callbacks cb) { - return new EthernetNetworkAgent(context, looper, nc, lp, config, provider, cb); - } - - public InterfaceParams getNetworkInterfaceByName(String name) { - return InterfaceParams.getByName(name); - } - - // TODO: remove legacy resource fallback after migrating its overlays. - private String getPlatformTcpBufferSizes(Context context) { - final Resources r = context.getResources(); - final int resId = r.getIdentifier("config_ethernet_tcp_buffers", "string", - context.getPackageName()); - return r.getString(resId); - } - - public String getTcpBufferSizesFromResource(Context context) { - final String tcpBufferSizes; - final String platformTcpBufferSizes = getPlatformTcpBufferSizes(context); - if (!LEGACY_TCP_BUFFER_SIZES.equals(platformTcpBufferSizes)) { - // Platform resource is not the historical default: use the overlay. - tcpBufferSizes = platformTcpBufferSizes; - } else { - final ConnectivityResources resources = new ConnectivityResources(context); - tcpBufferSizes = resources.get().getString(R.string.config_ethernet_tcp_buffers); - } - return tcpBufferSizes; - } - } - - public static class ConfigurationException extends AndroidRuntimeException { - public ConfigurationException(String msg) { - super(msg); - } - } - - public EthernetNetworkFactory(Handler handler, Context context) { - this(handler, context, new Dependencies()); - } - - @VisibleForTesting - EthernetNetworkFactory(Handler handler, Context context, Dependencies deps) { - super(handler.getLooper(), context, NETWORK_TYPE, createDefaultNetworkCapabilities()); - - mHandler = handler; - mContext = context; - mDeps = deps; - - setScoreFilter(NETWORK_SCORE); - } - - @Override - public boolean acceptRequest(NetworkRequest request) { - if (DBG) { - Log.d(TAG, "acceptRequest, request: " + request); - } - - return networkForRequest(request) != null; - } - - @Override - protected void needNetworkFor(NetworkRequest networkRequest) { - NetworkInterfaceState network = networkForRequest(networkRequest); - - if (network == null) { - Log.e(TAG, "needNetworkFor, failed to get a network for " + networkRequest); - return; - } - - if (++network.refCount == 1) { - network.start(); - } - } - - @Override - protected void releaseNetworkFor(NetworkRequest networkRequest) { - NetworkInterfaceState network = networkForRequest(networkRequest); - if (network == null) { - Log.e(TAG, "releaseNetworkFor, failed to get a network for " + networkRequest); - return; - } - - if (--network.refCount == 0) { - network.stop(); - } - } - - /** - * Returns an array of available interface names. The array is sorted: unrestricted interfaces - * goes first, then sorted by name. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected String[] getAvailableInterfaces(boolean includeRestricted) { - return mTrackingInterfaces.values() - .stream() - .filter(iface -> !iface.isRestricted() || includeRestricted) - .sorted((iface1, iface2) -> { - int r = Boolean.compare(iface1.isRestricted(), iface2.isRestricted()); - return r == 0 ? iface1.name.compareTo(iface2.name) : r; - }) - .map(iface -> iface.name) - .toArray(String[]::new); - } - - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected 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; - } - - final NetworkCapabilities nc = new NetworkCapabilities.Builder(capabilities) - .setNetworkSpecifier(new EthernetNetworkSpecifier(ifaceName)) - .build(); - - if (DBG) { - Log.d(TAG, "addInterface, iface: " + ifaceName + ", capabilities: " + nc); - } - - final NetworkInterfaceState iface = new NetworkInterfaceState( - ifaceName, hwAddress, mHandler, mContext, ipConfig, nc, this, mDeps); - mTrackingInterfaces.put(ifaceName, iface); - updateCapabilityFilter(); - } - - @VisibleForTesting - protected int getInterfaceState(@NonNull String iface) { - final NetworkInterfaceState interfaceState = mTrackingInterfaces.get(iface); - if (interfaceState == null) { - return EthernetManager.STATE_ABSENT; - } else if (!interfaceState.mLinkUp) { - return EthernetManager.STATE_LINK_DOWN; - } else { - return EthernetManager.STATE_LINK_UP; - } - } - - /** - * 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 or null. If - * {@code null} is passed, the existing IpConfiguration is not updated. - * @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 INetworkInterfaceOutcomeReceiver} to notify callers of - * completion. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected void updateInterface(@NonNull final String ifaceName, - @Nullable final IpConfiguration ipConfig, - @Nullable final NetworkCapabilities capabilities, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (!hasInterface(ifaceName)) { - maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); - return; - } - - final NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName); - iface.updateInterface(ipConfig, capabilities, listener); - mTrackingInterfaces.put(ifaceName, iface); - updateCapabilityFilter(); - } - - private static NetworkCapabilities mixInCapabilities(NetworkCapabilities nc, - NetworkCapabilities addedNc) { - final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(nc); - for (int transport : addedNc.getTransportTypes()) builder.addTransportType(transport); - for (int capability : addedNc.getCapabilities()) builder.addCapability(capability); - return builder.build(); - } - - private void updateCapabilityFilter() { - NetworkCapabilities capabilitiesFilter = createDefaultNetworkCapabilities(); - for (NetworkInterfaceState iface: mTrackingInterfaces.values()) { - capabilitiesFilter = mixInCapabilities(capabilitiesFilter, iface.mCapabilities); - } - - if (DBG) Log.d(TAG, "updateCapabilityFilter: " + capabilitiesFilter); - setCapabilityFilter(capabilitiesFilter); - } - - private static NetworkCapabilities createDefaultNetworkCapabilities() { - return NetworkCapabilities.Builder - .withoutDefaultCapabilities() - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET).build(); - } - - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected void removeInterface(String interfaceName) { - NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName); - if (iface != null) { - iface.maybeSendNetworkManagementCallbackForAbort(); - iface.stop(); - } - - updateCapabilityFilter(); - } - - /** Returns true if state has been modified */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected boolean updateInterfaceLinkState(@NonNull final String ifaceName, final boolean up, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (!hasInterface(ifaceName)) { - maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); - return false; - } - - if (DBG) { - Log.d(TAG, "updateInterfaceLinkState, iface: " + ifaceName + ", up: " + up); - } - - NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName); - return iface.updateLinkState(up, listener); - } - - private void maybeSendNetworkManagementCallbackForUntracked( - String ifaceName, INetworkInterfaceOutcomeReceiver listener) { - maybeSendNetworkManagementCallback(listener, null, - new EthernetNetworkManagementException( - ifaceName + " can't be updated as it is not available.")); - } - - @VisibleForTesting - protected boolean hasInterface(String ifaceName) { - return mTrackingInterfaces.containsKey(ifaceName); - } - - private NetworkInterfaceState networkForRequest(NetworkRequest request) { - String requestedIface = null; - - NetworkSpecifier specifier = request.getNetworkSpecifier(); - if (specifier instanceof EthernetNetworkSpecifier) { - requestedIface = ((EthernetNetworkSpecifier) specifier) - .getInterfaceName(); - } - - NetworkInterfaceState network = null; - if (!TextUtils.isEmpty(requestedIface)) { - NetworkInterfaceState n = mTrackingInterfaces.get(requestedIface); - if (n != null && request.canBeSatisfiedBy(n.mCapabilities)) { - network = n; - } - } else { - for (NetworkInterfaceState n : mTrackingInterfaces.values()) { - if (request.canBeSatisfiedBy(n.mCapabilities) && n.mLinkUp) { - network = n; - break; - } - } - } - - if (DBG) { - Log.i(TAG, "networkForRequest, request: " + request + ", network: " + network); - } - - return network; - } - - private static void maybeSendNetworkManagementCallback( - @Nullable final INetworkInterfaceOutcomeReceiver listener, - @Nullable final String iface, - @Nullable final EthernetNetworkManagementException e) { - if (null == listener) { - return; - } - - try { - if (iface != null) { - listener.onResult(iface); - } else { - listener.onError(e); - } - } catch (RemoteException re) { - Log.e(TAG, "Can't send onComplete for network management callback", re); - } - } - - @VisibleForTesting - static class NetworkInterfaceState { - final String name; - - private final String mHwAddress; - private final Handler mHandler; - private final Context mContext; - private final NetworkFactory mNetworkFactory; - private final Dependencies mDeps; - - 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 EthernetIpClientCallback mIpClientCallback; - private @Nullable EthernetNetworkAgent mNetworkAgent; - private @Nullable IpConfiguration mIpConfig; - - /** - * A map of TRANSPORT_* types to legacy transport types available for each type an ethernet - * interface could propagate. - * - * There are no legacy type equivalents to LOWPAN or WIFI_AWARE. These types are set to - * TYPE_NONE to match the behavior of their own network factories. - */ - private static final SparseArray sTransports = new SparseArray(); - static { - sTransports.put(NetworkCapabilities.TRANSPORT_ETHERNET, - ConnectivityManager.TYPE_ETHERNET); - sTransports.put(NetworkCapabilities.TRANSPORT_BLUETOOTH, - ConnectivityManager.TYPE_BLUETOOTH); - sTransports.put(NetworkCapabilities.TRANSPORT_WIFI, ConnectivityManager.TYPE_WIFI); - sTransports.put(NetworkCapabilities.TRANSPORT_CELLULAR, - ConnectivityManager.TYPE_MOBILE); - sTransports.put(NetworkCapabilities.TRANSPORT_LOWPAN, ConnectivityManager.TYPE_NONE); - sTransports.put(NetworkCapabilities.TRANSPORT_WIFI_AWARE, - ConnectivityManager.TYPE_NONE); - } - - long refCount = 0; - - private class EthernetIpClientCallback extends IpClientCallbacks { - private final ConditionVariable mIpClientStartCv = new ConditionVariable(false); - private final ConditionVariable mIpClientShutdownCv = new ConditionVariable(false); - @Nullable INetworkInterfaceOutcomeReceiver mNetworkManagementListener; - - EthernetIpClientCallback(@Nullable final INetworkInterfaceOutcomeReceiver listener) { - mNetworkManagementListener = listener; - } - - @Override - public void onIpClientCreated(IIpClient ipClient) { - mIpClient = mDeps.makeIpClientManager(ipClient); - mIpClientStartCv.open(); - } - - private void awaitIpClientStart() { - mIpClientStartCv.block(); - } - - private void awaitIpClientShutdown() { - mIpClientShutdownCv.block(); - } - - // At the time IpClient is stopped, an IpClient event may have already been posted on - // the back of the handler and is awaiting execution. Once that event is executed, the - // associated callback object may not be valid anymore - // (NetworkInterfaceState#mIpClientCallback points to a different object / null). - private boolean isCurrentCallback() { - return this == mIpClientCallback; - } - - private void handleIpEvent(final @NonNull Runnable r) { - mHandler.post(() -> { - if (!isCurrentCallback()) { - Log.i(TAG, "Ignoring stale IpClientCallbacks " + this); - return; - } - r.run(); - }); - } - - @Override - public void onProvisioningSuccess(LinkProperties newLp) { - handleIpEvent(() -> onIpLayerStarted(newLp, mNetworkManagementListener)); - } - - @Override - public void onProvisioningFailure(LinkProperties newLp) { - // This cannot happen due to provisioning timeout, because our timeout is 0. It can - // happen due to errors while provisioning or on provisioning loss. - handleIpEvent(() -> onIpLayerStopped(mNetworkManagementListener)); - } - - @Override - public void onLinkPropertiesChange(LinkProperties newLp) { - handleIpEvent(() -> updateLinkProperties(newLp)); - } - - @Override - public void onReachabilityLost(String logMsg) { - handleIpEvent(() -> updateNeighborLostEvent(logMsg)); - } - - @Override - public void onQuit() { - mIpClient = null; - mIpClientShutdownCv.open(); - } - } - - NetworkInterfaceState(String ifaceName, String hwAddress, Handler handler, Context context, - @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; - mHwAddress = hwAddress; - } - - /** - * Determines the legacy transport type from a NetworkCapabilities transport type. Defaults - * to legacy TYPE_NONE if there is no known conversion - */ - private static int getLegacyType(int transport) { - 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); - } - - void updateInterface(@Nullable final IpConfiguration ipConfig, - @Nullable final NetworkCapabilities capabilities, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (DBG) { - Log.d(TAG, "updateInterface, iface: " + name - + ", ipConfig: " + ipConfig + ", old ipConfig: " + mIpConfig - + ", capabilities: " + capabilities + ", old capabilities: " + mCapabilities - + ", listener: " + listener - ); - } - - if (null != ipConfig){ - mIpConfig = ipConfig; - } - if (null != capabilities) { - 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() { - return !mCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); - } - - private void start() { - start(null); - } - - private void start(@Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (mIpClient != null) { - if (DBG) Log.d(TAG, "IpClient already started"); - return; - } - if (DBG) { - Log.d(TAG, String.format("Starting Ethernet IpClient(%s)", name)); - } - - mIpClientCallback = new EthernetIpClientCallback(listener); - mDeps.makeIpClient(mContext, name, mIpClientCallback); - mIpClientCallback.awaitIpClientStart(); - - if (sTcpBufferSizes == null) { - sTcpBufferSizes = mDeps.getTcpBufferSizesFromResource(mContext); - } - provisionIpClient(mIpClient, mIpConfig, sTcpBufferSizes); - } - - void onIpLayerStarted(@NonNull final LinkProperties linkProperties, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (mNetworkAgent != null) { - Log.e(TAG, "Already have a NetworkAgent - aborting new request"); - stop(); - return; - } - mLinkProperties = linkProperties; - - // Create our NetworkAgent. - final NetworkAgentConfig config = new NetworkAgentConfig.Builder() - .setLegacyType(mLegacyType) - .setLegacyTypeName(NETWORK_TYPE) - .setLegacyExtraInfo(mHwAddress) - .build(); - mNetworkAgent = mDeps.makeEthernetNetworkAgent(mContext, mHandler.getLooper(), - mCapabilities, mLinkProperties, config, mNetworkFactory.getProvider(), - new EthernetNetworkAgent.Callbacks() { - @Override - public void onNetworkUnwanted() { - // if mNetworkAgent is null, we have already called stop. - if (mNetworkAgent == null) return; - - if (this == mNetworkAgent.getCallbacks()) { - stop(); - } else { - Log.d(TAG, "Ignoring unwanted as we have a more modern " + - "instance"); - } - } - }); - mNetworkAgent.register(); - mNetworkAgent.markConnected(); - realizeNetworkManagementCallback(name, null); - } - - void onIpLayerStopped(@Nullable final INetworkInterfaceOutcomeReceiver listener) { - // 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 EthernetNetworkManagementException( - "The IP provisioning request has been aborted.")); - } - - // Must be called on the handler thread - private void realizeNetworkManagementCallback(@Nullable final String iface, - @Nullable final EthernetNetworkManagementException e) { - ensureRunningOnEthernetHandlerThread(); - if (null == mIpClientCallback) { - return; - } - - EthernetNetworkFactory.maybeSendNetworkManagementCallback( - mIpClientCallback.mNetworkManagementListener, iface, 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()); - } - } - - void updateLinkProperties(LinkProperties linkProperties) { - mLinkProperties = linkProperties; - if (mNetworkAgent != null) { - mNetworkAgent.sendLinkPropertiesImpl(linkProperties); - } - } - - void updateNeighborLostEvent(String logMsg) { - Log.i(TAG, "updateNeighborLostEvent " + logMsg); - // Reachability lost will be seen only if the gateway is not reachable. - // Since ethernet FW doesn't have the mechanism to scan for new networks - // like WiFi, simply restart. - // If there is a better network, that will become default and apps - // will be able to use internet. If ethernet gets connected again, - // and has backhaul connectivity, it will become default. - restart(); - } - - /** Returns true if state has been modified */ - boolean updateLinkState(final boolean up, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (mLinkUp == up) { - EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, null, - new EthernetNetworkManagementException( - "No changes with requested link state " + up + " for " + name)); - return false; - } - mLinkUp = up; - - if (!up) { // was up, goes down - // Send an abort on a provisioning request callback if necessary before stopping. - maybeSendNetworkManagementCallbackForAbort(); - stop(); - // If only setting the interface down, send a callback to signal completion. - EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, name, null); - } else { // was down, goes up - stop(); - start(listener); - } - - return true; - } - - void stop() { - // Invalidate all previous start requests - if (mIpClient != null) { - mIpClient.shutdown(); - mIpClientCallback.awaitIpClientShutdown(); - mIpClient = null; - } - mIpClientCallback = null; - - if (mNetworkAgent != null) { - mNetworkAgent.unregister(); - mNetworkAgent = null; - } - mLinkProperties.clear(); - } - - private static void provisionIpClient(@NonNull final IpClientManager ipClient, - @NonNull final IpConfiguration config, @NonNull final String tcpBufferSizes) { - if (config.getProxySettings() == ProxySettings.STATIC || - config.getProxySettings() == ProxySettings.PAC) { - ipClient.setHttpProxy(config.getHttpProxy()); - } - - if (!TextUtils.isEmpty(tcpBufferSizes)) { - ipClient.setTcpBufferSizes(tcpBufferSizes); - } - - ipClient.startProvisioning(createProvisioningConfiguration(config)); - } - - private static ProvisioningConfiguration createProvisioningConfiguration( - @NonNull final IpConfiguration config) { - if (config.getIpAssignment() == IpAssignment.STATIC) { - return new ProvisioningConfiguration.Builder() - .withStaticConfiguration(config.getStaticIpConfiguration()) - .build(); - } - return new ProvisioningConfiguration.Builder() - .withProvisioningTimeoutMs(0) - .build(); - } - - void restart() { - restart(null); - } - - void restart(@Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (DBG) Log.d(TAG, "reconnecting Ethernet"); - stop(); - start(listener); - } - - @Override - public String toString() { - return getClass().getSimpleName() + "{ " - + "refCount: " + refCount + ", " - + "iface: " + name + ", " - + "up: " + mLinkUp + ", " - + "hwAddress: " + mHwAddress + ", " - + "networkCapabilities: " + mCapabilities + ", " - + "networkAgent: " + mNetworkAgent + ", " - + "ipClient: " + mIpClient + "," - + "linkProperties: " + mLinkProperties - + "}"; - } - } - - void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { - super.dump(fd, pw, args); - pw.println(getClass().getSimpleName()); - pw.println("Tracking interfaces:"); - pw.increaseIndent(); - for (String iface: mTrackingInterfaces.keySet()) { - NetworkInterfaceState ifaceState = mTrackingInterfaces.get(iface); - pw.println(iface + ":" + ifaceState); - pw.increaseIndent(); - if (null == ifaceState.mIpClient) { - pw.println("IpClient is null"); - } - pw.decreaseIndent(); - } - pw.decreaseIndent(); - } -} diff --git a/java/com/android/server/ethernet/EthernetService.java b/java/com/android/server/ethernet/EthernetService.java deleted file mode 100644 index d405fd5..0000000 --- a/java/com/android/server/ethernet/EthernetService.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2014 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 android.content.Context; -import android.net.INetd; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; - -import java.util.Objects; - -// TODO: consider renaming EthernetServiceImpl to EthernetService and deleting this file. -public final class EthernetService { - private static final String TAG = "EthernetService"; - private static final String THREAD_NAME = "EthernetServiceThread"; - - private static INetd getNetd(Context context) { - final INetd netd = - INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)); - Objects.requireNonNull(netd, "could not get netd instance"); - return netd; - } - - public static EthernetServiceImpl create(Context context) { - final HandlerThread handlerThread = new HandlerThread(THREAD_NAME); - handlerThread.start(); - final Handler handler = new Handler(handlerThread.getLooper()); - final EthernetNetworkFactory factory = new EthernetNetworkFactory(handler, context); - return new EthernetServiceImpl(context, handler, - new EthernetTracker(context, handler, factory, getNetd(context))); - } -} diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java deleted file mode 100644 index 5e830ad..0000000 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright (C) 2014 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 android.net.NetworkCapabilities.TRANSPORT_TEST; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.content.pm.PackageManager; -import android.net.IEthernetManager; -import android.net.IEthernetServiceListener; -import android.net.INetworkInterfaceOutcomeReceiver; -import android.net.ITetheredInterfaceCallback; -import android.net.EthernetNetworkUpdateRequest; -import android.net.IpConfiguration; -import android.net.NetworkCapabilities; -import android.os.Binder; -import android.os.Handler; -import android.os.RemoteException; -import android.util.Log; -import android.util.PrintWriterPrinter; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; -import com.android.net.module.util.PermissionUtils; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * EthernetServiceImpl handles remote Ethernet operation requests by implementing - * the IEthernetManager interface. - */ -public class EthernetServiceImpl extends IEthernetManager.Stub { - private static final String TAG = "EthernetServiceImpl"; - - @VisibleForTesting - final AtomicBoolean mStarted = new AtomicBoolean(false); - private final Context mContext; - private final Handler mHandler; - private final EthernetTracker mTracker; - - EthernetServiceImpl(@NonNull final Context context, @NonNull final Handler handler, - @NonNull final EthernetTracker tracker) { - mContext = context; - mHandler = handler; - mTracker = tracker; - } - - private void enforceAutomotiveDevice(final @NonNull String methodName) { - PermissionUtils.enforceSystemFeature(mContext, PackageManager.FEATURE_AUTOMOTIVE, - methodName + " is only available on automotive devices."); - } - - private boolean checkUseRestrictedNetworksPermission() { - return PermissionUtils.checkAnyPermissionOf(mContext, - android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS); - } - - public void start() { - Log.i(TAG, "Starting Ethernet service"); - mTracker.start(); - mStarted.set(true); - } - - private void throwIfEthernetNotStarted() { - if (!mStarted.get()) { - throw new IllegalStateException("System isn't ready to change ethernet configurations"); - } - } - - @Override - public String[] getAvailableInterfaces() throws RemoteException { - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - return mTracker.getInterfaces(checkUseRestrictedNetworksPermission()); - } - - /** - * Get Ethernet configuration - * @return the Ethernet Configuration, contained in {@link IpConfiguration}. - */ - @Override - public IpConfiguration getConfiguration(String iface) { - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - if (mTracker.isRestrictedInterface(iface)) { - PermissionUtils.enforceRestrictedNetworkPermission(mContext, TAG); - } - - return new IpConfiguration(mTracker.getIpConfiguration(iface)); - } - - /** - * Set Ethernet configuration - */ - @Override - public void setConfiguration(String iface, IpConfiguration config) { - throwIfEthernetNotStarted(); - - PermissionUtils.enforceNetworkStackPermission(mContext); - if (mTracker.isRestrictedInterface(iface)) { - PermissionUtils.enforceRestrictedNetworkPermission(mContext, TAG); - } - - // TODO: this does not check proxy settings, gateways, etc. - // Fix this by making IpConfiguration a complete representation of static configuration. - mTracker.updateIpConfiguration(iface, new IpConfiguration(config)); - } - - /** - * Indicates whether given interface is available. - */ - @Override - public boolean isAvailable(String iface) { - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - if (mTracker.isRestrictedInterface(iface)) { - PermissionUtils.enforceRestrictedNetworkPermission(mContext, TAG); - } - - return mTracker.isTrackingInterface(iface); - } - - /** - * Adds a listener. - * @param listener A {@link IEthernetServiceListener} to add. - */ - public void addListener(IEthernetServiceListener listener) throws RemoteException { - Objects.requireNonNull(listener, "listener must not be null"); - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - mTracker.addListener(listener, checkUseRestrictedNetworksPermission()); - } - - /** - * Removes a listener. - * @param listener A {@link IEthernetServiceListener} to remove. - */ - public void removeListener(IEthernetServiceListener listener) { - if (listener == null) { - throw new IllegalArgumentException("listener must not be null"); - } - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - mTracker.removeListener(listener); - } - - @Override - public void setIncludeTestInterfaces(boolean include) { - PermissionUtils.enforceNetworkStackPermissionOr(mContext, - android.Manifest.permission.NETWORK_SETTINGS); - mTracker.setIncludeTestInterfaces(include); - } - - @Override - public void requestTetheredInterface(ITetheredInterfaceCallback callback) { - Objects.requireNonNull(callback, "callback must not be null"); - PermissionUtils.enforceNetworkStackPermissionOr(mContext, - android.Manifest.permission.NETWORK_SETTINGS); - mTracker.requestTetheredInterface(callback); - } - - @Override - public void releaseTetheredInterface(ITetheredInterfaceCallback callback) { - Objects.requireNonNull(callback, "callback must not be null"); - PermissionUtils.enforceNetworkStackPermissionOr(mContext, - android.Manifest.permission.NETWORK_SETTINGS); - mTracker.releaseTetheredInterface(callback); - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump EthernetService from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid()); - return; - } - - pw.println("Current Ethernet state: "); - pw.increaseIndent(); - mTracker.dump(fd, pw, args); - pw.decreaseIndent(); - - pw.println("Handler:"); - pw.increaseIndent(); - mHandler.dump(new PrintWriterPrinter(pw), "EthernetServiceImpl"); - pw.decreaseIndent(); - } - - private void enforceNetworkManagementPermission() { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.MANAGE_ETHERNET_NETWORKS, - "EthernetServiceImpl"); - } - - private void enforceManageTestNetworksPermission() { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.MANAGE_TEST_NETWORKS, - "EthernetServiceImpl"); - } - - private void maybeValidateTestCapabilities(final String iface, - @Nullable final NetworkCapabilities nc) { - if (!mTracker.isValidTestInterface(iface)) { - return; - } - // For test interfaces, only null or capabilities that include TRANSPORT_TEST are - // allowed. - if (nc != null && !nc.hasTransport(TRANSPORT_TEST)) { - throw new IllegalArgumentException( - "Updates to test interfaces must have NetworkCapabilities.TRANSPORT_TEST."); - } - } - - private void enforceAdminPermission(final String iface, boolean enforceAutomotive, - final String logMessage) { - if (mTracker.isValidTestInterface(iface)) { - enforceManageTestNetworksPermission(); - } else { - enforceNetworkManagementPermission(); - if (enforceAutomotive) { - enforceAutomotiveDevice(logMessage); - } - } - } - - @Override - public void updateConfiguration(@NonNull final String iface, - @NonNull final EthernetNetworkUpdateRequest request, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - Objects.requireNonNull(iface); - Objects.requireNonNull(request); - throwIfEthernetNotStarted(); - - // TODO: validate that iface is listed in overlay config_ethernet_interfaces - // only automotive devices are allowed to set the NetworkCapabilities using this API - enforceAdminPermission(iface, request.getNetworkCapabilities() != null, - "updateConfiguration() with non-null capabilities"); - maybeValidateTestCapabilities(iface, request.getNetworkCapabilities()); - - mTracker.updateConfiguration( - iface, request.getIpConfiguration(), request.getNetworkCapabilities(), listener); - } - - @Override - public void connectNetwork(@NonNull final String iface, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - Log.i(TAG, "connectNetwork called with: iface=" + iface + ", listener=" + listener); - Objects.requireNonNull(iface); - throwIfEthernetNotStarted(); - - enforceAdminPermission(iface, true, "connectNetwork()"); - - mTracker.connectNetwork(iface, listener); - } - - @Override - public void disconnectNetwork(@NonNull final String iface, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - Log.i(TAG, "disconnectNetwork called with: iface=" + iface + ", listener=" + listener); - Objects.requireNonNull(iface); - throwIfEthernetNotStarted(); - - enforceAdminPermission(iface, true, "connectNetwork()"); - - mTracker.disconnectNetwork(iface, listener); - } - - @Override - public void setEthernetEnabled(boolean enabled) { - PermissionUtils.enforceNetworkStackPermissionOr(mContext, - android.Manifest.permission.NETWORK_SETTINGS); - - mTracker.setEthernetEnabled(enabled); - } - - @Override - public List getInterfaceList() { - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - return mTracker.getInterfaceList(); - } -} diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java deleted file mode 100644 index abb1635..0000000 --- a/java/com/android/server/ethernet/EthernetTracker.java +++ /dev/null @@ -1,943 +0,0 @@ -/* - * Copyright (C) 2018 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 android.net.EthernetManager.ETHERNET_STATE_DISABLED; -import static android.net.EthernetManager.ETHERNET_STATE_ENABLED; -import static android.net.TestNetworkManager.TEST_TAP_PREFIX; - -import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.content.res.Resources; -import android.net.ConnectivityResources; -import android.net.EthernetManager; -import android.net.IEthernetServiceListener; -import android.net.INetworkInterfaceOutcomeReceiver; -import android.net.INetd; -import android.net.ITetheredInterfaceCallback; -import android.net.InterfaceConfigurationParcel; -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.ConditionVariable; -import android.os.Handler; -import android.os.RemoteCallbackList; -import android.os.RemoteException; -import android.os.ServiceSpecificException; -import android.text.TextUtils; -import android.util.ArrayMap; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; -import com.android.net.module.util.BaseNetdUnsolicitedEventListener; -import com.android.net.module.util.NetdUtils; -import com.android.net.module.util.PermissionUtils; - -import java.io.FileDescriptor; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Tracks Ethernet interfaces and manages interface configurations. - * - *

Interfaces may have different {@link android.net.NetworkCapabilities}. This mapping is defined - * in {@code config_ethernet_interfaces}. Notably, some interfaces could be marked as restricted by - * not specifying {@link android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED} flag. - * Interfaces could have associated {@link android.net.IpConfiguration}. - * Ethernet Interfaces may be present at boot time or appear after boot (e.g., for Ethernet adapters - * connected over USB). This class supports multiple interfaces. When an interface appears on the - * system (or is present at boot time) this class will start tracking it and bring it up. Only - * interfaces whose names match the {@code config_ethernet_iface_regex} regular expression are - * tracked. - * - *

All public or package private methods must be thread-safe unless stated otherwise. - */ -@VisibleForTesting(visibility = PACKAGE) -public class EthernetTracker { - private static final int INTERFACE_MODE_CLIENT = 1; - private static final int INTERFACE_MODE_SERVER = 2; - - private static final String TAG = EthernetTracker.class.getSimpleName(); - private static final boolean DBG = EthernetNetworkFactory.DBG; - - private static final String TEST_IFACE_REGEXP = TEST_TAP_PREFIX + "\\d+"; - private static final String LEGACY_IFACE_REGEXP = "eth\\d"; - - /** - * Interface names we track. This is a product-dependent regular expression, plus, - * if setIncludeTestInterfaces is true, any test interfaces. - */ - private volatile String mIfaceMatch; - - /** - * Track test interfaces if true, don't track otherwise. - */ - private boolean mIncludeTestInterfaces = false; - - /** Mapping between {iface name | mac address} -> {NetworkCapabilities} */ - private final ConcurrentHashMap mNetworkCapabilities = - new ConcurrentHashMap<>(); - private final ConcurrentHashMap mIpConfigurations = - new ConcurrentHashMap<>(); - - private final Context mContext; - private final INetd mNetd; - private final Handler mHandler; - private final EthernetNetworkFactory mFactory; - private final EthernetConfigStore mConfigStore; - private final Dependencies mDeps; - - private final RemoteCallbackList mListeners = - new RemoteCallbackList<>(); - private final TetheredInterfaceRequestList mTetheredInterfaceRequests = - new TetheredInterfaceRequestList(); - - // Used only on the handler thread - private String mDefaultInterface; - private int mDefaultInterfaceMode = INTERFACE_MODE_CLIENT; - // Tracks whether clients were notified that the tethered interface is available - private boolean mTetheredInterfaceWasAvailable = false; - private volatile IpConfiguration mIpConfigForDefaultInterface; - - private int mEthernetState = ETHERNET_STATE_ENABLED; - - private class TetheredInterfaceRequestList extends - RemoteCallbackList { - @Override - public void onCallbackDied(ITetheredInterfaceCallback cb, Object cookie) { - mHandler.post(EthernetTracker.this::maybeUntetherDefaultInterface); - } - } - - public static class Dependencies { - // TODO: remove legacy resource fallback after migrating its overlays. - private String getPlatformRegexResource(Context context) { - final Resources r = context.getResources(); - final int resId = - r.getIdentifier("config_ethernet_iface_regex", "string", context.getPackageName()); - return r.getString(resId); - } - - // TODO: remove legacy resource fallback after migrating its overlays. - private String[] getPlatformInterfaceConfigs(Context context) { - final Resources r = context.getResources(); - final int resId = r.getIdentifier("config_ethernet_interfaces", "array", - context.getPackageName()); - return r.getStringArray(resId); - } - - public String getInterfaceRegexFromResource(Context context) { - final String platformRegex = getPlatformRegexResource(context); - final String match; - if (!LEGACY_IFACE_REGEXP.equals(platformRegex)) { - // Platform resource is not the historical default: use the overlay - match = platformRegex; - } else { - final ConnectivityResources resources = new ConnectivityResources(context); - match = resources.get().getString( - com.android.connectivity.resources.R.string.config_ethernet_iface_regex); - } - return match; - } - - public String[] getInterfaceConfigFromResource(Context context) { - final String[] platformInterfaceConfigs = getPlatformInterfaceConfigs(context); - final String[] interfaceConfigs; - if (platformInterfaceConfigs.length != 0) { - // Platform resource is not the historical default: use the overlay - interfaceConfigs = platformInterfaceConfigs; - } else { - final ConnectivityResources resources = new ConnectivityResources(context); - interfaceConfigs = resources.get().getStringArray( - com.android.connectivity.resources.R.array.config_ethernet_interfaces); - } - return interfaceConfigs; - } - } - - EthernetTracker(@NonNull final Context context, @NonNull final Handler handler, - @NonNull final EthernetNetworkFactory factory, @NonNull final INetd netd) { - this(context, handler, factory, netd, new Dependencies()); - } - - @VisibleForTesting - EthernetTracker(@NonNull final Context context, @NonNull final Handler handler, - @NonNull final EthernetNetworkFactory factory, @NonNull final INetd netd, - @NonNull final Dependencies deps) { - mContext = context; - mHandler = handler; - mFactory = factory; - mNetd = netd; - mDeps = deps; - - // Interface match regex. - updateIfaceMatchRegexp(); - - // Read default Ethernet interface configuration from resources - final String[] interfaceConfigs = mDeps.getInterfaceConfigFromResource(context); - for (String strConfig : interfaceConfigs) { - parseEthernetConfig(strConfig); - } - - mConfigStore = new EthernetConfigStore(); - } - - void start() { - mFactory.register(); - mConfigStore.read(); - - // Default interface is just the first one we want to track. - mIpConfigForDefaultInterface = mConfigStore.getIpConfigurationForDefaultInterface(); - final ArrayMap configs = mConfigStore.getIpConfigurations(); - for (int i = 0; i < configs.size(); i++) { - mIpConfigurations.put(configs.keyAt(i), configs.valueAt(i)); - } - - try { - PermissionUtils.enforceNetworkStackPermission(mContext); - mNetd.registerUnsolicitedEventListener(new InterfaceObserver()); - } catch (RemoteException | ServiceSpecificException e) { - Log.e(TAG, "Could not register InterfaceObserver " + e); - } - - mHandler.post(this::trackAvailableInterfaces); - } - - void updateIpConfiguration(String iface, IpConfiguration ipConfiguration) { - if (DBG) { - Log.i(TAG, "updateIpConfiguration, iface: " + iface + ", cfg: " + ipConfiguration); - } - writeIpConfiguration(iface, ipConfiguration); - mHandler.post(() -> { - mFactory.updateInterface(iface, ipConfiguration, null, null); - broadcastInterfaceStateChange(iface); - }); - } - - private void writeIpConfiguration(@NonNull final String iface, - @NonNull final IpConfiguration ipConfig) { - mConfigStore.write(iface, ipConfig); - mIpConfigurations.put(iface, ipConfig); - } - - private IpConfiguration getIpConfigurationForCallback(String iface, int state) { - return (state == EthernetManager.STATE_ABSENT) ? null : getOrCreateIpConfiguration(iface); - } - - private void ensureRunningOnEthernetServiceThread() { - if (mHandler.getLooper().getThread() != Thread.currentThread()) { - throw new IllegalStateException( - "Not running on EthernetService thread: " - + Thread.currentThread().getName()); - } - } - - /** - * Broadcast the link state or IpConfiguration change of existing Ethernet interfaces to all - * listeners. - */ - protected void broadcastInterfaceStateChange(@NonNull String iface) { - ensureRunningOnEthernetServiceThread(); - final int state = mFactory.getInterfaceState(iface); - final int role = getInterfaceRole(iface); - final IpConfiguration config = getIpConfigurationForCallback(iface, state); - final int n = mListeners.beginBroadcast(); - for (int i = 0; i < n; i++) { - try { - mListeners.getBroadcastItem(i).onInterfaceStateChanged(iface, state, role, config); - } catch (RemoteException e) { - // Do nothing here. - } - } - mListeners.finishBroadcast(); - } - - /** - * Unicast the interface state or IpConfiguration change of existing Ethernet interfaces to a - * specific listener. - */ - protected void unicastInterfaceStateChange(@NonNull IEthernetServiceListener listener, - @NonNull String iface) { - ensureRunningOnEthernetServiceThread(); - final int state = mFactory.getInterfaceState(iface); - final int role = getInterfaceRole(iface); - final IpConfiguration config = getIpConfigurationForCallback(iface, state); - try { - listener.onInterfaceStateChanged(iface, state, role, config); - } catch (RemoteException e) { - // Do nothing here. - } - } - - @VisibleForTesting(visibility = PACKAGE) - protected void updateConfiguration(@NonNull final String iface, - @Nullable final IpConfiguration ipConfig, - @Nullable final NetworkCapabilities capabilities, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (DBG) { - Log.i(TAG, "updateConfiguration, iface: " + iface + ", capabilities: " + capabilities - + ", ipConfig: " + ipConfig); - } - - final IpConfiguration localIpConfig = ipConfig == null - ? null : new IpConfiguration(ipConfig); - if (ipConfig != null) { - writeIpConfiguration(iface, localIpConfig); - } - - if (null != capabilities) { - mNetworkCapabilities.put(iface, capabilities); - } - mHandler.post(() -> { - mFactory.updateInterface(iface, localIpConfig, capabilities, listener); - broadcastInterfaceStateChange(iface); - }); - } - - @VisibleForTesting(visibility = PACKAGE) - protected void connectNetwork(@NonNull final String iface, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - mHandler.post(() -> updateInterfaceState(iface, true, listener)); - } - - @VisibleForTesting(visibility = PACKAGE) - protected void disconnectNetwork(@NonNull final String iface, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - mHandler.post(() -> updateInterfaceState(iface, false, listener)); - } - - IpConfiguration getIpConfiguration(String iface) { - return mIpConfigurations.get(iface); - } - - @VisibleForTesting(visibility = PACKAGE) - protected boolean isTrackingInterface(String iface) { - return mFactory.hasInterface(iface); - } - - String[] getInterfaces(boolean includeRestricted) { - return mFactory.getAvailableInterfaces(includeRestricted); - } - - List getInterfaceList() { - final List interfaceList = new ArrayList(); - final String[] ifaces; - try { - ifaces = mNetd.interfaceGetList(); - } catch (RemoteException e) { - Log.e(TAG, "Could not get list of interfaces " + e); - return interfaceList; - } - final String ifaceMatch = mIfaceMatch; - for (String iface : ifaces) { - if (iface.matches(ifaceMatch)) interfaceList.add(iface); - } - return interfaceList; - } - - /** - * Returns true if given interface was configured as restricted (doesn't have - * NET_CAPABILITY_NOT_RESTRICTED) capability. Otherwise, returns false. - */ - boolean isRestrictedInterface(String iface) { - final NetworkCapabilities nc = mNetworkCapabilities.get(iface); - return nc != null && !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); - } - - void addListener(IEthernetServiceListener listener, boolean canUseRestrictedNetworks) { - mHandler.post(() -> { - if (!mListeners.register(listener, new ListenerInfo(canUseRestrictedNetworks))) { - // Remote process has already died - return; - } - for (String iface : getInterfaces(canUseRestrictedNetworks)) { - unicastInterfaceStateChange(listener, iface); - } - - unicastEthernetStateChange(listener, mEthernetState); - }); - } - - void removeListener(IEthernetServiceListener listener) { - mHandler.post(() -> mListeners.unregister(listener)); - } - - public void setIncludeTestInterfaces(boolean include) { - mHandler.post(() -> { - mIncludeTestInterfaces = include; - updateIfaceMatchRegexp(); - mHandler.post(() -> trackAvailableInterfaces()); - }); - } - - public void requestTetheredInterface(ITetheredInterfaceCallback callback) { - mHandler.post(() -> { - if (!mTetheredInterfaceRequests.register(callback)) { - // Remote process has already died - return; - } - if (mDefaultInterfaceMode == INTERFACE_MODE_SERVER) { - if (mTetheredInterfaceWasAvailable) { - notifyTetheredInterfaceAvailable(callback, mDefaultInterface); - } - return; - } - - setDefaultInterfaceMode(INTERFACE_MODE_SERVER); - }); - } - - public void releaseTetheredInterface(ITetheredInterfaceCallback callback) { - mHandler.post(() -> { - mTetheredInterfaceRequests.unregister(callback); - maybeUntetherDefaultInterface(); - }); - } - - private void notifyTetheredInterfaceAvailable(ITetheredInterfaceCallback cb, String iface) { - try { - cb.onAvailable(iface); - } catch (RemoteException e) { - Log.e(TAG, "Error sending tethered interface available callback", e); - } - } - - private void notifyTetheredInterfaceUnavailable(ITetheredInterfaceCallback cb) { - try { - cb.onUnavailable(); - } catch (RemoteException e) { - Log.e(TAG, "Error sending tethered interface available callback", e); - } - } - - private void maybeUntetherDefaultInterface() { - if (mTetheredInterfaceRequests.getRegisteredCallbackCount() > 0) return; - if (mDefaultInterfaceMode == INTERFACE_MODE_CLIENT) return; - setDefaultInterfaceMode(INTERFACE_MODE_CLIENT); - } - - private void setDefaultInterfaceMode(int mode) { - Log.d(TAG, "Setting default interface mode to " + mode); - mDefaultInterfaceMode = mode; - if (mDefaultInterface != null) { - removeInterface(mDefaultInterface); - addInterface(mDefaultInterface); - } - } - - private int getInterfaceRole(final String iface) { - if (!mFactory.hasInterface(iface)) return EthernetManager.ROLE_NONE; - final int mode = getInterfaceMode(iface); - return (mode == INTERFACE_MODE_CLIENT) - ? EthernetManager.ROLE_CLIENT - : EthernetManager.ROLE_SERVER; - } - - private int getInterfaceMode(final String iface) { - if (iface.equals(mDefaultInterface)) { - return mDefaultInterfaceMode; - } - return INTERFACE_MODE_CLIENT; - } - - private void removeInterface(String iface) { - mFactory.removeInterface(iface); - maybeUpdateServerModeInterfaceState(iface, false); - } - - private void stopTrackingInterface(String iface) { - removeInterface(iface); - if (iface.equals(mDefaultInterface)) { - mDefaultInterface = null; - } - broadcastInterfaceStateChange(iface); - } - - private void addInterface(String iface) { - InterfaceConfigurationParcel config = null; - // Bring up the interface so we get link status indications. - try { - PermissionUtils.enforceNetworkStackPermission(mContext); - NetdUtils.setInterfaceUp(mNetd, iface); - config = NetdUtils.getInterfaceConfigParcel(mNetd, iface); - } catch (IllegalStateException e) { - // Either the system is crashing or the interface has disappeared. Just ignore the - // error; we haven't modified any state because we only do that if our calls succeed. - Log.e(TAG, "Error upping interface " + iface, e); - } - - if (config == null) { - Log.e(TAG, "Null interface config parcelable for " + iface + ". Bailing out."); - return; - } - - final String hwAddress = config.hwAddr; - - NetworkCapabilities nc = mNetworkCapabilities.get(iface); - if (nc == null) { - // Try to resolve using mac address - nc = mNetworkCapabilities.get(hwAddress); - if (nc == null) { - final boolean isTestIface = iface.matches(TEST_IFACE_REGEXP); - nc = createDefaultNetworkCapabilities(isTestIface); - } - } - - final int mode = getInterfaceMode(iface); - if (mode == INTERFACE_MODE_CLIENT) { - IpConfiguration ipConfiguration = getOrCreateIpConfiguration(iface); - Log.d(TAG, "Tracking interface in client mode: " + iface); - mFactory.addInterface(iface, hwAddress, ipConfiguration, nc); - } else { - maybeUpdateServerModeInterfaceState(iface, true); - } - - // Note: if the interface already has link (e.g., if we crashed and got - // restarted while it was running), we need to fake a link up notification so we - // start configuring it. - if (NetdUtils.hasFlag(config, "running")) { - updateInterfaceState(iface, true); - } - } - - private void updateInterfaceState(String iface, boolean up) { - updateInterfaceState(iface, up, null /* listener */); - } - - private void updateInterfaceState(@NonNull final String iface, final boolean up, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - final int mode = getInterfaceMode(iface); - final boolean factoryLinkStateUpdated = (mode == INTERFACE_MODE_CLIENT) - && mFactory.updateInterfaceLinkState(iface, up, listener); - - if (factoryLinkStateUpdated) { - broadcastInterfaceStateChange(iface); - } - } - - private void maybeUpdateServerModeInterfaceState(String iface, boolean available) { - if (available == mTetheredInterfaceWasAvailable || !iface.equals(mDefaultInterface)) return; - - Log.d(TAG, (available ? "Tracking" : "No longer tracking") - + " interface in server mode: " + iface); - - final int pendingCbs = mTetheredInterfaceRequests.beginBroadcast(); - for (int i = 0; i < pendingCbs; i++) { - ITetheredInterfaceCallback item = mTetheredInterfaceRequests.getBroadcastItem(i); - if (available) { - notifyTetheredInterfaceAvailable(item, iface); - } else { - notifyTetheredInterfaceUnavailable(item); - } - } - mTetheredInterfaceRequests.finishBroadcast(); - mTetheredInterfaceWasAvailable = available; - } - - private void maybeTrackInterface(String iface) { - if (!iface.matches(mIfaceMatch)) { - return; - } - - // If we don't already track this interface, and if this interface matches - // our regex, start tracking it. - if (mFactory.hasInterface(iface) || iface.equals(mDefaultInterface)) { - if (DBG) Log.w(TAG, "Ignoring already-tracked interface " + iface); - return; - } - if (DBG) Log.i(TAG, "maybeTrackInterface: " + iface); - - // TODO: avoid making an interface default if it has configured NetworkCapabilities. - if (mDefaultInterface == null) { - mDefaultInterface = iface; - } - - if (mIpConfigForDefaultInterface != null) { - updateIpConfiguration(iface, mIpConfigForDefaultInterface); - mIpConfigForDefaultInterface = null; - } - - addInterface(iface); - - broadcastInterfaceStateChange(iface); - } - - private void trackAvailableInterfaces() { - try { - final String[] ifaces = mNetd.interfaceGetList(); - for (String iface : ifaces) { - maybeTrackInterface(iface); - } - } catch (RemoteException | ServiceSpecificException e) { - Log.e(TAG, "Could not get list of interfaces " + e); - } - } - - private class InterfaceObserver extends BaseNetdUnsolicitedEventListener { - - @Override - public void onInterfaceLinkStateChanged(String iface, boolean up) { - if (DBG) { - Log.i(TAG, "interfaceLinkStateChanged, iface: " + iface + ", up: " + up); - } - mHandler.post(() -> updateInterfaceState(iface, up)); - } - - @Override - public void onInterfaceAdded(String iface) { - if (DBG) { - Log.i(TAG, "onInterfaceAdded, iface: " + iface); - } - mHandler.post(() -> maybeTrackInterface(iface)); - } - - @Override - public void onInterfaceRemoved(String iface) { - if (DBG) { - Log.i(TAG, "onInterfaceRemoved, iface: " + iface); - } - mHandler.post(() -> stopTrackingInterface(iface)); - } - } - - private static class ListenerInfo { - - boolean canUseRestrictedNetworks = false; - - ListenerInfo(boolean canUseRestrictedNetworks) { - this.canUseRestrictedNetworks = canUseRestrictedNetworks; - } - } - - /** - * Parses an Ethernet interface configuration - * - * @param configString represents an Ethernet configuration in the following format: {@code - * ;[Network Capabilities];[IP config];[Override Transport]} - */ - private void parseEthernetConfig(String configString) { - final EthernetTrackerConfig config = createEthernetTrackerConfig(configString); - NetworkCapabilities nc = createNetworkCapabilities( - !TextUtils.isEmpty(config.mCapabilities) /* clear default capabilities */, - config.mCapabilities, config.mTransport).build(); - mNetworkCapabilities.put(config.mIface, nc); - - if (null != config.mIpConfig) { - IpConfiguration ipConfig = parseStaticIpConfiguration(config.mIpConfig); - mIpConfigurations.put(config.mIface, ipConfig); - } - } - - @VisibleForTesting - static EthernetTrackerConfig createEthernetTrackerConfig(@NonNull final String configString) { - Objects.requireNonNull(configString, "EthernetTrackerConfig requires non-null config"); - return new EthernetTrackerConfig(configString.split(";", /* limit of tokens */ 4)); - } - - private static NetworkCapabilities createDefaultNetworkCapabilities(boolean isTestIface) { - NetworkCapabilities.Builder builder = createNetworkCapabilities( - false /* clear default capabilities */, null, null) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); - - if (isTestIface) { - builder.addTransportType(NetworkCapabilities.TRANSPORT_TEST); - } else { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - } - - return builder.build(); - } - - /** - * Parses a static list of network capabilities - * - * @param clearDefaultCapabilities Indicates whether or not to clear any default capabilities - * @param commaSeparatedCapabilities A comma separated string list of integer encoded - * NetworkCapability.NET_CAPABILITY_* values - * @param overrideTransport A string representing a single integer encoded override transport - * type. Must be one of the NetworkCapability.TRANSPORT_* - * values. TRANSPORT_VPN is not supported. Errors with input - * will cause the override to be ignored. - */ - @VisibleForTesting - static NetworkCapabilities.Builder createNetworkCapabilities( - boolean clearDefaultCapabilities, @Nullable String commaSeparatedCapabilities, - @Nullable String overrideTransport) { - - final NetworkCapabilities.Builder builder = clearDefaultCapabilities - ? NetworkCapabilities.Builder.withoutDefaultCapabilities() - : new NetworkCapabilities.Builder(); - - // Determine the transport type. If someone has tried to define an override transport then - // attempt to add it. Since we can only have one override, all errors with it will - // gracefully default back to TRANSPORT_ETHERNET and warn the user. VPN is not allowed as an - // override type. Wifi Aware and LoWPAN are currently unsupported as well. - int transport = NetworkCapabilities.TRANSPORT_ETHERNET; - if (!TextUtils.isEmpty(overrideTransport)) { - try { - int parsedTransport = Integer.valueOf(overrideTransport); - if (parsedTransport == NetworkCapabilities.TRANSPORT_VPN - || parsedTransport == NetworkCapabilities.TRANSPORT_WIFI_AWARE - || parsedTransport == NetworkCapabilities.TRANSPORT_LOWPAN) { - Log.e(TAG, "Override transport '" + parsedTransport + "' is not supported. " - + "Defaulting to TRANSPORT_ETHERNET"); - } else { - transport = parsedTransport; - } - } catch (NumberFormatException nfe) { - Log.e(TAG, "Override transport type '" + overrideTransport + "' " - + "could not be parsed. Defaulting to TRANSPORT_ETHERNET"); - } - } - - // Apply the transport. If the user supplied a valid number that is not a valid transport - // then adding will throw an exception. Default back to TRANSPORT_ETHERNET if that happens - try { - builder.addTransportType(transport); - } catch (IllegalArgumentException iae) { - Log.e(TAG, transport + " is not a valid NetworkCapability.TRANSPORT_* value. " - + "Defaulting to TRANSPORT_ETHERNET"); - builder.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET); - } - - builder.setLinkUpstreamBandwidthKbps(100 * 1000); - builder.setLinkDownstreamBandwidthKbps(100 * 1000); - - if (!TextUtils.isEmpty(commaSeparatedCapabilities)) { - for (String strNetworkCapability : commaSeparatedCapabilities.split(",")) { - if (!TextUtils.isEmpty(strNetworkCapability)) { - try { - builder.addCapability(Integer.valueOf(strNetworkCapability)); - } catch (NumberFormatException nfe) { - Log.e(TAG, "Capability '" + strNetworkCapability + "' could not be parsed"); - } catch (IllegalArgumentException iae) { - Log.e(TAG, strNetworkCapability + " is not a valid " - + "NetworkCapability.NET_CAPABILITY_* value"); - } - } - } - } - // Ethernet networks have no way to update the following capabilities, so they always - // have them. - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); - - return builder; - } - - /** - * Parses static IP configuration. - * - * @param staticIpConfig represents static IP configuration in the following format: {@code - * ip= gateway= dns= - * domains=} - */ - @VisibleForTesting - static IpConfiguration parseStaticIpConfiguration(String staticIpConfig) { - final StaticIpConfiguration.Builder staticIpConfigBuilder = - new StaticIpConfiguration.Builder(); - - for (String keyValueAsString : staticIpConfig.trim().split(" ")) { - if (TextUtils.isEmpty(keyValueAsString)) continue; - - String[] pair = keyValueAsString.split("="); - if (pair.length != 2) { - throw new IllegalArgumentException("Unexpected token: " + keyValueAsString - + " in " + staticIpConfig); - } - - String key = pair[0]; - String value = pair[1]; - - switch (key) { - case "ip": - staticIpConfigBuilder.setIpAddress(new LinkAddress(value)); - break; - case "domains": - staticIpConfigBuilder.setDomains(value); - break; - case "gateway": - staticIpConfigBuilder.setGateway(InetAddress.parseNumericAddress(value)); - break; - case "dns": { - ArrayList dnsAddresses = new ArrayList<>(); - for (String address: value.split(",")) { - dnsAddresses.add(InetAddress.parseNumericAddress(address)); - } - staticIpConfigBuilder.setDnsServers(dnsAddresses); - break; - } - default : { - throw new IllegalArgumentException("Unexpected key: " + key - + " in " + staticIpConfig); - } - } - } - return createIpConfiguration(staticIpConfigBuilder.build()); - } - - private static IpConfiguration createIpConfiguration( - @NonNull final StaticIpConfiguration staticIpConfig) { - return new IpConfiguration.Builder().setStaticIpConfiguration(staticIpConfig).build(); - } - - private IpConfiguration getOrCreateIpConfiguration(String iface) { - IpConfiguration ret = mIpConfigurations.get(iface); - if (ret != null) return ret; - ret = new IpConfiguration(); - ret.setIpAssignment(IpAssignment.DHCP); - ret.setProxySettings(ProxySettings.NONE); - return ret; - } - - private void updateIfaceMatchRegexp() { - final String match = mDeps.getInterfaceRegexFromResource(mContext); - mIfaceMatch = mIncludeTestInterfaces - ? "(" + match + "|" + TEST_IFACE_REGEXP + ")" - : match; - Log.d(TAG, "Interface match regexp set to '" + mIfaceMatch + "'"); - } - - /** - * Validate if a given interface is valid for testing. - * - * @param iface the name of the interface to validate. - * @return {@code true} if test interfaces are enabled and the given {@code iface} has a test - * interface prefix, {@code false} otherwise. - */ - public boolean isValidTestInterface(@NonNull final String iface) { - return mIncludeTestInterfaces && iface.matches(TEST_IFACE_REGEXP); - } - - private void postAndWaitForRunnable(Runnable r) { - final ConditionVariable cv = new ConditionVariable(); - if (mHandler.post(() -> { - r.run(); - cv.open(); - })) { - cv.block(2000L); - } - } - - @VisibleForTesting(visibility = PACKAGE) - protected void setEthernetEnabled(boolean enabled) { - mHandler.post(() -> { - int newState = enabled ? ETHERNET_STATE_ENABLED : ETHERNET_STATE_DISABLED; - if (mEthernetState == newState) return; - - mEthernetState = newState; - - if (enabled) { - trackAvailableInterfaces(); - } else { - // TODO: maybe also disable server mode interface as well. - untrackFactoryInterfaces(); - } - broadcastEthernetStateChange(mEthernetState); - }); - } - - private void untrackFactoryInterfaces() { - for (String iface : mFactory.getAvailableInterfaces(true /* includeRestricted */)) { - stopTrackingInterface(iface); - } - } - - private void unicastEthernetStateChange(@NonNull IEthernetServiceListener listener, - int state) { - ensureRunningOnEthernetServiceThread(); - try { - listener.onEthernetStateChanged(state); - } catch (RemoteException e) { - // Do nothing here. - } - } - - private void broadcastEthernetStateChange(int state) { - ensureRunningOnEthernetServiceThread(); - final int n = mListeners.beginBroadcast(); - for (int i = 0; i < n; i++) { - try { - mListeners.getBroadcastItem(i).onEthernetStateChanged(state); - } catch (RemoteException e) { - // Do nothing here. - } - } - mListeners.finishBroadcast(); - } - - void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { - postAndWaitForRunnable(() -> { - pw.println(getClass().getSimpleName()); - pw.println("Ethernet interface name filter: " + mIfaceMatch); - pw.println("Default interface: " + mDefaultInterface); - pw.println("Default interface mode: " + mDefaultInterfaceMode); - pw.println("Tethered interface requests: " - + mTetheredInterfaceRequests.getRegisteredCallbackCount()); - pw.println("Listeners: " + mListeners.getRegisteredCallbackCount()); - pw.println("IP Configurations:"); - pw.increaseIndent(); - for (String iface : mIpConfigurations.keySet()) { - pw.println(iface + ": " + mIpConfigurations.get(iface)); - } - pw.decreaseIndent(); - pw.println(); - - pw.println("Network Capabilities:"); - pw.increaseIndent(); - for (String iface : mNetworkCapabilities.keySet()) { - pw.println(iface + ": " + mNetworkCapabilities.get(iface)); - } - pw.decreaseIndent(); - pw.println(); - - mFactory.dump(fd, pw, args); - }); - } - - @VisibleForTesting - static class EthernetTrackerConfig { - 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"); - 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/Android.bp b/tests/Android.bp deleted file mode 100644 index 6cfebdc..0000000 --- a/tests/Android.bp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (C) 2018 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 { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -// TODO: merge the tests into service-connectivity tests after -// ethernet service migration completes. So far just import the -// ethernet service source to fix the dependencies. -android_test { - name: "EthernetServiceTests", - - srcs: [ - ":ethernet-service-updatable-sources", - ":services.connectivity-ethernet-sources", - "java/**/*.java", - ], - - certificate: "platform", - platform_apis: true, - - libs: [ - "android.test.runner", - "android.test.base", - "android.test.mock", - "framework-connectivity.impl", - "framework-connectivity-t.impl", - "ServiceConnectivityResources", - ], - - static_libs: [ - "androidx.test.rules", - "frameworks-base-testutils", - "mockito-target-minus-junit4", - "net-tests-utils", - "services.core", - "services.net", - ], - test_suites: ["general-tests"], -} diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml deleted file mode 100644 index cd875b0..0000000 --- a/tests/AndroidManifest.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - diff --git a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java deleted file mode 100644 index 4d3e4d3..0000000 --- a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java +++ /dev/null @@ -1,783 +0,0 @@ -/* - * 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.assertNotSame; -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.EthernetNetworkManagementException; -import android.net.INetworkInterfaceOutcomeReceiver; -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.connectivity.resources.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 INetworkInterfaceOutcomeReceiver 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(mDeps.getTcpBufferSizesFromResource(eq(mContext))).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 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(); - assertEquals(listener.expectOnResult(), TEST_IFACE); - } - - @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(); - assertEquals(listener.expectOnResult(), TEST_IFACE); - } - - @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()); - assertEquals(listener.expectOnResult(), TEST_IFACE); - } - - @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(); - listener.expectOnErrorWithMessage("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(); - listener.expectOnErrorWithMessage("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()); - } - - private IpClientCallbacks getStaleIpClientCallbacks() throws Exception { - createAndVerifyProvisionedInterface(TEST_IFACE); - final IpClientCallbacks staleIpClientCallbacks = mIpClientCallbacks; - mNetFactory.removeInterface(TEST_IFACE); - verifyStop(); - assertNotSame(mIpClientCallbacks, staleIpClientCallbacks); - return staleIpClientCallbacks; - } - - @Test - public void testIgnoreOnIpLayerStartedCallbackForStaleCallback() throws Exception { - initEthernetNetworkFactory(); - final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); - - staleIpClientCallbacks.onProvisioningSuccess(new LinkProperties()); - mLooper.dispatchAll(); - - verify(mIpClient, never()).startProvisioning(any()); - verify(mNetworkAgent, never()).register(); - } - - @Test - public void testIgnoreOnIpLayerStoppedCallbackForStaleCallback() throws Exception { - initEthernetNetworkFactory(); - when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams); - final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); - - staleIpClientCallbacks.onProvisioningFailure(new LinkProperties()); - mLooper.dispatchAll(); - - verify(mIpClient, never()).startProvisioning(any()); - } - - @Test - public void testIgnoreLinkPropertiesCallbackForStaleCallback() throws Exception { - initEthernetNetworkFactory(); - final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); - final LinkProperties lp = new LinkProperties(); - - staleIpClientCallbacks.onLinkPropertiesChange(lp); - mLooper.dispatchAll(); - - verify(mNetworkAgent, never()).sendLinkPropertiesImpl(eq(lp)); - } - - @Test - public void testIgnoreNeighborLossCallbackForStaleCallback() throws Exception { - initEthernetNetworkFactory(); - final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); - - staleIpClientCallbacks.onReachabilityLost("Neighbor Lost"); - mLooper.dispatchAll(); - - 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 INetworkInterfaceOutcomeReceiver { - private final CompletableFuture mResult = new CompletableFuture<>(); - private final CompletableFuture mError = - new CompletableFuture<>(); - - @Override - public void onResult(@NonNull String iface) { - mResult.complete(iface); - } - - @Override - public void onError(@NonNull EthernetNetworkManagementException exception) { - mError.complete(exception); - } - - String expectOnResult() throws Exception { - return mResult.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); - } - - EthernetNetworkManagementException expectOnError() throws Exception { - return mError.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); - } - - void expectOnErrorWithMessage(String msg) throws Exception { - assertTrue(expectOnError().getMessage().contains(msg)); - } - - @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(); - - assertEquals(listener.expectOnResult(), TEST_IFACE); - } - - @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(); - }); - - assertEquals(successfulListener.expectOnResult(), TEST_IFACE); - } - - 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(); - - failedListener.expectOnErrorWithMessage("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(); - - assertEquals(listener.expectOnResult(), TEST_IFACE); - 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(); - listener.expectOnErrorWithMessage("can't be updated as it is not available"); - } - - @Test - public void testUpdateInterfaceWithNullIpConfiguration() throws Exception { - initEthernetNetworkFactory(); - createAndVerifyProvisionedInterface(TEST_IFACE); - - final IpConfiguration initialIpConfig = createStaticIpConfig(); - mNetFactory.updateInterface(TEST_IFACE, initialIpConfig, null /*capabilities*/, - null /*listener*/); - triggerOnProvisioningSuccess(); - verifyRestart(initialIpConfig); - - // TODO: have verifyXyz functions clear invocations. - clearInvocations(mDeps); - clearInvocations(mIpClient); - clearInvocations(mNetworkAgent); - - - // verify that sending a null ipConfig does not update the current ipConfig. - mNetFactory.updateInterface(TEST_IFACE, null /*ipConfig*/, null /*capabilities*/, - null /*listener*/); - triggerOnProvisioningSuccess(); - verifyRestart(initialIpConfig); - } -} diff --git a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java deleted file mode 100644 index dd1f1ed..0000000 --- a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright (C) 2021 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 android.net.NetworkCapabilities.TRANSPORT_TEST; - -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.fail; - -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.Manifest; -import android.annotation.NonNull; -import android.content.Context; -import android.content.pm.PackageManager; -import android.net.INetworkInterfaceOutcomeReceiver; -import android.net.EthernetNetworkUpdateRequest; -import android.net.IpConfiguration; -import android.net.NetworkCapabilities; -import android.os.Handler; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class EthernetServiceImplTest { - private static final String TEST_IFACE = "test123"; - private static final EthernetNetworkUpdateRequest UPDATE_REQUEST = - new EthernetNetworkUpdateRequest.Builder() - .setIpConfiguration(new IpConfiguration()) - .setNetworkCapabilities(new NetworkCapabilities.Builder().build()) - .build(); - private static final EthernetNetworkUpdateRequest UPDATE_REQUEST_WITHOUT_CAPABILITIES = - new EthernetNetworkUpdateRequest.Builder() - .setIpConfiguration(new IpConfiguration()) - .build(); - private static final EthernetNetworkUpdateRequest UPDATE_REQUEST_WITHOUT_IP_CONFIG = - new EthernetNetworkUpdateRequest.Builder() - .setNetworkCapabilities(new NetworkCapabilities.Builder().build()) - .build(); - private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; - private EthernetServiceImpl mEthernetServiceImpl; - @Mock private Context mContext; - @Mock private Handler mHandler; - @Mock private EthernetTracker mEthernetTracker; - @Mock private PackageManager mPackageManager; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - doReturn(mPackageManager).when(mContext).getPackageManager(); - mEthernetServiceImpl = new EthernetServiceImpl(mContext, mHandler, mEthernetTracker); - mEthernetServiceImpl.mStarted.set(true); - toggleAutomotiveFeature(true); - shouldTrackIface(TEST_IFACE, true); - } - - private void toggleAutomotiveFeature(final boolean isEnabled) { - doReturn(isEnabled) - .when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); - } - - private void shouldTrackIface(@NonNull final String iface, final boolean shouldTrack) { - doReturn(shouldTrack).when(mEthernetTracker).isTrackingInterface(iface); - } - - @Test - public void testSetConfigurationRejectsWhenEthNotStarted() { - mEthernetServiceImpl.mStarted.set(false); - assertThrows(IllegalStateException.class, () -> { - mEthernetServiceImpl.setConfiguration("" /* iface */, new IpConfiguration()); - }); - } - - @Test - public void testUpdateConfigurationRejectsWhenEthNotStarted() { - mEthernetServiceImpl.mStarted.set(false); - assertThrows(IllegalStateException.class, () -> { - mEthernetServiceImpl.updateConfiguration( - "" /* iface */, UPDATE_REQUEST, null /* listener */); - }); - } - - @Test - public void testConnectNetworkRejectsWhenEthNotStarted() { - mEthernetServiceImpl.mStarted.set(false); - assertThrows(IllegalStateException.class, () -> { - mEthernetServiceImpl.connectNetwork("" /* iface */, null /* listener */); - }); - } - - @Test - public void testDisconnectNetworkRejectsWhenEthNotStarted() { - mEthernetServiceImpl.mStarted.set(false); - assertThrows(IllegalStateException.class, () -> { - mEthernetServiceImpl.disconnectNetwork("" /* iface */, null /* listener */); - }); - } - - @Test - public void testUpdateConfigurationRejectsNullIface() { - assertThrows(NullPointerException.class, () -> { - mEthernetServiceImpl.updateConfiguration(null, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - @Test - public void testConnectNetworkRejectsNullIface() { - assertThrows(NullPointerException.class, () -> { - mEthernetServiceImpl.connectNetwork(null /* iface */, NULL_LISTENER); - }); - } - - @Test - public void testDisconnectNetworkRejectsNullIface() { - assertThrows(NullPointerException.class, () -> { - mEthernetServiceImpl.disconnectNetwork(null /* iface */, NULL_LISTENER); - }); - } - - @Test - public void testUpdateConfigurationWithCapabilitiesRejectsWithoutAutomotiveFeature() { - toggleAutomotiveFeature(false); - assertThrows(UnsupportedOperationException.class, () -> { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - @Test - public void testUpdateConfigurationWithCapabilitiesWithAutomotiveFeature() { - toggleAutomotiveFeature(false); - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST_WITHOUT_CAPABILITIES, - NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration(eq(TEST_IFACE), - eq(UPDATE_REQUEST_WITHOUT_CAPABILITIES.getIpConfiguration()), - eq(UPDATE_REQUEST_WITHOUT_CAPABILITIES.getNetworkCapabilities()), isNull()); - } - - @Test - public void testConnectNetworkRejectsWithoutAutomotiveFeature() { - toggleAutomotiveFeature(false); - assertThrows(UnsupportedOperationException.class, () -> { - mEthernetServiceImpl.connectNetwork("" /* iface */, NULL_LISTENER); - }); - } - - @Test - public void testDisconnectNetworkRejectsWithoutAutomotiveFeature() { - toggleAutomotiveFeature(false); - assertThrows(UnsupportedOperationException.class, () -> { - mEthernetServiceImpl.disconnectNetwork("" /* iface */, NULL_LISTENER); - }); - } - - private void denyManageEthPermission() { - doThrow(new SecurityException("")).when(mContext) - .enforceCallingOrSelfPermission( - eq(Manifest.permission.MANAGE_ETHERNET_NETWORKS), anyString()); - } - - private void denyManageTestNetworksPermission() { - doThrow(new SecurityException("")).when(mContext) - .enforceCallingOrSelfPermission( - eq(Manifest.permission.MANAGE_TEST_NETWORKS), anyString()); - } - - @Test - public void testUpdateConfigurationRejectsWithoutManageEthPermission() { - denyManageEthPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - @Test - public void testConnectNetworkRejectsWithoutManageEthPermission() { - denyManageEthPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER); - }); - } - - @Test - public void testDisconnectNetworkRejectsWithoutManageEthPermission() { - denyManageEthPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - }); - } - - private void enableTestInterface() { - when(mEthernetTracker.isValidTestInterface(eq(TEST_IFACE))).thenReturn(true); - } - - @Test - public void testUpdateConfigurationRejectsTestRequestWithoutTestPermission() { - enableTestInterface(); - denyManageTestNetworksPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - @Test - public void testConnectNetworkRejectsTestRequestWithoutTestPermission() { - enableTestInterface(); - denyManageTestNetworksPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER); - }); - } - - @Test - public void testDisconnectNetworkRejectsTestRequestWithoutTestPermission() { - enableTestInterface(); - denyManageTestNetworksPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - }); - } - - @Test - public void testUpdateConfiguration() { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration( - eq(TEST_IFACE), - eq(UPDATE_REQUEST.getIpConfiguration()), - eq(UPDATE_REQUEST.getNetworkCapabilities()), eq(NULL_LISTENER)); - } - - @Test - public void testConnectNetwork() { - mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER); - verify(mEthernetTracker).connectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); - } - - @Test - public void testDisconnectNetwork() { - mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - verify(mEthernetTracker).disconnectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); - } - - @Test - public void testUpdateConfigurationAcceptsTestRequestWithNullCapabilities() { - enableTestInterface(); - final EthernetNetworkUpdateRequest request = - new EthernetNetworkUpdateRequest - .Builder() - .setIpConfiguration(new IpConfiguration()).build(); - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, request, NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration(eq(TEST_IFACE), - eq(request.getIpConfiguration()), - eq(request.getNetworkCapabilities()), isNull()); - } - - @Test - public void testUpdateConfigurationAcceptsRequestWithNullIpConfiguration() { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST_WITHOUT_IP_CONFIG, - NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration(eq(TEST_IFACE), - eq(UPDATE_REQUEST_WITHOUT_IP_CONFIG.getIpConfiguration()), - eq(UPDATE_REQUEST_WITHOUT_IP_CONFIG.getNetworkCapabilities()), isNull()); - } - - @Test - public void testUpdateConfigurationRejectsInvalidTestRequest() { - enableTestInterface(); - assertThrows(IllegalArgumentException.class, () -> { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - private EthernetNetworkUpdateRequest createTestNetworkUpdateRequest() { - final NetworkCapabilities nc = new NetworkCapabilities - .Builder(UPDATE_REQUEST.getNetworkCapabilities()) - .addTransportType(TRANSPORT_TEST).build(); - - return new EthernetNetworkUpdateRequest - .Builder(UPDATE_REQUEST) - .setNetworkCapabilities(nc).build(); - } - - @Test - public void testUpdateConfigurationForTestRequestDoesNotRequireAutoOrEthernetPermission() { - enableTestInterface(); - toggleAutomotiveFeature(false); - denyManageEthPermission(); - final EthernetNetworkUpdateRequest request = createTestNetworkUpdateRequest(); - - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, request, NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration( - eq(TEST_IFACE), - eq(request.getIpConfiguration()), - eq(request.getNetworkCapabilities()), eq(NULL_LISTENER)); - } - - @Test - public void testConnectNetworkForTestRequestDoesNotRequireAutoOrNetPermission() { - enableTestInterface(); - toggleAutomotiveFeature(false); - denyManageEthPermission(); - - mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER); - verify(mEthernetTracker).connectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); - } - - @Test - public void testDisconnectNetworkForTestRequestDoesNotRequireAutoOrNetPermission() { - enableTestInterface(); - toggleAutomotiveFeature(false); - denyManageEthPermission(); - - mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - verify(mEthernetTracker).disconnectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); - } - - private void denyPermissions(String... permissions) { - for (String permission: permissions) { - doReturn(PackageManager.PERMISSION_DENIED).when(mContext) - .checkCallingOrSelfPermission(eq(permission)); - } - } - - @Test - public void testSetEthernetEnabled() { - denyPermissions(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); - mEthernetServiceImpl.setEthernetEnabled(true); - verify(mEthernetTracker).setEthernetEnabled(true); - reset(mEthernetTracker); - - denyPermissions(Manifest.permission.NETWORK_STACK); - mEthernetServiceImpl.setEthernetEnabled(false); - verify(mEthernetTracker).setEthernetEnabled(false); - reset(mEthernetTracker); - - denyPermissions(Manifest.permission.NETWORK_SETTINGS); - try { - mEthernetServiceImpl.setEthernetEnabled(true); - fail("Should get SecurityException"); - } catch (SecurityException e) { } - verify(mEthernetTracker, never()).setEthernetEnabled(false); - } -} diff --git a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java deleted file mode 100644 index b1831c4..0000000 --- a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Copyright (C) 2018 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 android.net.TestNetworkManager.TEST_TAP_PREFIX; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.content.res.Resources; -import android.net.EthernetManager; -import android.net.InetAddresses; -import android.net.INetworkInterfaceOutcomeReceiver; -import android.net.IEthernetServiceListener; -import android.net.INetd; -import android.net.IpConfiguration; -import android.net.IpConfiguration.IpAssignment; -import android.net.IpConfiguration.ProxySettings; -import android.net.InterfaceConfigurationParcel; -import android.net.LinkAddress; -import android.net.NetworkCapabilities; -import android.net.StaticIpConfiguration; -import android.os.HandlerThread; -import android.os.RemoteException; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.connectivity.resources.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; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class EthernetTrackerTest { - private static final String TEST_IFACE = "test123"; - private static final int TIMEOUT_MS = 1_000; - private static final String THREAD_NAME = "EthernetServiceThread"; - private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; - private EthernetTracker tracker; - private HandlerThread mHandlerThread; - @Mock private Context mContext; - @Mock private EthernetNetworkFactory mFactory; - @Mock private INetd mNetd; - @Mock private EthernetTracker.Dependencies mDeps; - - @Before - public void setUp() throws RemoteException { - MockitoAnnotations.initMocks(this); - initMockResources(); - when(mFactory.updateInterfaceLinkState(anyString(), anyBoolean(), any())).thenReturn(false); - when(mNetd.interfaceGetList()).thenReturn(new String[0]); - mHandlerThread = new HandlerThread(THREAD_NAME); - mHandlerThread.start(); - tracker = new EthernetTracker(mContext, mHandlerThread.getThreadHandler(), mFactory, mNetd, - mDeps); - } - - @After - public void cleanUp() { - mHandlerThread.quitSafely(); - } - - private void initMockResources() { - when(mDeps.getInterfaceRegexFromResource(eq(mContext))).thenReturn(""); - when(mDeps.getInterfaceConfigFromResource(eq(mContext))).thenReturn(new String[0]); - } - - private void waitForIdle() { - HandlerUtils.waitForIdle(mHandlerThread, TIMEOUT_MS); - } - - /** - * Test: Creation of various valid static IP configurations - */ - @Test - public void createStaticIpConfiguration() { - // Empty gives default StaticIPConfiguration object - assertStaticConfiguration(new StaticIpConfiguration(), ""); - - // Setting only the IP address properly cascades and assumes defaults - assertStaticConfiguration(new StaticIpConfiguration.Builder() - .setIpAddress(new LinkAddress("192.0.2.10/24")).build(), "ip=192.0.2.10/24"); - - final ArrayList dnsAddresses = new ArrayList<>(); - dnsAddresses.add(InetAddresses.parseNumericAddress("4.4.4.4")); - dnsAddresses.add(InetAddresses.parseNumericAddress("8.8.8.8")); - // Setting other fields properly cascades them - assertStaticConfiguration(new StaticIpConfiguration.Builder() - .setIpAddress(new LinkAddress("192.0.2.10/24")) - .setDnsServers(dnsAddresses) - .setGateway(InetAddresses.parseNumericAddress("192.0.2.1")) - .setDomains("android").build(), - "ip=192.0.2.10/24 dns=4.4.4.4,8.8.8.8 gateway=192.0.2.1 domains=android"); - - // Verify order doesn't matter - assertStaticConfiguration(new StaticIpConfiguration.Builder() - .setIpAddress(new LinkAddress("192.0.2.10/24")) - .setDnsServers(dnsAddresses) - .setGateway(InetAddresses.parseNumericAddress("192.0.2.1")) - .setDomains("android").build(), - "domains=android ip=192.0.2.10/24 gateway=192.0.2.1 dns=4.4.4.4,8.8.8.8 "); - } - - /** - * Test: Attempt creation of various bad static IP configurations - */ - @Test - public void createStaticIpConfiguration_Bad() { - assertStaticConfigurationFails("ip=192.0.2.1/24 gateway= blah=20.20.20.20"); // Unknown key - assertStaticConfigurationFails("ip=192.0.2.1"); // mask is missing - assertStaticConfigurationFails("ip=a.b.c"); // not a valid ip address - assertStaticConfigurationFails("dns=4.4.4.4,1.2.3.A"); // not valid ip address in dns - assertStaticConfigurationFails("="); // Key and value is empty - assertStaticConfigurationFails("ip="); // Value is empty - assertStaticConfigurationFails("ip=192.0.2.1/24 gateway="); // Gateway is empty - } - - private void assertStaticConfigurationFails(String config) { - try { - EthernetTracker.parseStaticIpConfiguration(config); - fail("Expected to fail: " + config); - } catch (IllegalArgumentException e) { - // expected - } - } - - private void assertStaticConfiguration(StaticIpConfiguration expectedStaticIpConfig, - String configAsString) { - final IpConfiguration expectedIpConfiguration = new IpConfiguration(); - expectedIpConfiguration.setIpAssignment(IpAssignment.STATIC); - expectedIpConfiguration.setProxySettings(ProxySettings.NONE); - expectedIpConfiguration.setStaticIpConfiguration(expectedStaticIpConfig); - - assertEquals(expectedIpConfiguration, - EthernetTracker.parseStaticIpConfiguration(configAsString)); - } - - private NetworkCapabilities.Builder makeEthernetCapabilitiesBuilder(boolean clearAll) { - final NetworkCapabilities.Builder builder = - clearAll ? NetworkCapabilities.Builder.withoutDefaultCapabilities() - : new NetworkCapabilities.Builder(); - return builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); - } - - /** - * Test: Attempt to create a capabilties with various valid sets of capabilities/transports - */ - @Test - public void createNetworkCapabilities() { - - // Particularly common expected results - NetworkCapabilities defaultEthernetCleared = - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .build(); - - NetworkCapabilities ethernetClearedWithCommonCaps = - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .addCapability(12) - .addCapability(13) - .addCapability(14) - .addCapability(15) - .build(); - - // Empty capabilities and transports lists with a "please clear defaults" should - // yield an empty capabilities set with TRANPORT_ETHERNET - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", ""); - - // Empty capabilities and transports without the clear defaults flag should return the - // default capabilities set with TRANSPORT_ETHERNET - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(false /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .build(), - false, "", ""); - - // A list of capabilities without the clear defaults flag should return the default - // capabilities, mixed with the desired capabilities, and TRANSPORT_ETHERNET - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(false /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .addCapability(11) - .addCapability(12) - .build(), - false, "11,12", ""); - - // Adding a list of capabilities with a clear defaults will leave exactly those capabilities - // with a default TRANSPORT_ETHERNET since no overrides are specified - assertParsedNetworkCapabilities(ethernetClearedWithCommonCaps, true, "12,13,14,15", ""); - - // Adding any invalid capabilities to the list will cause them to be ignored - assertParsedNetworkCapabilities(ethernetClearedWithCommonCaps, true, "12,13,14,15,65,73", ""); - assertParsedNetworkCapabilities(ethernetClearedWithCommonCaps, true, "12,13,14,15,abcdefg", ""); - - // Adding a valid override transport will remove the default TRANSPORT_ETHERNET transport - // and apply only the override to the capabiltities object - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(0) - .build(), - true, "", "0"); - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(1) - .build(), - true, "", "1"); - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(2) - .build(), - true, "", "2"); - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(3) - .build(), - true, "", "3"); - - // "4" is TRANSPORT_VPN, which is unsupported. Should default back to TRANPORT_ETHERNET - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", "4"); - - // "5" is TRANSPORT_WIFI_AWARE, which is currently supported due to no legacy TYPE_NONE - // conversion. When that becomes available, this test must be updated - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", "5"); - - // "6" is TRANSPORT_LOWPAN, which is currently supported due to no legacy TYPE_NONE - // conversion. When that becomes available, this test must be updated - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", "6"); - - // Adding an invalid override transport will leave the transport as TRANSPORT_ETHERNET - assertParsedNetworkCapabilities(defaultEthernetCleared,true, "", "100"); - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", "abcdefg"); - - // Ensure the adding of both capabilities and transports work - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addCapability(12) - .addCapability(13) - .addCapability(14) - .addCapability(15) - .addTransportType(3) - .build(), - true, "12,13,14,15", "3"); - - // Ensure order does not matter for capability list - assertParsedNetworkCapabilities(ethernetClearedWithCommonCaps, true, "13,12,15,14", ""); - } - - private void assertParsedNetworkCapabilities(NetworkCapabilities expectedNetworkCapabilities, - boolean clearCapabilties, String configCapabiltiies,String configTransports) { - assertEquals(expectedNetworkCapabilities, - EthernetTracker.createNetworkCapabilities(clearCapabilties, configCapabiltiies, - configTransports).build()); - } - - @Test - public void testCreateEthernetTrackerConfigReturnsCorrectValue() { - final String capabilities = "2"; - final String ipConfig = "3"; - final String transport = "4"; - final String configString = String.join(";", TEST_IFACE, capabilities, ipConfig, transport); - - final EthernetTracker.EthernetTrackerConfig config = - EthernetTracker.createEthernetTrackerConfig(configString); - - assertEquals(TEST_IFACE, config.mIface); - assertEquals(capabilities, config.mCapabilities); - assertEquals(ipConfig, config.mIpConfig); - assertEquals(transport, config.mTransport); - } - - @Test - public void testCreateEthernetTrackerConfigThrowsNpeWithNullInput() { - assertThrows(NullPointerException.class, - () -> EthernetTracker.createEthernetTrackerConfig(null)); - } - - @Test - public void testUpdateConfiguration() { - final NetworkCapabilities capabilities = new NetworkCapabilities.Builder().build(); - final LinkAddress linkAddr = new LinkAddress("192.0.2.2/25"); - final StaticIpConfiguration staticIpConfig = - new StaticIpConfiguration.Builder().setIpAddress(linkAddr).build(); - final IpConfiguration ipConfig = - new IpConfiguration.Builder().setStaticIpConfiguration(staticIpConfig).build(); - final INetworkInterfaceOutcomeReceiver listener = null; - - tracker.updateConfiguration(TEST_IFACE, ipConfig, capabilities, listener); - waitForIdle(); - - verify(mFactory).updateInterface( - eq(TEST_IFACE), eq(ipConfig), eq(capabilities), eq(listener)); - } - - @Test - public void testConnectNetworkCorrectlyCallsFactory() { - tracker.connectNetwork(TEST_IFACE, NULL_LISTENER); - waitForIdle(); - - verify(mFactory).updateInterfaceLinkState(eq(TEST_IFACE), eq(true /* up */), - eq(NULL_LISTENER)); - } - - @Test - public void testDisconnectNetworkCorrectlyCallsFactory() { - tracker.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - waitForIdle(); - - verify(mFactory).updateInterfaceLinkState(eq(TEST_IFACE), eq(false /* up */), - eq(NULL_LISTENER)); - } - - @Test - public void testIsValidTestInterfaceIsFalseWhenTestInterfacesAreNotIncluded() { - final String validIfaceName = TEST_TAP_PREFIX + "123"; - tracker.setIncludeTestInterfaces(false); - waitForIdle(); - - final boolean isValidTestInterface = tracker.isValidTestInterface(validIfaceName); - - assertFalse(isValidTestInterface); - } - - @Test - public void testIsValidTestInterfaceIsFalseWhenTestInterfaceNameIsInvalid() { - final String invalidIfaceName = "123" + TEST_TAP_PREFIX; - tracker.setIncludeTestInterfaces(true); - waitForIdle(); - - final boolean isValidTestInterface = tracker.isValidTestInterface(invalidIfaceName); - - assertFalse(isValidTestInterface); - } - - @Test - public void testIsValidTestInterfaceIsTrueWhenTestInterfacesIncludedAndValidName() { - final String validIfaceName = TEST_TAP_PREFIX + "123"; - tracker.setIncludeTestInterfaces(true); - waitForIdle(); - - final boolean isValidTestInterface = tracker.isValidTestInterface(validIfaceName); - - assertTrue(isValidTestInterface); - } - - public static class EthernetStateListener extends IEthernetServiceListener.Stub { - @Override - public void onEthernetStateChanged(int state) { } - - @Override - public void onInterfaceStateChanged(String iface, int state, int role, - IpConfiguration configuration) { } - } - - @Test - public void testListenEthernetStateChange() throws Exception { - final String testIface = "testtap123"; - final String testHwAddr = "11:22:33:44:55:66"; - final InterfaceConfigurationParcel ifaceParcel = new InterfaceConfigurationParcel(); - ifaceParcel.ifName = testIface; - ifaceParcel.hwAddr = testHwAddr; - ifaceParcel.flags = new String[] {INetd.IF_STATE_UP}; - - tracker.setIncludeTestInterfaces(true); - waitForIdle(); - - when(mNetd.interfaceGetList()).thenReturn(new String[] {testIface}); - when(mNetd.interfaceGetCfg(eq(testIface))).thenReturn(ifaceParcel); - doReturn(new String[] {testIface}).when(mFactory).getAvailableInterfaces(anyBoolean()); - doReturn(EthernetManager.STATE_LINK_UP).when(mFactory).getInterfaceState(eq(testIface)); - - final EthernetStateListener listener = spy(new EthernetStateListener()); - tracker.addListener(listener, true /* canUseRestrictedNetworks */); - // Check default state. - waitForIdle(); - verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_LINK_UP), - anyInt(), any()); - verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_ENABLED)); - reset(listener); - - doReturn(EthernetManager.STATE_ABSENT).when(mFactory).getInterfaceState(eq(testIface)); - tracker.setEthernetEnabled(false); - waitForIdle(); - verify(mFactory).removeInterface(eq(testIface)); - verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_DISABLED)); - verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_ABSENT), - anyInt(), any()); - reset(listener); - - doReturn(EthernetManager.STATE_LINK_UP).when(mFactory).getInterfaceState(eq(testIface)); - tracker.setEthernetEnabled(true); - waitForIdle(); - verify(mFactory).addInterface(eq(testIface), eq(testHwAddr), any(), any()); - verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_ENABLED)); - verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_LINK_UP), - anyInt(), any()); - } -} -- cgit v1.2.3 From 9fe3c9736406a35421ad8582f7148d34f90ac7ef Mon Sep 17 00:00:00 2001 From: Patrick Rohr Date: Wed, 30 Mar 2022 15:15:34 +0200 Subject: Move f/o/n/ethernet to p/m/Connectivity Test: TH Bug: 222234190 Merged-In: I4dd5a6ccd3294af838abcfc5ca08b76f1c002155 Change-Id: Iea43a76d84002a5a587fa735b692d8b6ce6fd12e --- Android.bp | 32 - TEST_MAPPING | 12 - .../server/ethernet/EthernetConfigStore.java | 88 -- .../server/ethernet/EthernetNetworkAgent.java | 65 -- .../server/ethernet/EthernetNetworkFactory.java | 785 ----------------- .../android/server/ethernet/EthernetService.java | 47 - .../server/ethernet/EthernetServiceImpl.java | 299 ------- .../android/server/ethernet/EthernetTracker.java | 942 --------------------- tests/Android.bp | 53 -- tests/AndroidManifest.xml | 28 - .../ethernet/EthernetNetworkFactoryTest.java | 783 ----------------- .../server/ethernet/EthernetServiceImplTest.java | 372 -------- .../server/ethernet/EthernetTrackerTest.java | 456 ---------- 13 files changed, 3962 deletions(-) delete mode 100644 Android.bp delete mode 100644 TEST_MAPPING delete mode 100644 java/com/android/server/ethernet/EthernetConfigStore.java delete mode 100644 java/com/android/server/ethernet/EthernetNetworkAgent.java delete mode 100644 java/com/android/server/ethernet/EthernetNetworkFactory.java delete mode 100644 java/com/android/server/ethernet/EthernetService.java delete mode 100644 java/com/android/server/ethernet/EthernetServiceImpl.java delete mode 100644 java/com/android/server/ethernet/EthernetTracker.java delete mode 100644 tests/Android.bp delete mode 100644 tests/AndroidManifest.xml delete mode 100644 tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java delete mode 100644 tests/java/com/android/server/ethernet/EthernetServiceImplTest.java delete mode 100644 tests/java/com/android/server/ethernet/EthernetTrackerTest.java diff --git a/Android.bp b/Android.bp deleted file mode 100644 index c429a6f..0000000 --- a/Android.bp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2014 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. - -// Build the java code -// ============================================================ - -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -filegroup { - name: "ethernet-service-updatable-sources", - srcs: [ - "java/**/*.java", - ], - path: "java", - visibility: [ - "//frameworks/opt/net/ethernet/tests", - "//packages/modules/Connectivity/service-t", - ], -} diff --git a/TEST_MAPPING b/TEST_MAPPING deleted file mode 100644 index 24c152b..0000000 --- a/TEST_MAPPING +++ /dev/null @@ -1,12 +0,0 @@ -{ - "presubmit": [ - { - "name": "EthernetServiceTests" - } - ], - "imports": [ - { - "path": "packages/modules/Connectivity" - } - ] -} diff --git a/java/com/android/server/ethernet/EthernetConfigStore.java b/java/com/android/server/ethernet/EthernetConfigStore.java deleted file mode 100644 index 6b623f4..0000000 --- a/java/com/android/server/ethernet/EthernetConfigStore.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2014 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 android.annotation.Nullable; -import android.net.IpConfiguration; -import android.os.Environment; -import android.util.ArrayMap; - -import com.android.server.net.IpConfigStore; - - -/** - * This class provides an API to store and manage Ethernet network configuration. - */ -public class EthernetConfigStore { - private static final String ipConfigFile = Environment.getDataDirectory() + - "/misc/ethernet/ipconfig.txt"; - - private IpConfigStore mStore = new IpConfigStore(); - private ArrayMap mIpConfigurations; - private IpConfiguration mIpConfigurationForDefaultInterface; - private final Object mSync = new Object(); - - public EthernetConfigStore() { - mIpConfigurations = new ArrayMap<>(0); - } - - public void read() { - synchronized (mSync) { - ArrayMap configs = - IpConfigStore.readIpConfigurations(ipConfigFile); - - // This configuration may exist in old file versions when there was only a single active - // Ethernet interface. - if (configs.containsKey("0")) { - mIpConfigurationForDefaultInterface = configs.remove("0"); - } - - mIpConfigurations = configs; - } - } - - public void write(String iface, IpConfiguration config) { - boolean modified; - - synchronized (mSync) { - if (config == null) { - modified = mIpConfigurations.remove(iface) != null; - } else { - IpConfiguration oldConfig = mIpConfigurations.put(iface, config); - modified = !config.equals(oldConfig); - } - - if (modified) { - mStore.writeIpConfigurations(ipConfigFile, mIpConfigurations); - } - } - } - - public ArrayMap getIpConfigurations() { - synchronized (mSync) { - return new ArrayMap<>(mIpConfigurations); - } - } - - @Nullable - public IpConfiguration getIpConfigurationForDefaultInterface() { - synchronized (mSync) { - return mIpConfigurationForDefaultInterface == null - ? null : new IpConfiguration(mIpConfigurationForDefaultInterface); - } - } -} diff --git a/java/com/android/server/ethernet/EthernetNetworkAgent.java b/java/com/android/server/ethernet/EthernetNetworkAgent.java deleted file mode 100644 index 57fbce7..0000000 --- a/java/com/android/server/ethernet/EthernetNetworkAgent.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2021 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 android.content.Context; -import android.net.LinkProperties; -import android.net.NetworkAgent; -import android.net.NetworkAgentConfig; -import android.net.NetworkCapabilities; -import android.net.NetworkProvider; -import android.net.NetworkScore; -import android.os.Looper; -import android.annotation.NonNull; -import android.annotation.Nullable; - -public class EthernetNetworkAgent extends NetworkAgent { - - private static final String TAG = "EthernetNetworkAgent"; - - public interface Callbacks { - void onNetworkUnwanted(); - } - - private final Callbacks mCallbacks; - - EthernetNetworkAgent( - @NonNull Context context, - @NonNull Looper looper, - @NonNull NetworkCapabilities nc, - @NonNull LinkProperties lp, - @NonNull NetworkAgentConfig config, - @Nullable NetworkProvider provider, - @NonNull Callbacks cb) { - super(context, looper, TAG, nc, lp, new NetworkScore.Builder().build(), config, provider); - mCallbacks = cb; - } - - @Override - public void onNetworkUnwanted() { - mCallbacks.onNetworkUnwanted(); - } - - // sendLinkProperties is final in NetworkAgent, so it cannot be mocked. - public void sendLinkPropertiesImpl(LinkProperties lp) { - sendLinkProperties(lp); - } - - public Callbacks getCallbacks() { - return mCallbacks; - } -} diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java deleted file mode 100644 index d910629..0000000 --- a/java/com/android/server/ethernet/EthernetNetworkFactory.java +++ /dev/null @@ -1,785 +0,0 @@ -/* - * Copyright (C) 2014 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 android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.content.res.Resources; -import android.net.ConnectivityManager; -import android.net.ConnectivityResources; -import android.net.EthernetManager; -import android.net.EthernetNetworkSpecifier; -import android.net.EthernetNetworkManagementException; -import android.net.INetworkInterfaceOutcomeReceiver; -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; -import android.net.NetworkProvider; -import android.net.NetworkRequest; -import android.net.NetworkSpecifier; -import android.net.ip.IIpClient; -import android.net.ip.IpClientCallbacks; -import android.net.ip.IpClientManager; -import android.net.ip.IpClientUtil; -import android.net.shared.ProvisioningConfiguration; -import android.os.ConditionVariable; -import android.os.Handler; -import android.os.Looper; -import android.os.RemoteException; -import android.text.TextUtils; -import android.util.AndroidRuntimeException; -import android.util.Log; -import android.util.SparseArray; - -import com.android.connectivity.resources.R; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; -import com.android.net.module.util.InterfaceParams; - -import java.io.FileDescriptor; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; - -/** - * {@link NetworkFactory} that represents Ethernet networks. - * - * This class reports a static network score of 70 when it is tracking an interface and that - * interface's link is up, and a score of 0 otherwise. - */ -public class EthernetNetworkFactory extends NetworkFactory { - private final static String TAG = EthernetNetworkFactory.class.getSimpleName(); - final static boolean DBG = true; - - private final static int NETWORK_SCORE = 70; - private static final String NETWORK_TYPE = "Ethernet"; - private static final String LEGACY_TCP_BUFFER_SIZES = - "524288,1048576,3145728,524288,1048576,2097152"; - - private final ConcurrentHashMap mTrackingInterfaces = - new ConcurrentHashMap<>(); - private final Handler mHandler; - private final Context mContext; - final Dependencies mDeps; - - public static class Dependencies { - public void makeIpClient(Context context, String iface, IpClientCallbacks callbacks) { - IpClientUtil.makeIpClient(context, iface, callbacks); - } - - public IpClientManager makeIpClientManager(@NonNull final IIpClient ipClient) { - return new IpClientManager(ipClient, TAG); - } - - public EthernetNetworkAgent makeEthernetNetworkAgent(Context context, Looper looper, - NetworkCapabilities nc, LinkProperties lp, NetworkAgentConfig config, - NetworkProvider provider, EthernetNetworkAgent.Callbacks cb) { - return new EthernetNetworkAgent(context, looper, nc, lp, config, provider, cb); - } - - public InterfaceParams getNetworkInterfaceByName(String name) { - return InterfaceParams.getByName(name); - } - - // TODO: remove legacy resource fallback after migrating its overlays. - private String getPlatformTcpBufferSizes(Context context) { - final Resources r = context.getResources(); - final int resId = r.getIdentifier("config_ethernet_tcp_buffers", "string", - context.getPackageName()); - return r.getString(resId); - } - - public String getTcpBufferSizesFromResource(Context context) { - final String tcpBufferSizes; - final String platformTcpBufferSizes = getPlatformTcpBufferSizes(context); - if (!LEGACY_TCP_BUFFER_SIZES.equals(platformTcpBufferSizes)) { - // Platform resource is not the historical default: use the overlay. - tcpBufferSizes = platformTcpBufferSizes; - } else { - final ConnectivityResources resources = new ConnectivityResources(context); - tcpBufferSizes = resources.get().getString(R.string.config_ethernet_tcp_buffers); - } - return tcpBufferSizes; - } - } - - public static class ConfigurationException extends AndroidRuntimeException { - public ConfigurationException(String msg) { - super(msg); - } - } - - public EthernetNetworkFactory(Handler handler, Context context) { - this(handler, context, new Dependencies()); - } - - @VisibleForTesting - EthernetNetworkFactory(Handler handler, Context context, Dependencies deps) { - super(handler.getLooper(), context, NETWORK_TYPE, createDefaultNetworkCapabilities()); - - mHandler = handler; - mContext = context; - mDeps = deps; - - setScoreFilter(NETWORK_SCORE); - } - - @Override - public boolean acceptRequest(NetworkRequest request) { - if (DBG) { - Log.d(TAG, "acceptRequest, request: " + request); - } - - return networkForRequest(request) != null; - } - - @Override - protected void needNetworkFor(NetworkRequest networkRequest) { - NetworkInterfaceState network = networkForRequest(networkRequest); - - if (network == null) { - Log.e(TAG, "needNetworkFor, failed to get a network for " + networkRequest); - return; - } - - if (++network.refCount == 1) { - network.start(); - } - } - - @Override - protected void releaseNetworkFor(NetworkRequest networkRequest) { - NetworkInterfaceState network = networkForRequest(networkRequest); - if (network == null) { - Log.e(TAG, "releaseNetworkFor, failed to get a network for " + networkRequest); - return; - } - - if (--network.refCount == 0) { - network.stop(); - } - } - - /** - * Returns an array of available interface names. The array is sorted: unrestricted interfaces - * goes first, then sorted by name. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected String[] getAvailableInterfaces(boolean includeRestricted) { - return mTrackingInterfaces.values() - .stream() - .filter(iface -> !iface.isRestricted() || includeRestricted) - .sorted((iface1, iface2) -> { - int r = Boolean.compare(iface1.isRestricted(), iface2.isRestricted()); - return r == 0 ? iface1.name.compareTo(iface2.name) : r; - }) - .map(iface -> iface.name) - .toArray(String[]::new); - } - - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected 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; - } - - final NetworkCapabilities nc = new NetworkCapabilities.Builder(capabilities) - .setNetworkSpecifier(new EthernetNetworkSpecifier(ifaceName)) - .build(); - - if (DBG) { - Log.d(TAG, "addInterface, iface: " + ifaceName + ", capabilities: " + nc); - } - - final NetworkInterfaceState iface = new NetworkInterfaceState( - ifaceName, hwAddress, mHandler, mContext, ipConfig, nc, this, mDeps); - mTrackingInterfaces.put(ifaceName, iface); - updateCapabilityFilter(); - } - - @VisibleForTesting - protected int getInterfaceState(@NonNull String iface) { - final NetworkInterfaceState interfaceState = mTrackingInterfaces.get(iface); - if (interfaceState == null) { - return EthernetManager.STATE_ABSENT; - } else if (!interfaceState.mLinkUp) { - return EthernetManager.STATE_LINK_DOWN; - } else { - return EthernetManager.STATE_LINK_UP; - } - } - - /** - * 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 or null. If - * {@code null} is passed, the existing IpConfiguration is not updated. - * @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 INetworkInterfaceOutcomeReceiver} to notify callers of - * completion. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected void updateInterface(@NonNull final String ifaceName, - @Nullable final IpConfiguration ipConfig, - @Nullable final NetworkCapabilities capabilities, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (!hasInterface(ifaceName)) { - maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); - return; - } - - final NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName); - iface.updateInterface(ipConfig, capabilities, listener); - mTrackingInterfaces.put(ifaceName, iface); - updateCapabilityFilter(); - } - - private static NetworkCapabilities mixInCapabilities(NetworkCapabilities nc, - NetworkCapabilities addedNc) { - final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(nc); - for (int transport : addedNc.getTransportTypes()) builder.addTransportType(transport); - for (int capability : addedNc.getCapabilities()) builder.addCapability(capability); - return builder.build(); - } - - private void updateCapabilityFilter() { - NetworkCapabilities capabilitiesFilter = createDefaultNetworkCapabilities(); - for (NetworkInterfaceState iface: mTrackingInterfaces.values()) { - capabilitiesFilter = mixInCapabilities(capabilitiesFilter, iface.mCapabilities); - } - - if (DBG) Log.d(TAG, "updateCapabilityFilter: " + capabilitiesFilter); - setCapabilityFilter(capabilitiesFilter); - } - - private static NetworkCapabilities createDefaultNetworkCapabilities() { - return NetworkCapabilities.Builder - .withoutDefaultCapabilities() - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET).build(); - } - - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected void removeInterface(String interfaceName) { - NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName); - if (iface != null) { - iface.maybeSendNetworkManagementCallbackForAbort(); - iface.stop(); - } - - updateCapabilityFilter(); - } - - /** Returns true if state has been modified */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected boolean updateInterfaceLinkState(@NonNull final String ifaceName, final boolean up, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (!hasInterface(ifaceName)) { - maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); - return false; - } - - if (DBG) { - Log.d(TAG, "updateInterfaceLinkState, iface: " + ifaceName + ", up: " + up); - } - - NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName); - return iface.updateLinkState(up, listener); - } - - private void maybeSendNetworkManagementCallbackForUntracked( - String ifaceName, INetworkInterfaceOutcomeReceiver listener) { - maybeSendNetworkManagementCallback(listener, null, - new EthernetNetworkManagementException( - ifaceName + " can't be updated as it is not available.")); - } - - @VisibleForTesting - protected boolean hasInterface(String ifaceName) { - return mTrackingInterfaces.containsKey(ifaceName); - } - - private NetworkInterfaceState networkForRequest(NetworkRequest request) { - String requestedIface = null; - - NetworkSpecifier specifier = request.getNetworkSpecifier(); - if (specifier instanceof EthernetNetworkSpecifier) { - requestedIface = ((EthernetNetworkSpecifier) specifier) - .getInterfaceName(); - } - - NetworkInterfaceState network = null; - if (!TextUtils.isEmpty(requestedIface)) { - NetworkInterfaceState n = mTrackingInterfaces.get(requestedIface); - if (n != null && request.canBeSatisfiedBy(n.mCapabilities)) { - network = n; - } - } else { - for (NetworkInterfaceState n : mTrackingInterfaces.values()) { - if (request.canBeSatisfiedBy(n.mCapabilities) && n.mLinkUp) { - network = n; - break; - } - } - } - - if (DBG) { - Log.i(TAG, "networkForRequest, request: " + request + ", network: " + network); - } - - return network; - } - - private static void maybeSendNetworkManagementCallback( - @Nullable final INetworkInterfaceOutcomeReceiver listener, - @Nullable final String iface, - @Nullable final EthernetNetworkManagementException e) { - if (null == listener) { - return; - } - - try { - if (iface != null) { - listener.onResult(iface); - } else { - listener.onError(e); - } - } catch (RemoteException re) { - Log.e(TAG, "Can't send onComplete for network management callback", re); - } - } - - @VisibleForTesting - static class NetworkInterfaceState { - final String name; - - private final String mHwAddress; - private final Handler mHandler; - private final Context mContext; - private final NetworkFactory mNetworkFactory; - private final Dependencies mDeps; - - 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 EthernetIpClientCallback mIpClientCallback; - private @Nullable EthernetNetworkAgent mNetworkAgent; - private @Nullable IpConfiguration mIpConfig; - - /** - * A map of TRANSPORT_* types to legacy transport types available for each type an ethernet - * interface could propagate. - * - * There are no legacy type equivalents to LOWPAN or WIFI_AWARE. These types are set to - * TYPE_NONE to match the behavior of their own network factories. - */ - private static final SparseArray sTransports = new SparseArray(); - static { - sTransports.put(NetworkCapabilities.TRANSPORT_ETHERNET, - ConnectivityManager.TYPE_ETHERNET); - sTransports.put(NetworkCapabilities.TRANSPORT_BLUETOOTH, - ConnectivityManager.TYPE_BLUETOOTH); - sTransports.put(NetworkCapabilities.TRANSPORT_WIFI, ConnectivityManager.TYPE_WIFI); - sTransports.put(NetworkCapabilities.TRANSPORT_CELLULAR, - ConnectivityManager.TYPE_MOBILE); - sTransports.put(NetworkCapabilities.TRANSPORT_LOWPAN, ConnectivityManager.TYPE_NONE); - sTransports.put(NetworkCapabilities.TRANSPORT_WIFI_AWARE, - ConnectivityManager.TYPE_NONE); - } - - long refCount = 0; - - private class EthernetIpClientCallback extends IpClientCallbacks { - private final ConditionVariable mIpClientStartCv = new ConditionVariable(false); - private final ConditionVariable mIpClientShutdownCv = new ConditionVariable(false); - @Nullable INetworkInterfaceOutcomeReceiver mNetworkManagementListener; - - EthernetIpClientCallback(@Nullable final INetworkInterfaceOutcomeReceiver listener) { - mNetworkManagementListener = listener; - } - - @Override - public void onIpClientCreated(IIpClient ipClient) { - mIpClient = mDeps.makeIpClientManager(ipClient); - mIpClientStartCv.open(); - } - - private void awaitIpClientStart() { - mIpClientStartCv.block(); - } - - private void awaitIpClientShutdown() { - mIpClientShutdownCv.block(); - } - - // At the time IpClient is stopped, an IpClient event may have already been posted on - // the back of the handler and is awaiting execution. Once that event is executed, the - // associated callback object may not be valid anymore - // (NetworkInterfaceState#mIpClientCallback points to a different object / null). - private boolean isCurrentCallback() { - return this == mIpClientCallback; - } - - private void handleIpEvent(final @NonNull Runnable r) { - mHandler.post(() -> { - if (!isCurrentCallback()) { - Log.i(TAG, "Ignoring stale IpClientCallbacks " + this); - return; - } - r.run(); - }); - } - - @Override - public void onProvisioningSuccess(LinkProperties newLp) { - handleIpEvent(() -> onIpLayerStarted(newLp, mNetworkManagementListener)); - } - - @Override - public void onProvisioningFailure(LinkProperties newLp) { - // This cannot happen due to provisioning timeout, because our timeout is 0. It can - // happen due to errors while provisioning or on provisioning loss. - handleIpEvent(() -> onIpLayerStopped(mNetworkManagementListener)); - } - - @Override - public void onLinkPropertiesChange(LinkProperties newLp) { - handleIpEvent(() -> updateLinkProperties(newLp)); - } - - @Override - public void onReachabilityLost(String logMsg) { - handleIpEvent(() -> updateNeighborLostEvent(logMsg)); - } - - @Override - public void onQuit() { - mIpClient = null; - mIpClientShutdownCv.open(); - } - } - - NetworkInterfaceState(String ifaceName, String hwAddress, Handler handler, Context context, - @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; - mHwAddress = hwAddress; - } - - /** - * Determines the legacy transport type from a NetworkCapabilities transport type. Defaults - * to legacy TYPE_NONE if there is no known conversion - */ - private static int getLegacyType(int transport) { - 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); - } - - void updateInterface(@Nullable final IpConfiguration ipConfig, - @Nullable final NetworkCapabilities capabilities, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (DBG) { - Log.d(TAG, "updateInterface, iface: " + name - + ", ipConfig: " + ipConfig + ", old ipConfig: " + mIpConfig - + ", capabilities: " + capabilities + ", old capabilities: " + mCapabilities - + ", listener: " + listener - ); - } - - if (null != ipConfig){ - mIpConfig = ipConfig; - } - if (null != capabilities) { - 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() { - return !mCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); - } - - private void start() { - start(null); - } - - private void start(@Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (mIpClient != null) { - if (DBG) Log.d(TAG, "IpClient already started"); - return; - } - if (DBG) { - Log.d(TAG, String.format("Starting Ethernet IpClient(%s)", name)); - } - - mIpClientCallback = new EthernetIpClientCallback(listener); - mDeps.makeIpClient(mContext, name, mIpClientCallback); - mIpClientCallback.awaitIpClientStart(); - - if (sTcpBufferSizes == null) { - sTcpBufferSizes = mDeps.getTcpBufferSizesFromResource(mContext); - } - provisionIpClient(mIpClient, mIpConfig, sTcpBufferSizes); - } - - void onIpLayerStarted(@NonNull final LinkProperties linkProperties, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (mNetworkAgent != null) { - Log.e(TAG, "Already have a NetworkAgent - aborting new request"); - stop(); - return; - } - mLinkProperties = linkProperties; - - // Create our NetworkAgent. - final NetworkAgentConfig config = new NetworkAgentConfig.Builder() - .setLegacyType(mLegacyType) - .setLegacyTypeName(NETWORK_TYPE) - .setLegacyExtraInfo(mHwAddress) - .build(); - mNetworkAgent = mDeps.makeEthernetNetworkAgent(mContext, mHandler.getLooper(), - mCapabilities, mLinkProperties, config, mNetworkFactory.getProvider(), - new EthernetNetworkAgent.Callbacks() { - @Override - public void onNetworkUnwanted() { - // if mNetworkAgent is null, we have already called stop. - if (mNetworkAgent == null) return; - - if (this == mNetworkAgent.getCallbacks()) { - stop(); - } else { - Log.d(TAG, "Ignoring unwanted as we have a more modern " + - "instance"); - } - } - }); - mNetworkAgent.register(); - mNetworkAgent.markConnected(); - realizeNetworkManagementCallback(name, null); - } - - void onIpLayerStopped(@Nullable final INetworkInterfaceOutcomeReceiver listener) { - // 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 EthernetNetworkManagementException( - "The IP provisioning request has been aborted.")); - } - - // Must be called on the handler thread - private void realizeNetworkManagementCallback(@Nullable final String iface, - @Nullable final EthernetNetworkManagementException e) { - ensureRunningOnEthernetHandlerThread(); - if (null == mIpClientCallback) { - return; - } - - EthernetNetworkFactory.maybeSendNetworkManagementCallback( - mIpClientCallback.mNetworkManagementListener, iface, 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()); - } - } - - void updateLinkProperties(LinkProperties linkProperties) { - mLinkProperties = linkProperties; - if (mNetworkAgent != null) { - mNetworkAgent.sendLinkPropertiesImpl(linkProperties); - } - } - - void updateNeighborLostEvent(String logMsg) { - Log.i(TAG, "updateNeighborLostEvent " + logMsg); - // Reachability lost will be seen only if the gateway is not reachable. - // Since ethernet FW doesn't have the mechanism to scan for new networks - // like WiFi, simply restart. - // If there is a better network, that will become default and apps - // will be able to use internet. If ethernet gets connected again, - // and has backhaul connectivity, it will become default. - restart(); - } - - /** Returns true if state has been modified */ - boolean updateLinkState(final boolean up, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (mLinkUp == up) { - EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, null, - new EthernetNetworkManagementException( - "No changes with requested link state " + up + " for " + name)); - return false; - } - mLinkUp = up; - - if (!up) { // was up, goes down - // Send an abort on a provisioning request callback if necessary before stopping. - maybeSendNetworkManagementCallbackForAbort(); - stop(); - // If only setting the interface down, send a callback to signal completion. - EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, name, null); - } else { // was down, goes up - stop(); - start(listener); - } - - return true; - } - - void stop() { - // Invalidate all previous start requests - if (mIpClient != null) { - mIpClient.shutdown(); - mIpClientCallback.awaitIpClientShutdown(); - mIpClient = null; - } - mIpClientCallback = null; - - if (mNetworkAgent != null) { - mNetworkAgent.unregister(); - mNetworkAgent = null; - } - mLinkProperties.clear(); - } - - private static void provisionIpClient(@NonNull final IpClientManager ipClient, - @NonNull final IpConfiguration config, @NonNull final String tcpBufferSizes) { - if (config.getProxySettings() == ProxySettings.STATIC || - config.getProxySettings() == ProxySettings.PAC) { - ipClient.setHttpProxy(config.getHttpProxy()); - } - - if (!TextUtils.isEmpty(tcpBufferSizes)) { - ipClient.setTcpBufferSizes(tcpBufferSizes); - } - - ipClient.startProvisioning(createProvisioningConfiguration(config)); - } - - private static ProvisioningConfiguration createProvisioningConfiguration( - @NonNull final IpConfiguration config) { - if (config.getIpAssignment() == IpAssignment.STATIC) { - return new ProvisioningConfiguration.Builder() - .withStaticConfiguration(config.getStaticIpConfiguration()) - .build(); - } - return new ProvisioningConfiguration.Builder() - .withProvisioningTimeoutMs(0) - .build(); - } - - void restart() { - restart(null); - } - - void restart(@Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (DBG) Log.d(TAG, "reconnecting Ethernet"); - stop(); - start(listener); - } - - @Override - public String toString() { - return getClass().getSimpleName() + "{ " - + "refCount: " + refCount + ", " - + "iface: " + name + ", " - + "up: " + mLinkUp + ", " - + "hwAddress: " + mHwAddress + ", " - + "networkCapabilities: " + mCapabilities + ", " - + "networkAgent: " + mNetworkAgent + ", " - + "ipClient: " + mIpClient + "," - + "linkProperties: " + mLinkProperties - + "}"; - } - } - - void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { - super.dump(fd, pw, args); - pw.println(getClass().getSimpleName()); - pw.println("Tracking interfaces:"); - pw.increaseIndent(); - for (String iface: mTrackingInterfaces.keySet()) { - NetworkInterfaceState ifaceState = mTrackingInterfaces.get(iface); - pw.println(iface + ":" + ifaceState); - pw.increaseIndent(); - if (null == ifaceState.mIpClient) { - pw.println("IpClient is null"); - } - pw.decreaseIndent(); - } - pw.decreaseIndent(); - } -} diff --git a/java/com/android/server/ethernet/EthernetService.java b/java/com/android/server/ethernet/EthernetService.java deleted file mode 100644 index d405fd5..0000000 --- a/java/com/android/server/ethernet/EthernetService.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2014 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 android.content.Context; -import android.net.INetd; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; - -import java.util.Objects; - -// TODO: consider renaming EthernetServiceImpl to EthernetService and deleting this file. -public final class EthernetService { - private static final String TAG = "EthernetService"; - private static final String THREAD_NAME = "EthernetServiceThread"; - - private static INetd getNetd(Context context) { - final INetd netd = - INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)); - Objects.requireNonNull(netd, "could not get netd instance"); - return netd; - } - - public static EthernetServiceImpl create(Context context) { - final HandlerThread handlerThread = new HandlerThread(THREAD_NAME); - handlerThread.start(); - final Handler handler = new Handler(handlerThread.getLooper()); - final EthernetNetworkFactory factory = new EthernetNetworkFactory(handler, context); - return new EthernetServiceImpl(context, handler, - new EthernetTracker(context, handler, factory, getNetd(context))); - } -} diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java deleted file mode 100644 index 5e830ad..0000000 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright (C) 2014 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 android.net.NetworkCapabilities.TRANSPORT_TEST; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.content.pm.PackageManager; -import android.net.IEthernetManager; -import android.net.IEthernetServiceListener; -import android.net.INetworkInterfaceOutcomeReceiver; -import android.net.ITetheredInterfaceCallback; -import android.net.EthernetNetworkUpdateRequest; -import android.net.IpConfiguration; -import android.net.NetworkCapabilities; -import android.os.Binder; -import android.os.Handler; -import android.os.RemoteException; -import android.util.Log; -import android.util.PrintWriterPrinter; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; -import com.android.net.module.util.PermissionUtils; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * EthernetServiceImpl handles remote Ethernet operation requests by implementing - * the IEthernetManager interface. - */ -public class EthernetServiceImpl extends IEthernetManager.Stub { - private static final String TAG = "EthernetServiceImpl"; - - @VisibleForTesting - final AtomicBoolean mStarted = new AtomicBoolean(false); - private final Context mContext; - private final Handler mHandler; - private final EthernetTracker mTracker; - - EthernetServiceImpl(@NonNull final Context context, @NonNull final Handler handler, - @NonNull final EthernetTracker tracker) { - mContext = context; - mHandler = handler; - mTracker = tracker; - } - - private void enforceAutomotiveDevice(final @NonNull String methodName) { - PermissionUtils.enforceSystemFeature(mContext, PackageManager.FEATURE_AUTOMOTIVE, - methodName + " is only available on automotive devices."); - } - - private boolean checkUseRestrictedNetworksPermission() { - return PermissionUtils.checkAnyPermissionOf(mContext, - android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS); - } - - public void start() { - Log.i(TAG, "Starting Ethernet service"); - mTracker.start(); - mStarted.set(true); - } - - private void throwIfEthernetNotStarted() { - if (!mStarted.get()) { - throw new IllegalStateException("System isn't ready to change ethernet configurations"); - } - } - - @Override - public String[] getAvailableInterfaces() throws RemoteException { - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - return mTracker.getInterfaces(checkUseRestrictedNetworksPermission()); - } - - /** - * Get Ethernet configuration - * @return the Ethernet Configuration, contained in {@link IpConfiguration}. - */ - @Override - public IpConfiguration getConfiguration(String iface) { - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - if (mTracker.isRestrictedInterface(iface)) { - PermissionUtils.enforceRestrictedNetworkPermission(mContext, TAG); - } - - return new IpConfiguration(mTracker.getIpConfiguration(iface)); - } - - /** - * Set Ethernet configuration - */ - @Override - public void setConfiguration(String iface, IpConfiguration config) { - throwIfEthernetNotStarted(); - - PermissionUtils.enforceNetworkStackPermission(mContext); - if (mTracker.isRestrictedInterface(iface)) { - PermissionUtils.enforceRestrictedNetworkPermission(mContext, TAG); - } - - // TODO: this does not check proxy settings, gateways, etc. - // Fix this by making IpConfiguration a complete representation of static configuration. - mTracker.updateIpConfiguration(iface, new IpConfiguration(config)); - } - - /** - * Indicates whether given interface is available. - */ - @Override - public boolean isAvailable(String iface) { - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - if (mTracker.isRestrictedInterface(iface)) { - PermissionUtils.enforceRestrictedNetworkPermission(mContext, TAG); - } - - return mTracker.isTrackingInterface(iface); - } - - /** - * Adds a listener. - * @param listener A {@link IEthernetServiceListener} to add. - */ - public void addListener(IEthernetServiceListener listener) throws RemoteException { - Objects.requireNonNull(listener, "listener must not be null"); - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - mTracker.addListener(listener, checkUseRestrictedNetworksPermission()); - } - - /** - * Removes a listener. - * @param listener A {@link IEthernetServiceListener} to remove. - */ - public void removeListener(IEthernetServiceListener listener) { - if (listener == null) { - throw new IllegalArgumentException("listener must not be null"); - } - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - mTracker.removeListener(listener); - } - - @Override - public void setIncludeTestInterfaces(boolean include) { - PermissionUtils.enforceNetworkStackPermissionOr(mContext, - android.Manifest.permission.NETWORK_SETTINGS); - mTracker.setIncludeTestInterfaces(include); - } - - @Override - public void requestTetheredInterface(ITetheredInterfaceCallback callback) { - Objects.requireNonNull(callback, "callback must not be null"); - PermissionUtils.enforceNetworkStackPermissionOr(mContext, - android.Manifest.permission.NETWORK_SETTINGS); - mTracker.requestTetheredInterface(callback); - } - - @Override - public void releaseTetheredInterface(ITetheredInterfaceCallback callback) { - Objects.requireNonNull(callback, "callback must not be null"); - PermissionUtils.enforceNetworkStackPermissionOr(mContext, - android.Manifest.permission.NETWORK_SETTINGS); - mTracker.releaseTetheredInterface(callback); - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump EthernetService from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid()); - return; - } - - pw.println("Current Ethernet state: "); - pw.increaseIndent(); - mTracker.dump(fd, pw, args); - pw.decreaseIndent(); - - pw.println("Handler:"); - pw.increaseIndent(); - mHandler.dump(new PrintWriterPrinter(pw), "EthernetServiceImpl"); - pw.decreaseIndent(); - } - - private void enforceNetworkManagementPermission() { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.MANAGE_ETHERNET_NETWORKS, - "EthernetServiceImpl"); - } - - private void enforceManageTestNetworksPermission() { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.MANAGE_TEST_NETWORKS, - "EthernetServiceImpl"); - } - - private void maybeValidateTestCapabilities(final String iface, - @Nullable final NetworkCapabilities nc) { - if (!mTracker.isValidTestInterface(iface)) { - return; - } - // For test interfaces, only null or capabilities that include TRANSPORT_TEST are - // allowed. - if (nc != null && !nc.hasTransport(TRANSPORT_TEST)) { - throw new IllegalArgumentException( - "Updates to test interfaces must have NetworkCapabilities.TRANSPORT_TEST."); - } - } - - private void enforceAdminPermission(final String iface, boolean enforceAutomotive, - final String logMessage) { - if (mTracker.isValidTestInterface(iface)) { - enforceManageTestNetworksPermission(); - } else { - enforceNetworkManagementPermission(); - if (enforceAutomotive) { - enforceAutomotiveDevice(logMessage); - } - } - } - - @Override - public void updateConfiguration(@NonNull final String iface, - @NonNull final EthernetNetworkUpdateRequest request, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - Objects.requireNonNull(iface); - Objects.requireNonNull(request); - throwIfEthernetNotStarted(); - - // TODO: validate that iface is listed in overlay config_ethernet_interfaces - // only automotive devices are allowed to set the NetworkCapabilities using this API - enforceAdminPermission(iface, request.getNetworkCapabilities() != null, - "updateConfiguration() with non-null capabilities"); - maybeValidateTestCapabilities(iface, request.getNetworkCapabilities()); - - mTracker.updateConfiguration( - iface, request.getIpConfiguration(), request.getNetworkCapabilities(), listener); - } - - @Override - public void connectNetwork(@NonNull final String iface, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - Log.i(TAG, "connectNetwork called with: iface=" + iface + ", listener=" + listener); - Objects.requireNonNull(iface); - throwIfEthernetNotStarted(); - - enforceAdminPermission(iface, true, "connectNetwork()"); - - mTracker.connectNetwork(iface, listener); - } - - @Override - public void disconnectNetwork(@NonNull final String iface, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - Log.i(TAG, "disconnectNetwork called with: iface=" + iface + ", listener=" + listener); - Objects.requireNonNull(iface); - throwIfEthernetNotStarted(); - - enforceAdminPermission(iface, true, "connectNetwork()"); - - mTracker.disconnectNetwork(iface, listener); - } - - @Override - public void setEthernetEnabled(boolean enabled) { - PermissionUtils.enforceNetworkStackPermissionOr(mContext, - android.Manifest.permission.NETWORK_SETTINGS); - - mTracker.setEthernetEnabled(enabled); - } - - @Override - public List getInterfaceList() { - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - return mTracker.getInterfaceList(); - } -} diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java deleted file mode 100644 index c291b3f..0000000 --- a/java/com/android/server/ethernet/EthernetTracker.java +++ /dev/null @@ -1,942 +0,0 @@ -/* - * Copyright (C) 2018 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 android.net.EthernetManager.ETHERNET_STATE_DISABLED; -import static android.net.EthernetManager.ETHERNET_STATE_ENABLED; -import static android.net.TestNetworkManager.TEST_TAP_PREFIX; - -import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.content.res.Resources; -import android.net.ConnectivityResources; -import android.net.EthernetManager; -import android.net.IEthernetServiceListener; -import android.net.INetworkInterfaceOutcomeReceiver; -import android.net.INetd; -import android.net.ITetheredInterfaceCallback; -import android.net.InterfaceConfigurationParcel; -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.ConditionVariable; -import android.os.Handler; -import android.os.RemoteCallbackList; -import android.os.RemoteException; -import android.os.ServiceSpecificException; -import android.text.TextUtils; -import android.util.ArrayMap; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; -import com.android.net.module.util.BaseNetdUnsolicitedEventListener; -import com.android.net.module.util.NetdUtils; -import com.android.net.module.util.PermissionUtils; - -import java.io.FileDescriptor; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Tracks Ethernet interfaces and manages interface configurations. - * - *

Interfaces may have different {@link android.net.NetworkCapabilities}. This mapping is defined - * in {@code config_ethernet_interfaces}. Notably, some interfaces could be marked as restricted by - * not specifying {@link android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED} flag. - * Interfaces could have associated {@link android.net.IpConfiguration}. - * Ethernet Interfaces may be present at boot time or appear after boot (e.g., for Ethernet adapters - * connected over USB). This class supports multiple interfaces. When an interface appears on the - * system (or is present at boot time) this class will start tracking it and bring it up. Only - * interfaces whose names match the {@code config_ethernet_iface_regex} regular expression are - * tracked. - * - *

All public or package private methods must be thread-safe unless stated otherwise. - */ -@VisibleForTesting(visibility = PACKAGE) -public class EthernetTracker { - private static final int INTERFACE_MODE_CLIENT = 1; - private static final int INTERFACE_MODE_SERVER = 2; - - private static final String TAG = EthernetTracker.class.getSimpleName(); - private static final boolean DBG = EthernetNetworkFactory.DBG; - - private static final String TEST_IFACE_REGEXP = TEST_TAP_PREFIX + "\\d+"; - private static final String LEGACY_IFACE_REGEXP = "eth\\d"; - - /** - * Interface names we track. This is a product-dependent regular expression, plus, - * if setIncludeTestInterfaces is true, any test interfaces. - */ - private volatile String mIfaceMatch; - /** - * Track test interfaces if true, don't track otherwise. - */ - private boolean mIncludeTestInterfaces = false; - - /** Mapping between {iface name | mac address} -> {NetworkCapabilities} */ - private final ConcurrentHashMap mNetworkCapabilities = - new ConcurrentHashMap<>(); - private final ConcurrentHashMap mIpConfigurations = - new ConcurrentHashMap<>(); - - private final Context mContext; - private final INetd mNetd; - private final Handler mHandler; - private final EthernetNetworkFactory mFactory; - private final EthernetConfigStore mConfigStore; - private final Dependencies mDeps; - - private final RemoteCallbackList mListeners = - new RemoteCallbackList<>(); - private final TetheredInterfaceRequestList mTetheredInterfaceRequests = - new TetheredInterfaceRequestList(); - - // Used only on the handler thread - private String mDefaultInterface; - private int mDefaultInterfaceMode = INTERFACE_MODE_CLIENT; - // Tracks whether clients were notified that the tethered interface is available - private boolean mTetheredInterfaceWasAvailable = false; - private volatile IpConfiguration mIpConfigForDefaultInterface; - - private int mEthernetState = ETHERNET_STATE_ENABLED; - - private class TetheredInterfaceRequestList extends - RemoteCallbackList { - @Override - public void onCallbackDied(ITetheredInterfaceCallback cb, Object cookie) { - mHandler.post(EthernetTracker.this::maybeUntetherDefaultInterface); - } - } - - public static class Dependencies { - // TODO: remove legacy resource fallback after migrating its overlays. - private String getPlatformRegexResource(Context context) { - final Resources r = context.getResources(); - final int resId = - r.getIdentifier("config_ethernet_iface_regex", "string", context.getPackageName()); - return r.getString(resId); - } - - // TODO: remove legacy resource fallback after migrating its overlays. - private String[] getPlatformInterfaceConfigs(Context context) { - final Resources r = context.getResources(); - final int resId = r.getIdentifier("config_ethernet_interfaces", "array", - context.getPackageName()); - return r.getStringArray(resId); - } - - public String getInterfaceRegexFromResource(Context context) { - final String platformRegex = getPlatformRegexResource(context); - final String match; - if (!LEGACY_IFACE_REGEXP.equals(platformRegex)) { - // Platform resource is not the historical default: use the overlay - match = platformRegex; - } else { - final ConnectivityResources resources = new ConnectivityResources(context); - match = resources.get().getString( - com.android.connectivity.resources.R.string.config_ethernet_iface_regex); - } - return match; - } - - public String[] getInterfaceConfigFromResource(Context context) { - final String[] platformInterfaceConfigs = getPlatformInterfaceConfigs(context); - final String[] interfaceConfigs; - if (platformInterfaceConfigs.length != 0) { - // Platform resource is not the historical default: use the overlay - interfaceConfigs = platformInterfaceConfigs; - } else { - final ConnectivityResources resources = new ConnectivityResources(context); - interfaceConfigs = resources.get().getStringArray( - com.android.connectivity.resources.R.array.config_ethernet_interfaces); - } - return interfaceConfigs; - } - } - - EthernetTracker(@NonNull final Context context, @NonNull final Handler handler, - @NonNull final EthernetNetworkFactory factory, @NonNull final INetd netd) { - this(context, handler, factory, netd, new Dependencies()); - } - - @VisibleForTesting - EthernetTracker(@NonNull final Context context, @NonNull final Handler handler, - @NonNull final EthernetNetworkFactory factory, @NonNull final INetd netd, - @NonNull final Dependencies deps) { - mContext = context; - mHandler = handler; - mFactory = factory; - mNetd = netd; - mDeps = deps; - - // Interface match regex. - updateIfaceMatchRegexp(); - - // Read default Ethernet interface configuration from resources - final String[] interfaceConfigs = mDeps.getInterfaceConfigFromResource(context); - for (String strConfig : interfaceConfigs) { - parseEthernetConfig(strConfig); - } - - mConfigStore = new EthernetConfigStore(); - } - - void start() { - mFactory.register(); - mConfigStore.read(); - - // Default interface is just the first one we want to track. - mIpConfigForDefaultInterface = mConfigStore.getIpConfigurationForDefaultInterface(); - final ArrayMap configs = mConfigStore.getIpConfigurations(); - for (int i = 0; i < configs.size(); i++) { - mIpConfigurations.put(configs.keyAt(i), configs.valueAt(i)); - } - - try { - PermissionUtils.enforceNetworkStackPermission(mContext); - mNetd.registerUnsolicitedEventListener(new InterfaceObserver()); - } catch (RemoteException | ServiceSpecificException e) { - Log.e(TAG, "Could not register InterfaceObserver " + e); - } - - mHandler.post(this::trackAvailableInterfaces); - } - - void updateIpConfiguration(String iface, IpConfiguration ipConfiguration) { - if (DBG) { - Log.i(TAG, "updateIpConfiguration, iface: " + iface + ", cfg: " + ipConfiguration); - } - writeIpConfiguration(iface, ipConfiguration); - mHandler.post(() -> { - mFactory.updateInterface(iface, ipConfiguration, null, null); - broadcastInterfaceStateChange(iface); - }); - } - - private void writeIpConfiguration(@NonNull final String iface, - @NonNull final IpConfiguration ipConfig) { - mConfigStore.write(iface, ipConfig); - mIpConfigurations.put(iface, ipConfig); - } - - private IpConfiguration getIpConfigurationForCallback(String iface, int state) { - return (state == EthernetManager.STATE_ABSENT) ? null : getOrCreateIpConfiguration(iface); - } - - private void ensureRunningOnEthernetServiceThread() { - if (mHandler.getLooper().getThread() != Thread.currentThread()) { - throw new IllegalStateException( - "Not running on EthernetService thread: " - + Thread.currentThread().getName()); - } - } - - /** - * Broadcast the link state or IpConfiguration change of existing Ethernet interfaces to all - * listeners. - */ - protected void broadcastInterfaceStateChange(@NonNull String iface) { - ensureRunningOnEthernetServiceThread(); - final int state = mFactory.getInterfaceState(iface); - final int role = getInterfaceRole(iface); - final IpConfiguration config = getIpConfigurationForCallback(iface, state); - final int n = mListeners.beginBroadcast(); - for (int i = 0; i < n; i++) { - try { - mListeners.getBroadcastItem(i).onInterfaceStateChanged(iface, state, role, config); - } catch (RemoteException e) { - // Do nothing here. - } - } - mListeners.finishBroadcast(); - } - - /** - * Unicast the interface state or IpConfiguration change of existing Ethernet interfaces to a - * specific listener. - */ - protected void unicastInterfaceStateChange(@NonNull IEthernetServiceListener listener, - @NonNull String iface) { - ensureRunningOnEthernetServiceThread(); - final int state = mFactory.getInterfaceState(iface); - final int role = getInterfaceRole(iface); - final IpConfiguration config = getIpConfigurationForCallback(iface, state); - try { - listener.onInterfaceStateChanged(iface, state, role, config); - } catch (RemoteException e) { - // Do nothing here. - } - } - - @VisibleForTesting(visibility = PACKAGE) - protected void updateConfiguration(@NonNull final String iface, - @Nullable final IpConfiguration ipConfig, - @Nullable final NetworkCapabilities capabilities, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (DBG) { - Log.i(TAG, "updateConfiguration, iface: " + iface + ", capabilities: " + capabilities - + ", ipConfig: " + ipConfig); - } - - final IpConfiguration localIpConfig = ipConfig == null - ? null : new IpConfiguration(ipConfig); - if (ipConfig != null) { - writeIpConfiguration(iface, localIpConfig); - } - - if (null != capabilities) { - mNetworkCapabilities.put(iface, capabilities); - } - mHandler.post(() -> { - mFactory.updateInterface(iface, localIpConfig, capabilities, listener); - broadcastInterfaceStateChange(iface); - }); - } - - @VisibleForTesting(visibility = PACKAGE) - protected void connectNetwork(@NonNull final String iface, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - mHandler.post(() -> updateInterfaceState(iface, true, listener)); - } - - @VisibleForTesting(visibility = PACKAGE) - protected void disconnectNetwork(@NonNull final String iface, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - mHandler.post(() -> updateInterfaceState(iface, false, listener)); - } - - IpConfiguration getIpConfiguration(String iface) { - return mIpConfigurations.get(iface); - } - - @VisibleForTesting(visibility = PACKAGE) - protected boolean isTrackingInterface(String iface) { - return mFactory.hasInterface(iface); - } - - String[] getInterfaces(boolean includeRestricted) { - return mFactory.getAvailableInterfaces(includeRestricted); - } - - List getInterfaceList() { - final List interfaceList = new ArrayList(); - final String[] ifaces; - try { - ifaces = mNetd.interfaceGetList(); - } catch (RemoteException e) { - Log.e(TAG, "Could not get list of interfaces " + e); - return interfaceList; - } - final String ifaceMatch = mIfaceMatch; - for (String iface : ifaces) { - if (iface.matches(ifaceMatch)) interfaceList.add(iface); - } - return interfaceList; - } - - /** - * Returns true if given interface was configured as restricted (doesn't have - * NET_CAPABILITY_NOT_RESTRICTED) capability. Otherwise, returns false. - */ - boolean isRestrictedInterface(String iface) { - final NetworkCapabilities nc = mNetworkCapabilities.get(iface); - return nc != null && !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); - } - - void addListener(IEthernetServiceListener listener, boolean canUseRestrictedNetworks) { - mHandler.post(() -> { - if (!mListeners.register(listener, new ListenerInfo(canUseRestrictedNetworks))) { - // Remote process has already died - return; - } - for (String iface : getInterfaces(canUseRestrictedNetworks)) { - unicastInterfaceStateChange(listener, iface); - } - - unicastEthernetStateChange(listener, mEthernetState); - }); - } - - void removeListener(IEthernetServiceListener listener) { - mHandler.post(() -> mListeners.unregister(listener)); - } - - public void setIncludeTestInterfaces(boolean include) { - mHandler.post(() -> { - mIncludeTestInterfaces = include; - updateIfaceMatchRegexp(); - mHandler.post(() -> trackAvailableInterfaces()); - }); - } - - public void requestTetheredInterface(ITetheredInterfaceCallback callback) { - mHandler.post(() -> { - if (!mTetheredInterfaceRequests.register(callback)) { - // Remote process has already died - return; - } - if (mDefaultInterfaceMode == INTERFACE_MODE_SERVER) { - if (mTetheredInterfaceWasAvailable) { - notifyTetheredInterfaceAvailable(callback, mDefaultInterface); - } - return; - } - - setDefaultInterfaceMode(INTERFACE_MODE_SERVER); - }); - } - - public void releaseTetheredInterface(ITetheredInterfaceCallback callback) { - mHandler.post(() -> { - mTetheredInterfaceRequests.unregister(callback); - maybeUntetherDefaultInterface(); - }); - } - - private void notifyTetheredInterfaceAvailable(ITetheredInterfaceCallback cb, String iface) { - try { - cb.onAvailable(iface); - } catch (RemoteException e) { - Log.e(TAG, "Error sending tethered interface available callback", e); - } - } - - private void notifyTetheredInterfaceUnavailable(ITetheredInterfaceCallback cb) { - try { - cb.onUnavailable(); - } catch (RemoteException e) { - Log.e(TAG, "Error sending tethered interface available callback", e); - } - } - - private void maybeUntetherDefaultInterface() { - if (mTetheredInterfaceRequests.getRegisteredCallbackCount() > 0) return; - if (mDefaultInterfaceMode == INTERFACE_MODE_CLIENT) return; - setDefaultInterfaceMode(INTERFACE_MODE_CLIENT); - } - - private void setDefaultInterfaceMode(int mode) { - Log.d(TAG, "Setting default interface mode to " + mode); - mDefaultInterfaceMode = mode; - if (mDefaultInterface != null) { - removeInterface(mDefaultInterface); - addInterface(mDefaultInterface); - } - } - - private int getInterfaceRole(final String iface) { - if (!mFactory.hasInterface(iface)) return EthernetManager.ROLE_NONE; - final int mode = getInterfaceMode(iface); - return (mode == INTERFACE_MODE_CLIENT) - ? EthernetManager.ROLE_CLIENT - : EthernetManager.ROLE_SERVER; - } - - private int getInterfaceMode(final String iface) { - if (iface.equals(mDefaultInterface)) { - return mDefaultInterfaceMode; - } - return INTERFACE_MODE_CLIENT; - } - - private void removeInterface(String iface) { - mFactory.removeInterface(iface); - maybeUpdateServerModeInterfaceState(iface, false); - } - - private void stopTrackingInterface(String iface) { - removeInterface(iface); - if (iface.equals(mDefaultInterface)) { - mDefaultInterface = null; - } - broadcastInterfaceStateChange(iface); - } - - private void addInterface(String iface) { - InterfaceConfigurationParcel config = null; - // Bring up the interface so we get link status indications. - try { - PermissionUtils.enforceNetworkStackPermission(mContext); - NetdUtils.setInterfaceUp(mNetd, iface); - config = NetdUtils.getInterfaceConfigParcel(mNetd, iface); - } catch (IllegalStateException e) { - // Either the system is crashing or the interface has disappeared. Just ignore the - // error; we haven't modified any state because we only do that if our calls succeed. - Log.e(TAG, "Error upping interface " + iface, e); - } - - if (config == null) { - Log.e(TAG, "Null interface config parcelable for " + iface + ". Bailing out."); - return; - } - - final String hwAddress = config.hwAddr; - - NetworkCapabilities nc = mNetworkCapabilities.get(iface); - if (nc == null) { - // Try to resolve using mac address - nc = mNetworkCapabilities.get(hwAddress); - if (nc == null) { - final boolean isTestIface = iface.matches(TEST_IFACE_REGEXP); - nc = createDefaultNetworkCapabilities(isTestIface); - } - } - - final int mode = getInterfaceMode(iface); - if (mode == INTERFACE_MODE_CLIENT) { - IpConfiguration ipConfiguration = getOrCreateIpConfiguration(iface); - Log.d(TAG, "Tracking interface in client mode: " + iface); - mFactory.addInterface(iface, hwAddress, ipConfiguration, nc); - } else { - maybeUpdateServerModeInterfaceState(iface, true); - } - - // Note: if the interface already has link (e.g., if we crashed and got - // restarted while it was running), we need to fake a link up notification so we - // start configuring it. - if (NetdUtils.hasFlag(config, "running")) { - updateInterfaceState(iface, true); - } - } - - private void updateInterfaceState(String iface, boolean up) { - updateInterfaceState(iface, up, null /* listener */); - } - - private void updateInterfaceState(@NonNull final String iface, final boolean up, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - final int mode = getInterfaceMode(iface); - final boolean factoryLinkStateUpdated = (mode == INTERFACE_MODE_CLIENT) - && mFactory.updateInterfaceLinkState(iface, up, listener); - - if (factoryLinkStateUpdated) { - broadcastInterfaceStateChange(iface); - } - } - - private void maybeUpdateServerModeInterfaceState(String iface, boolean available) { - if (available == mTetheredInterfaceWasAvailable || !iface.equals(mDefaultInterface)) return; - - Log.d(TAG, (available ? "Tracking" : "No longer tracking") - + " interface in server mode: " + iface); - - final int pendingCbs = mTetheredInterfaceRequests.beginBroadcast(); - for (int i = 0; i < pendingCbs; i++) { - ITetheredInterfaceCallback item = mTetheredInterfaceRequests.getBroadcastItem(i); - if (available) { - notifyTetheredInterfaceAvailable(item, iface); - } else { - notifyTetheredInterfaceUnavailable(item); - } - } - mTetheredInterfaceRequests.finishBroadcast(); - mTetheredInterfaceWasAvailable = available; - } - - private void maybeTrackInterface(String iface) { - if (!iface.matches(mIfaceMatch)) { - return; - } - - // If we don't already track this interface, and if this interface matches - // our regex, start tracking it. - if (mFactory.hasInterface(iface) || iface.equals(mDefaultInterface)) { - if (DBG) Log.w(TAG, "Ignoring already-tracked interface " + iface); - return; - } - if (DBG) Log.i(TAG, "maybeTrackInterface: " + iface); - - // TODO: avoid making an interface default if it has configured NetworkCapabilities. - if (mDefaultInterface == null) { - mDefaultInterface = iface; - } - - if (mIpConfigForDefaultInterface != null) { - updateIpConfiguration(iface, mIpConfigForDefaultInterface); - mIpConfigForDefaultInterface = null; - } - - addInterface(iface); - - broadcastInterfaceStateChange(iface); - } - - private void trackAvailableInterfaces() { - try { - final String[] ifaces = mNetd.interfaceGetList(); - for (String iface : ifaces) { - maybeTrackInterface(iface); - } - } catch (RemoteException | ServiceSpecificException e) { - Log.e(TAG, "Could not get list of interfaces " + e); - } - } - - private class InterfaceObserver extends BaseNetdUnsolicitedEventListener { - - @Override - public void onInterfaceLinkStateChanged(String iface, boolean up) { - if (DBG) { - Log.i(TAG, "interfaceLinkStateChanged, iface: " + iface + ", up: " + up); - } - mHandler.post(() -> updateInterfaceState(iface, up)); - } - - @Override - public void onInterfaceAdded(String iface) { - if (DBG) { - Log.i(TAG, "onInterfaceAdded, iface: " + iface); - } - mHandler.post(() -> maybeTrackInterface(iface)); - } - - @Override - public void onInterfaceRemoved(String iface) { - if (DBG) { - Log.i(TAG, "onInterfaceRemoved, iface: " + iface); - } - mHandler.post(() -> stopTrackingInterface(iface)); - } - } - - private static class ListenerInfo { - - boolean canUseRestrictedNetworks = false; - - ListenerInfo(boolean canUseRestrictedNetworks) { - this.canUseRestrictedNetworks = canUseRestrictedNetworks; - } - } - - /** - * Parses an Ethernet interface configuration - * - * @param configString represents an Ethernet configuration in the following format: {@code - * ;[Network Capabilities];[IP config];[Override Transport]} - */ - private void parseEthernetConfig(String configString) { - final EthernetTrackerConfig config = createEthernetTrackerConfig(configString); - NetworkCapabilities nc = createNetworkCapabilities( - !TextUtils.isEmpty(config.mCapabilities) /* clear default capabilities */, - config.mCapabilities, config.mTransport).build(); - mNetworkCapabilities.put(config.mIface, nc); - - if (null != config.mIpConfig) { - IpConfiguration ipConfig = parseStaticIpConfiguration(config.mIpConfig); - mIpConfigurations.put(config.mIface, ipConfig); - } - } - - @VisibleForTesting - static EthernetTrackerConfig createEthernetTrackerConfig(@NonNull final String configString) { - Objects.requireNonNull(configString, "EthernetTrackerConfig requires non-null config"); - return new EthernetTrackerConfig(configString.split(";", /* limit of tokens */ 4)); - } - - private static NetworkCapabilities createDefaultNetworkCapabilities(boolean isTestIface) { - NetworkCapabilities.Builder builder = createNetworkCapabilities( - false /* clear default capabilities */, null, null) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); - - if (isTestIface) { - builder.addTransportType(NetworkCapabilities.TRANSPORT_TEST); - } else { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - } - - return builder.build(); - } - - /** - * Parses a static list of network capabilities - * - * @param clearDefaultCapabilities Indicates whether or not to clear any default capabilities - * @param commaSeparatedCapabilities A comma separated string list of integer encoded - * NetworkCapability.NET_CAPABILITY_* values - * @param overrideTransport A string representing a single integer encoded override transport - * type. Must be one of the NetworkCapability.TRANSPORT_* - * values. TRANSPORT_VPN is not supported. Errors with input - * will cause the override to be ignored. - */ - @VisibleForTesting - static NetworkCapabilities.Builder createNetworkCapabilities( - boolean clearDefaultCapabilities, @Nullable String commaSeparatedCapabilities, - @Nullable String overrideTransport) { - - final NetworkCapabilities.Builder builder = clearDefaultCapabilities - ? NetworkCapabilities.Builder.withoutDefaultCapabilities() - : new NetworkCapabilities.Builder(); - - // Determine the transport type. If someone has tried to define an override transport then - // attempt to add it. Since we can only have one override, all errors with it will - // gracefully default back to TRANSPORT_ETHERNET and warn the user. VPN is not allowed as an - // override type. Wifi Aware and LoWPAN are currently unsupported as well. - int transport = NetworkCapabilities.TRANSPORT_ETHERNET; - if (!TextUtils.isEmpty(overrideTransport)) { - try { - int parsedTransport = Integer.valueOf(overrideTransport); - if (parsedTransport == NetworkCapabilities.TRANSPORT_VPN - || parsedTransport == NetworkCapabilities.TRANSPORT_WIFI_AWARE - || parsedTransport == NetworkCapabilities.TRANSPORT_LOWPAN) { - Log.e(TAG, "Override transport '" + parsedTransport + "' is not supported. " - + "Defaulting to TRANSPORT_ETHERNET"); - } else { - transport = parsedTransport; - } - } catch (NumberFormatException nfe) { - Log.e(TAG, "Override transport type '" + overrideTransport + "' " - + "could not be parsed. Defaulting to TRANSPORT_ETHERNET"); - } - } - - // Apply the transport. If the user supplied a valid number that is not a valid transport - // then adding will throw an exception. Default back to TRANSPORT_ETHERNET if that happens - try { - builder.addTransportType(transport); - } catch (IllegalArgumentException iae) { - Log.e(TAG, transport + " is not a valid NetworkCapability.TRANSPORT_* value. " - + "Defaulting to TRANSPORT_ETHERNET"); - builder.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET); - } - - builder.setLinkUpstreamBandwidthKbps(100 * 1000); - builder.setLinkDownstreamBandwidthKbps(100 * 1000); - - if (!TextUtils.isEmpty(commaSeparatedCapabilities)) { - for (String strNetworkCapability : commaSeparatedCapabilities.split(",")) { - if (!TextUtils.isEmpty(strNetworkCapability)) { - try { - builder.addCapability(Integer.valueOf(strNetworkCapability)); - } catch (NumberFormatException nfe) { - Log.e(TAG, "Capability '" + strNetworkCapability + "' could not be parsed"); - } catch (IllegalArgumentException iae) { - Log.e(TAG, strNetworkCapability + " is not a valid " - + "NetworkCapability.NET_CAPABILITY_* value"); - } - } - } - } - // Ethernet networks have no way to update the following capabilities, so they always - // have them. - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); - - return builder; - } - - /** - * Parses static IP configuration. - * - * @param staticIpConfig represents static IP configuration in the following format: {@code - * ip= gateway= dns= - * domains=} - */ - @VisibleForTesting - static IpConfiguration parseStaticIpConfiguration(String staticIpConfig) { - final StaticIpConfiguration.Builder staticIpConfigBuilder = - new StaticIpConfiguration.Builder(); - - for (String keyValueAsString : staticIpConfig.trim().split(" ")) { - if (TextUtils.isEmpty(keyValueAsString)) continue; - - String[] pair = keyValueAsString.split("="); - if (pair.length != 2) { - throw new IllegalArgumentException("Unexpected token: " + keyValueAsString - + " in " + staticIpConfig); - } - - String key = pair[0]; - String value = pair[1]; - - switch (key) { - case "ip": - staticIpConfigBuilder.setIpAddress(new LinkAddress(value)); - break; - case "domains": - staticIpConfigBuilder.setDomains(value); - break; - case "gateway": - staticIpConfigBuilder.setGateway(InetAddress.parseNumericAddress(value)); - break; - case "dns": { - ArrayList dnsAddresses = new ArrayList<>(); - for (String address: value.split(",")) { - dnsAddresses.add(InetAddress.parseNumericAddress(address)); - } - staticIpConfigBuilder.setDnsServers(dnsAddresses); - break; - } - default : { - throw new IllegalArgumentException("Unexpected key: " + key - + " in " + staticIpConfig); - } - } - } - return createIpConfiguration(staticIpConfigBuilder.build()); - } - - private static IpConfiguration createIpConfiguration( - @NonNull final StaticIpConfiguration staticIpConfig) { - return new IpConfiguration.Builder().setStaticIpConfiguration(staticIpConfig).build(); - } - - private IpConfiguration getOrCreateIpConfiguration(String iface) { - IpConfiguration ret = mIpConfigurations.get(iface); - if (ret != null) return ret; - ret = new IpConfiguration(); - ret.setIpAssignment(IpAssignment.DHCP); - ret.setProxySettings(ProxySettings.NONE); - return ret; - } - - private void updateIfaceMatchRegexp() { - final String match = mDeps.getInterfaceRegexFromResource(mContext); - mIfaceMatch = mIncludeTestInterfaces - ? "(" + match + "|" + TEST_IFACE_REGEXP + ")" - : match; - Log.d(TAG, "Interface match regexp set to '" + mIfaceMatch + "'"); - } - - /** - * Validate if a given interface is valid for testing. - * - * @param iface the name of the interface to validate. - * @return {@code true} if test interfaces are enabled and the given {@code iface} has a test - * interface prefix, {@code false} otherwise. - */ - public boolean isValidTestInterface(@NonNull final String iface) { - return mIncludeTestInterfaces && iface.matches(TEST_IFACE_REGEXP); - } - - private void postAndWaitForRunnable(Runnable r) { - final ConditionVariable cv = new ConditionVariable(); - if (mHandler.post(() -> { - r.run(); - cv.open(); - })) { - cv.block(2000L); - } - } - - @VisibleForTesting(visibility = PACKAGE) - protected void setEthernetEnabled(boolean enabled) { - mHandler.post(() -> { - int newState = enabled ? ETHERNET_STATE_ENABLED : ETHERNET_STATE_DISABLED; - if (mEthernetState == newState) return; - - mEthernetState = newState; - - if (enabled) { - trackAvailableInterfaces(); - } else { - // TODO: maybe also disable server mode interface as well. - untrackFactoryInterfaces(); - } - broadcastEthernetStateChange(mEthernetState); - }); - } - - private void untrackFactoryInterfaces() { - for (String iface : mFactory.getAvailableInterfaces(true /* includeRestricted */)) { - stopTrackingInterface(iface); - } - } - - private void unicastEthernetStateChange(@NonNull IEthernetServiceListener listener, - int state) { - ensureRunningOnEthernetServiceThread(); - try { - listener.onEthernetStateChanged(state); - } catch (RemoteException e) { - // Do nothing here. - } - } - - private void broadcastEthernetStateChange(int state) { - ensureRunningOnEthernetServiceThread(); - final int n = mListeners.beginBroadcast(); - for (int i = 0; i < n; i++) { - try { - mListeners.getBroadcastItem(i).onEthernetStateChanged(state); - } catch (RemoteException e) { - // Do nothing here. - } - } - mListeners.finishBroadcast(); - } - - void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { - postAndWaitForRunnable(() -> { - pw.println(getClass().getSimpleName()); - pw.println("Ethernet interface name filter: " + mIfaceMatch); - pw.println("Default interface: " + mDefaultInterface); - pw.println("Default interface mode: " + mDefaultInterfaceMode); - pw.println("Tethered interface requests: " - + mTetheredInterfaceRequests.getRegisteredCallbackCount()); - pw.println("Listeners: " + mListeners.getRegisteredCallbackCount()); - pw.println("IP Configurations:"); - pw.increaseIndent(); - for (String iface : mIpConfigurations.keySet()) { - pw.println(iface + ": " + mIpConfigurations.get(iface)); - } - pw.decreaseIndent(); - pw.println(); - - pw.println("Network Capabilities:"); - pw.increaseIndent(); - for (String iface : mNetworkCapabilities.keySet()) { - pw.println(iface + ": " + mNetworkCapabilities.get(iface)); - } - pw.decreaseIndent(); - pw.println(); - - mFactory.dump(fd, pw, args); - }); - } - - @VisibleForTesting - static class EthernetTrackerConfig { - 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"); - 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/Android.bp b/tests/Android.bp deleted file mode 100644 index 6cfebdc..0000000 --- a/tests/Android.bp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (C) 2018 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 { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -// TODO: merge the tests into service-connectivity tests after -// ethernet service migration completes. So far just import the -// ethernet service source to fix the dependencies. -android_test { - name: "EthernetServiceTests", - - srcs: [ - ":ethernet-service-updatable-sources", - ":services.connectivity-ethernet-sources", - "java/**/*.java", - ], - - certificate: "platform", - platform_apis: true, - - libs: [ - "android.test.runner", - "android.test.base", - "android.test.mock", - "framework-connectivity.impl", - "framework-connectivity-t.impl", - "ServiceConnectivityResources", - ], - - static_libs: [ - "androidx.test.rules", - "frameworks-base-testutils", - "mockito-target-minus-junit4", - "net-tests-utils", - "services.core", - "services.net", - ], - test_suites: ["general-tests"], -} diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml deleted file mode 100644 index cd875b0..0000000 --- a/tests/AndroidManifest.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - diff --git a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java deleted file mode 100644 index 4d3e4d3..0000000 --- a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java +++ /dev/null @@ -1,783 +0,0 @@ -/* - * 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.assertNotSame; -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.EthernetNetworkManagementException; -import android.net.INetworkInterfaceOutcomeReceiver; -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.connectivity.resources.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 INetworkInterfaceOutcomeReceiver 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(mDeps.getTcpBufferSizesFromResource(eq(mContext))).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 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(); - assertEquals(listener.expectOnResult(), TEST_IFACE); - } - - @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(); - assertEquals(listener.expectOnResult(), TEST_IFACE); - } - - @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()); - assertEquals(listener.expectOnResult(), TEST_IFACE); - } - - @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(); - listener.expectOnErrorWithMessage("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(); - listener.expectOnErrorWithMessage("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()); - } - - private IpClientCallbacks getStaleIpClientCallbacks() throws Exception { - createAndVerifyProvisionedInterface(TEST_IFACE); - final IpClientCallbacks staleIpClientCallbacks = mIpClientCallbacks; - mNetFactory.removeInterface(TEST_IFACE); - verifyStop(); - assertNotSame(mIpClientCallbacks, staleIpClientCallbacks); - return staleIpClientCallbacks; - } - - @Test - public void testIgnoreOnIpLayerStartedCallbackForStaleCallback() throws Exception { - initEthernetNetworkFactory(); - final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); - - staleIpClientCallbacks.onProvisioningSuccess(new LinkProperties()); - mLooper.dispatchAll(); - - verify(mIpClient, never()).startProvisioning(any()); - verify(mNetworkAgent, never()).register(); - } - - @Test - public void testIgnoreOnIpLayerStoppedCallbackForStaleCallback() throws Exception { - initEthernetNetworkFactory(); - when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams); - final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); - - staleIpClientCallbacks.onProvisioningFailure(new LinkProperties()); - mLooper.dispatchAll(); - - verify(mIpClient, never()).startProvisioning(any()); - } - - @Test - public void testIgnoreLinkPropertiesCallbackForStaleCallback() throws Exception { - initEthernetNetworkFactory(); - final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); - final LinkProperties lp = new LinkProperties(); - - staleIpClientCallbacks.onLinkPropertiesChange(lp); - mLooper.dispatchAll(); - - verify(mNetworkAgent, never()).sendLinkPropertiesImpl(eq(lp)); - } - - @Test - public void testIgnoreNeighborLossCallbackForStaleCallback() throws Exception { - initEthernetNetworkFactory(); - final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); - - staleIpClientCallbacks.onReachabilityLost("Neighbor Lost"); - mLooper.dispatchAll(); - - 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 INetworkInterfaceOutcomeReceiver { - private final CompletableFuture mResult = new CompletableFuture<>(); - private final CompletableFuture mError = - new CompletableFuture<>(); - - @Override - public void onResult(@NonNull String iface) { - mResult.complete(iface); - } - - @Override - public void onError(@NonNull EthernetNetworkManagementException exception) { - mError.complete(exception); - } - - String expectOnResult() throws Exception { - return mResult.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); - } - - EthernetNetworkManagementException expectOnError() throws Exception { - return mError.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); - } - - void expectOnErrorWithMessage(String msg) throws Exception { - assertTrue(expectOnError().getMessage().contains(msg)); - } - - @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(); - - assertEquals(listener.expectOnResult(), TEST_IFACE); - } - - @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(); - }); - - assertEquals(successfulListener.expectOnResult(), TEST_IFACE); - } - - 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(); - - failedListener.expectOnErrorWithMessage("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(); - - assertEquals(listener.expectOnResult(), TEST_IFACE); - 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(); - listener.expectOnErrorWithMessage("can't be updated as it is not available"); - } - - @Test - public void testUpdateInterfaceWithNullIpConfiguration() throws Exception { - initEthernetNetworkFactory(); - createAndVerifyProvisionedInterface(TEST_IFACE); - - final IpConfiguration initialIpConfig = createStaticIpConfig(); - mNetFactory.updateInterface(TEST_IFACE, initialIpConfig, null /*capabilities*/, - null /*listener*/); - triggerOnProvisioningSuccess(); - verifyRestart(initialIpConfig); - - // TODO: have verifyXyz functions clear invocations. - clearInvocations(mDeps); - clearInvocations(mIpClient); - clearInvocations(mNetworkAgent); - - - // verify that sending a null ipConfig does not update the current ipConfig. - mNetFactory.updateInterface(TEST_IFACE, null /*ipConfig*/, null /*capabilities*/, - null /*listener*/); - triggerOnProvisioningSuccess(); - verifyRestart(initialIpConfig); - } -} diff --git a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java deleted file mode 100644 index dd1f1ed..0000000 --- a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright (C) 2021 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 android.net.NetworkCapabilities.TRANSPORT_TEST; - -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.fail; - -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.Manifest; -import android.annotation.NonNull; -import android.content.Context; -import android.content.pm.PackageManager; -import android.net.INetworkInterfaceOutcomeReceiver; -import android.net.EthernetNetworkUpdateRequest; -import android.net.IpConfiguration; -import android.net.NetworkCapabilities; -import android.os.Handler; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class EthernetServiceImplTest { - private static final String TEST_IFACE = "test123"; - private static final EthernetNetworkUpdateRequest UPDATE_REQUEST = - new EthernetNetworkUpdateRequest.Builder() - .setIpConfiguration(new IpConfiguration()) - .setNetworkCapabilities(new NetworkCapabilities.Builder().build()) - .build(); - private static final EthernetNetworkUpdateRequest UPDATE_REQUEST_WITHOUT_CAPABILITIES = - new EthernetNetworkUpdateRequest.Builder() - .setIpConfiguration(new IpConfiguration()) - .build(); - private static final EthernetNetworkUpdateRequest UPDATE_REQUEST_WITHOUT_IP_CONFIG = - new EthernetNetworkUpdateRequest.Builder() - .setNetworkCapabilities(new NetworkCapabilities.Builder().build()) - .build(); - private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; - private EthernetServiceImpl mEthernetServiceImpl; - @Mock private Context mContext; - @Mock private Handler mHandler; - @Mock private EthernetTracker mEthernetTracker; - @Mock private PackageManager mPackageManager; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - doReturn(mPackageManager).when(mContext).getPackageManager(); - mEthernetServiceImpl = new EthernetServiceImpl(mContext, mHandler, mEthernetTracker); - mEthernetServiceImpl.mStarted.set(true); - toggleAutomotiveFeature(true); - shouldTrackIface(TEST_IFACE, true); - } - - private void toggleAutomotiveFeature(final boolean isEnabled) { - doReturn(isEnabled) - .when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); - } - - private void shouldTrackIface(@NonNull final String iface, final boolean shouldTrack) { - doReturn(shouldTrack).when(mEthernetTracker).isTrackingInterface(iface); - } - - @Test - public void testSetConfigurationRejectsWhenEthNotStarted() { - mEthernetServiceImpl.mStarted.set(false); - assertThrows(IllegalStateException.class, () -> { - mEthernetServiceImpl.setConfiguration("" /* iface */, new IpConfiguration()); - }); - } - - @Test - public void testUpdateConfigurationRejectsWhenEthNotStarted() { - mEthernetServiceImpl.mStarted.set(false); - assertThrows(IllegalStateException.class, () -> { - mEthernetServiceImpl.updateConfiguration( - "" /* iface */, UPDATE_REQUEST, null /* listener */); - }); - } - - @Test - public void testConnectNetworkRejectsWhenEthNotStarted() { - mEthernetServiceImpl.mStarted.set(false); - assertThrows(IllegalStateException.class, () -> { - mEthernetServiceImpl.connectNetwork("" /* iface */, null /* listener */); - }); - } - - @Test - public void testDisconnectNetworkRejectsWhenEthNotStarted() { - mEthernetServiceImpl.mStarted.set(false); - assertThrows(IllegalStateException.class, () -> { - mEthernetServiceImpl.disconnectNetwork("" /* iface */, null /* listener */); - }); - } - - @Test - public void testUpdateConfigurationRejectsNullIface() { - assertThrows(NullPointerException.class, () -> { - mEthernetServiceImpl.updateConfiguration(null, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - @Test - public void testConnectNetworkRejectsNullIface() { - assertThrows(NullPointerException.class, () -> { - mEthernetServiceImpl.connectNetwork(null /* iface */, NULL_LISTENER); - }); - } - - @Test - public void testDisconnectNetworkRejectsNullIface() { - assertThrows(NullPointerException.class, () -> { - mEthernetServiceImpl.disconnectNetwork(null /* iface */, NULL_LISTENER); - }); - } - - @Test - public void testUpdateConfigurationWithCapabilitiesRejectsWithoutAutomotiveFeature() { - toggleAutomotiveFeature(false); - assertThrows(UnsupportedOperationException.class, () -> { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - @Test - public void testUpdateConfigurationWithCapabilitiesWithAutomotiveFeature() { - toggleAutomotiveFeature(false); - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST_WITHOUT_CAPABILITIES, - NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration(eq(TEST_IFACE), - eq(UPDATE_REQUEST_WITHOUT_CAPABILITIES.getIpConfiguration()), - eq(UPDATE_REQUEST_WITHOUT_CAPABILITIES.getNetworkCapabilities()), isNull()); - } - - @Test - public void testConnectNetworkRejectsWithoutAutomotiveFeature() { - toggleAutomotiveFeature(false); - assertThrows(UnsupportedOperationException.class, () -> { - mEthernetServiceImpl.connectNetwork("" /* iface */, NULL_LISTENER); - }); - } - - @Test - public void testDisconnectNetworkRejectsWithoutAutomotiveFeature() { - toggleAutomotiveFeature(false); - assertThrows(UnsupportedOperationException.class, () -> { - mEthernetServiceImpl.disconnectNetwork("" /* iface */, NULL_LISTENER); - }); - } - - private void denyManageEthPermission() { - doThrow(new SecurityException("")).when(mContext) - .enforceCallingOrSelfPermission( - eq(Manifest.permission.MANAGE_ETHERNET_NETWORKS), anyString()); - } - - private void denyManageTestNetworksPermission() { - doThrow(new SecurityException("")).when(mContext) - .enforceCallingOrSelfPermission( - eq(Manifest.permission.MANAGE_TEST_NETWORKS), anyString()); - } - - @Test - public void testUpdateConfigurationRejectsWithoutManageEthPermission() { - denyManageEthPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - @Test - public void testConnectNetworkRejectsWithoutManageEthPermission() { - denyManageEthPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER); - }); - } - - @Test - public void testDisconnectNetworkRejectsWithoutManageEthPermission() { - denyManageEthPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - }); - } - - private void enableTestInterface() { - when(mEthernetTracker.isValidTestInterface(eq(TEST_IFACE))).thenReturn(true); - } - - @Test - public void testUpdateConfigurationRejectsTestRequestWithoutTestPermission() { - enableTestInterface(); - denyManageTestNetworksPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - @Test - public void testConnectNetworkRejectsTestRequestWithoutTestPermission() { - enableTestInterface(); - denyManageTestNetworksPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER); - }); - } - - @Test - public void testDisconnectNetworkRejectsTestRequestWithoutTestPermission() { - enableTestInterface(); - denyManageTestNetworksPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - }); - } - - @Test - public void testUpdateConfiguration() { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration( - eq(TEST_IFACE), - eq(UPDATE_REQUEST.getIpConfiguration()), - eq(UPDATE_REQUEST.getNetworkCapabilities()), eq(NULL_LISTENER)); - } - - @Test - public void testConnectNetwork() { - mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER); - verify(mEthernetTracker).connectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); - } - - @Test - public void testDisconnectNetwork() { - mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - verify(mEthernetTracker).disconnectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); - } - - @Test - public void testUpdateConfigurationAcceptsTestRequestWithNullCapabilities() { - enableTestInterface(); - final EthernetNetworkUpdateRequest request = - new EthernetNetworkUpdateRequest - .Builder() - .setIpConfiguration(new IpConfiguration()).build(); - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, request, NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration(eq(TEST_IFACE), - eq(request.getIpConfiguration()), - eq(request.getNetworkCapabilities()), isNull()); - } - - @Test - public void testUpdateConfigurationAcceptsRequestWithNullIpConfiguration() { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST_WITHOUT_IP_CONFIG, - NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration(eq(TEST_IFACE), - eq(UPDATE_REQUEST_WITHOUT_IP_CONFIG.getIpConfiguration()), - eq(UPDATE_REQUEST_WITHOUT_IP_CONFIG.getNetworkCapabilities()), isNull()); - } - - @Test - public void testUpdateConfigurationRejectsInvalidTestRequest() { - enableTestInterface(); - assertThrows(IllegalArgumentException.class, () -> { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - private EthernetNetworkUpdateRequest createTestNetworkUpdateRequest() { - final NetworkCapabilities nc = new NetworkCapabilities - .Builder(UPDATE_REQUEST.getNetworkCapabilities()) - .addTransportType(TRANSPORT_TEST).build(); - - return new EthernetNetworkUpdateRequest - .Builder(UPDATE_REQUEST) - .setNetworkCapabilities(nc).build(); - } - - @Test - public void testUpdateConfigurationForTestRequestDoesNotRequireAutoOrEthernetPermission() { - enableTestInterface(); - toggleAutomotiveFeature(false); - denyManageEthPermission(); - final EthernetNetworkUpdateRequest request = createTestNetworkUpdateRequest(); - - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, request, NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration( - eq(TEST_IFACE), - eq(request.getIpConfiguration()), - eq(request.getNetworkCapabilities()), eq(NULL_LISTENER)); - } - - @Test - public void testConnectNetworkForTestRequestDoesNotRequireAutoOrNetPermission() { - enableTestInterface(); - toggleAutomotiveFeature(false); - denyManageEthPermission(); - - mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER); - verify(mEthernetTracker).connectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); - } - - @Test - public void testDisconnectNetworkForTestRequestDoesNotRequireAutoOrNetPermission() { - enableTestInterface(); - toggleAutomotiveFeature(false); - denyManageEthPermission(); - - mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - verify(mEthernetTracker).disconnectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); - } - - private void denyPermissions(String... permissions) { - for (String permission: permissions) { - doReturn(PackageManager.PERMISSION_DENIED).when(mContext) - .checkCallingOrSelfPermission(eq(permission)); - } - } - - @Test - public void testSetEthernetEnabled() { - denyPermissions(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); - mEthernetServiceImpl.setEthernetEnabled(true); - verify(mEthernetTracker).setEthernetEnabled(true); - reset(mEthernetTracker); - - denyPermissions(Manifest.permission.NETWORK_STACK); - mEthernetServiceImpl.setEthernetEnabled(false); - verify(mEthernetTracker).setEthernetEnabled(false); - reset(mEthernetTracker); - - denyPermissions(Manifest.permission.NETWORK_SETTINGS); - try { - mEthernetServiceImpl.setEthernetEnabled(true); - fail("Should get SecurityException"); - } catch (SecurityException e) { } - verify(mEthernetTracker, never()).setEthernetEnabled(false); - } -} diff --git a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java deleted file mode 100644 index b1831c4..0000000 --- a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Copyright (C) 2018 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 android.net.TestNetworkManager.TEST_TAP_PREFIX; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.content.res.Resources; -import android.net.EthernetManager; -import android.net.InetAddresses; -import android.net.INetworkInterfaceOutcomeReceiver; -import android.net.IEthernetServiceListener; -import android.net.INetd; -import android.net.IpConfiguration; -import android.net.IpConfiguration.IpAssignment; -import android.net.IpConfiguration.ProxySettings; -import android.net.InterfaceConfigurationParcel; -import android.net.LinkAddress; -import android.net.NetworkCapabilities; -import android.net.StaticIpConfiguration; -import android.os.HandlerThread; -import android.os.RemoteException; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.connectivity.resources.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; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class EthernetTrackerTest { - private static final String TEST_IFACE = "test123"; - private static final int TIMEOUT_MS = 1_000; - private static final String THREAD_NAME = "EthernetServiceThread"; - private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; - private EthernetTracker tracker; - private HandlerThread mHandlerThread; - @Mock private Context mContext; - @Mock private EthernetNetworkFactory mFactory; - @Mock private INetd mNetd; - @Mock private EthernetTracker.Dependencies mDeps; - - @Before - public void setUp() throws RemoteException { - MockitoAnnotations.initMocks(this); - initMockResources(); - when(mFactory.updateInterfaceLinkState(anyString(), anyBoolean(), any())).thenReturn(false); - when(mNetd.interfaceGetList()).thenReturn(new String[0]); - mHandlerThread = new HandlerThread(THREAD_NAME); - mHandlerThread.start(); - tracker = new EthernetTracker(mContext, mHandlerThread.getThreadHandler(), mFactory, mNetd, - mDeps); - } - - @After - public void cleanUp() { - mHandlerThread.quitSafely(); - } - - private void initMockResources() { - when(mDeps.getInterfaceRegexFromResource(eq(mContext))).thenReturn(""); - when(mDeps.getInterfaceConfigFromResource(eq(mContext))).thenReturn(new String[0]); - } - - private void waitForIdle() { - HandlerUtils.waitForIdle(mHandlerThread, TIMEOUT_MS); - } - - /** - * Test: Creation of various valid static IP configurations - */ - @Test - public void createStaticIpConfiguration() { - // Empty gives default StaticIPConfiguration object - assertStaticConfiguration(new StaticIpConfiguration(), ""); - - // Setting only the IP address properly cascades and assumes defaults - assertStaticConfiguration(new StaticIpConfiguration.Builder() - .setIpAddress(new LinkAddress("192.0.2.10/24")).build(), "ip=192.0.2.10/24"); - - final ArrayList dnsAddresses = new ArrayList<>(); - dnsAddresses.add(InetAddresses.parseNumericAddress("4.4.4.4")); - dnsAddresses.add(InetAddresses.parseNumericAddress("8.8.8.8")); - // Setting other fields properly cascades them - assertStaticConfiguration(new StaticIpConfiguration.Builder() - .setIpAddress(new LinkAddress("192.0.2.10/24")) - .setDnsServers(dnsAddresses) - .setGateway(InetAddresses.parseNumericAddress("192.0.2.1")) - .setDomains("android").build(), - "ip=192.0.2.10/24 dns=4.4.4.4,8.8.8.8 gateway=192.0.2.1 domains=android"); - - // Verify order doesn't matter - assertStaticConfiguration(new StaticIpConfiguration.Builder() - .setIpAddress(new LinkAddress("192.0.2.10/24")) - .setDnsServers(dnsAddresses) - .setGateway(InetAddresses.parseNumericAddress("192.0.2.1")) - .setDomains("android").build(), - "domains=android ip=192.0.2.10/24 gateway=192.0.2.1 dns=4.4.4.4,8.8.8.8 "); - } - - /** - * Test: Attempt creation of various bad static IP configurations - */ - @Test - public void createStaticIpConfiguration_Bad() { - assertStaticConfigurationFails("ip=192.0.2.1/24 gateway= blah=20.20.20.20"); // Unknown key - assertStaticConfigurationFails("ip=192.0.2.1"); // mask is missing - assertStaticConfigurationFails("ip=a.b.c"); // not a valid ip address - assertStaticConfigurationFails("dns=4.4.4.4,1.2.3.A"); // not valid ip address in dns - assertStaticConfigurationFails("="); // Key and value is empty - assertStaticConfigurationFails("ip="); // Value is empty - assertStaticConfigurationFails("ip=192.0.2.1/24 gateway="); // Gateway is empty - } - - private void assertStaticConfigurationFails(String config) { - try { - EthernetTracker.parseStaticIpConfiguration(config); - fail("Expected to fail: " + config); - } catch (IllegalArgumentException e) { - // expected - } - } - - private void assertStaticConfiguration(StaticIpConfiguration expectedStaticIpConfig, - String configAsString) { - final IpConfiguration expectedIpConfiguration = new IpConfiguration(); - expectedIpConfiguration.setIpAssignment(IpAssignment.STATIC); - expectedIpConfiguration.setProxySettings(ProxySettings.NONE); - expectedIpConfiguration.setStaticIpConfiguration(expectedStaticIpConfig); - - assertEquals(expectedIpConfiguration, - EthernetTracker.parseStaticIpConfiguration(configAsString)); - } - - private NetworkCapabilities.Builder makeEthernetCapabilitiesBuilder(boolean clearAll) { - final NetworkCapabilities.Builder builder = - clearAll ? NetworkCapabilities.Builder.withoutDefaultCapabilities() - : new NetworkCapabilities.Builder(); - return builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); - } - - /** - * Test: Attempt to create a capabilties with various valid sets of capabilities/transports - */ - @Test - public void createNetworkCapabilities() { - - // Particularly common expected results - NetworkCapabilities defaultEthernetCleared = - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .build(); - - NetworkCapabilities ethernetClearedWithCommonCaps = - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .addCapability(12) - .addCapability(13) - .addCapability(14) - .addCapability(15) - .build(); - - // Empty capabilities and transports lists with a "please clear defaults" should - // yield an empty capabilities set with TRANPORT_ETHERNET - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", ""); - - // Empty capabilities and transports without the clear defaults flag should return the - // default capabilities set with TRANSPORT_ETHERNET - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(false /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .build(), - false, "", ""); - - // A list of capabilities without the clear defaults flag should return the default - // capabilities, mixed with the desired capabilities, and TRANSPORT_ETHERNET - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(false /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .addCapability(11) - .addCapability(12) - .build(), - false, "11,12", ""); - - // Adding a list of capabilities with a clear defaults will leave exactly those capabilities - // with a default TRANSPORT_ETHERNET since no overrides are specified - assertParsedNetworkCapabilities(ethernetClearedWithCommonCaps, true, "12,13,14,15", ""); - - // Adding any invalid capabilities to the list will cause them to be ignored - assertParsedNetworkCapabilities(ethernetClearedWithCommonCaps, true, "12,13,14,15,65,73", ""); - assertParsedNetworkCapabilities(ethernetClearedWithCommonCaps, true, "12,13,14,15,abcdefg", ""); - - // Adding a valid override transport will remove the default TRANSPORT_ETHERNET transport - // and apply only the override to the capabiltities object - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(0) - .build(), - true, "", "0"); - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(1) - .build(), - true, "", "1"); - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(2) - .build(), - true, "", "2"); - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(3) - .build(), - true, "", "3"); - - // "4" is TRANSPORT_VPN, which is unsupported. Should default back to TRANPORT_ETHERNET - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", "4"); - - // "5" is TRANSPORT_WIFI_AWARE, which is currently supported due to no legacy TYPE_NONE - // conversion. When that becomes available, this test must be updated - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", "5"); - - // "6" is TRANSPORT_LOWPAN, which is currently supported due to no legacy TYPE_NONE - // conversion. When that becomes available, this test must be updated - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", "6"); - - // Adding an invalid override transport will leave the transport as TRANSPORT_ETHERNET - assertParsedNetworkCapabilities(defaultEthernetCleared,true, "", "100"); - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", "abcdefg"); - - // Ensure the adding of both capabilities and transports work - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addCapability(12) - .addCapability(13) - .addCapability(14) - .addCapability(15) - .addTransportType(3) - .build(), - true, "12,13,14,15", "3"); - - // Ensure order does not matter for capability list - assertParsedNetworkCapabilities(ethernetClearedWithCommonCaps, true, "13,12,15,14", ""); - } - - private void assertParsedNetworkCapabilities(NetworkCapabilities expectedNetworkCapabilities, - boolean clearCapabilties, String configCapabiltiies,String configTransports) { - assertEquals(expectedNetworkCapabilities, - EthernetTracker.createNetworkCapabilities(clearCapabilties, configCapabiltiies, - configTransports).build()); - } - - @Test - public void testCreateEthernetTrackerConfigReturnsCorrectValue() { - final String capabilities = "2"; - final String ipConfig = "3"; - final String transport = "4"; - final String configString = String.join(";", TEST_IFACE, capabilities, ipConfig, transport); - - final EthernetTracker.EthernetTrackerConfig config = - EthernetTracker.createEthernetTrackerConfig(configString); - - assertEquals(TEST_IFACE, config.mIface); - assertEquals(capabilities, config.mCapabilities); - assertEquals(ipConfig, config.mIpConfig); - assertEquals(transport, config.mTransport); - } - - @Test - public void testCreateEthernetTrackerConfigThrowsNpeWithNullInput() { - assertThrows(NullPointerException.class, - () -> EthernetTracker.createEthernetTrackerConfig(null)); - } - - @Test - public void testUpdateConfiguration() { - final NetworkCapabilities capabilities = new NetworkCapabilities.Builder().build(); - final LinkAddress linkAddr = new LinkAddress("192.0.2.2/25"); - final StaticIpConfiguration staticIpConfig = - new StaticIpConfiguration.Builder().setIpAddress(linkAddr).build(); - final IpConfiguration ipConfig = - new IpConfiguration.Builder().setStaticIpConfiguration(staticIpConfig).build(); - final INetworkInterfaceOutcomeReceiver listener = null; - - tracker.updateConfiguration(TEST_IFACE, ipConfig, capabilities, listener); - waitForIdle(); - - verify(mFactory).updateInterface( - eq(TEST_IFACE), eq(ipConfig), eq(capabilities), eq(listener)); - } - - @Test - public void testConnectNetworkCorrectlyCallsFactory() { - tracker.connectNetwork(TEST_IFACE, NULL_LISTENER); - waitForIdle(); - - verify(mFactory).updateInterfaceLinkState(eq(TEST_IFACE), eq(true /* up */), - eq(NULL_LISTENER)); - } - - @Test - public void testDisconnectNetworkCorrectlyCallsFactory() { - tracker.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - waitForIdle(); - - verify(mFactory).updateInterfaceLinkState(eq(TEST_IFACE), eq(false /* up */), - eq(NULL_LISTENER)); - } - - @Test - public void testIsValidTestInterfaceIsFalseWhenTestInterfacesAreNotIncluded() { - final String validIfaceName = TEST_TAP_PREFIX + "123"; - tracker.setIncludeTestInterfaces(false); - waitForIdle(); - - final boolean isValidTestInterface = tracker.isValidTestInterface(validIfaceName); - - assertFalse(isValidTestInterface); - } - - @Test - public void testIsValidTestInterfaceIsFalseWhenTestInterfaceNameIsInvalid() { - final String invalidIfaceName = "123" + TEST_TAP_PREFIX; - tracker.setIncludeTestInterfaces(true); - waitForIdle(); - - final boolean isValidTestInterface = tracker.isValidTestInterface(invalidIfaceName); - - assertFalse(isValidTestInterface); - } - - @Test - public void testIsValidTestInterfaceIsTrueWhenTestInterfacesIncludedAndValidName() { - final String validIfaceName = TEST_TAP_PREFIX + "123"; - tracker.setIncludeTestInterfaces(true); - waitForIdle(); - - final boolean isValidTestInterface = tracker.isValidTestInterface(validIfaceName); - - assertTrue(isValidTestInterface); - } - - public static class EthernetStateListener extends IEthernetServiceListener.Stub { - @Override - public void onEthernetStateChanged(int state) { } - - @Override - public void onInterfaceStateChanged(String iface, int state, int role, - IpConfiguration configuration) { } - } - - @Test - public void testListenEthernetStateChange() throws Exception { - final String testIface = "testtap123"; - final String testHwAddr = "11:22:33:44:55:66"; - final InterfaceConfigurationParcel ifaceParcel = new InterfaceConfigurationParcel(); - ifaceParcel.ifName = testIface; - ifaceParcel.hwAddr = testHwAddr; - ifaceParcel.flags = new String[] {INetd.IF_STATE_UP}; - - tracker.setIncludeTestInterfaces(true); - waitForIdle(); - - when(mNetd.interfaceGetList()).thenReturn(new String[] {testIface}); - when(mNetd.interfaceGetCfg(eq(testIface))).thenReturn(ifaceParcel); - doReturn(new String[] {testIface}).when(mFactory).getAvailableInterfaces(anyBoolean()); - doReturn(EthernetManager.STATE_LINK_UP).when(mFactory).getInterfaceState(eq(testIface)); - - final EthernetStateListener listener = spy(new EthernetStateListener()); - tracker.addListener(listener, true /* canUseRestrictedNetworks */); - // Check default state. - waitForIdle(); - verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_LINK_UP), - anyInt(), any()); - verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_ENABLED)); - reset(listener); - - doReturn(EthernetManager.STATE_ABSENT).when(mFactory).getInterfaceState(eq(testIface)); - tracker.setEthernetEnabled(false); - waitForIdle(); - verify(mFactory).removeInterface(eq(testIface)); - verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_DISABLED)); - verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_ABSENT), - anyInt(), any()); - reset(listener); - - doReturn(EthernetManager.STATE_LINK_UP).when(mFactory).getInterfaceState(eq(testIface)); - tracker.setEthernetEnabled(true); - waitForIdle(); - verify(mFactory).addInterface(eq(testIface), eq(testHwAddr), any(), any()); - verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_ENABLED)); - verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_LINK_UP), - anyInt(), any()); - } -} -- cgit v1.2.3 From 01788f8ae9c16735aad6d326a8da3223d9e9c669 Mon Sep 17 00:00:00 2001 From: Patrick Rohr Date: Wed, 30 Mar 2022 15:15:34 +0200 Subject: Move f/o/n/ethernet to p/m/Connectivity Test: TH Bug: 222234190 Merged-In: Ifa19b4476ec8825571f1107c7796dcbb116d7862 Change-Id: Iea43a76d84002a5a587fa735b692d8b6ce6fd12e --- Android.bp | 32 - TEST_MAPPING | 12 - .../server/ethernet/EthernetConfigStore.java | 88 -- .../server/ethernet/EthernetNetworkAgent.java | 65 -- .../server/ethernet/EthernetNetworkFactory.java | 785 ----------------- .../android/server/ethernet/EthernetService.java | 47 - .../server/ethernet/EthernetServiceImpl.java | 299 ------- .../android/server/ethernet/EthernetTracker.java | 942 --------------------- tests/Android.bp | 53 -- tests/AndroidManifest.xml | 28 - .../ethernet/EthernetNetworkFactoryTest.java | 783 ----------------- .../server/ethernet/EthernetServiceImplTest.java | 372 -------- .../server/ethernet/EthernetTrackerTest.java | 456 ---------- 13 files changed, 3962 deletions(-) delete mode 100644 Android.bp delete mode 100644 TEST_MAPPING delete mode 100644 java/com/android/server/ethernet/EthernetConfigStore.java delete mode 100644 java/com/android/server/ethernet/EthernetNetworkAgent.java delete mode 100644 java/com/android/server/ethernet/EthernetNetworkFactory.java delete mode 100644 java/com/android/server/ethernet/EthernetService.java delete mode 100644 java/com/android/server/ethernet/EthernetServiceImpl.java delete mode 100644 java/com/android/server/ethernet/EthernetTracker.java delete mode 100644 tests/Android.bp delete mode 100644 tests/AndroidManifest.xml delete mode 100644 tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java delete mode 100644 tests/java/com/android/server/ethernet/EthernetServiceImplTest.java delete mode 100644 tests/java/com/android/server/ethernet/EthernetTrackerTest.java diff --git a/Android.bp b/Android.bp deleted file mode 100644 index c429a6f..0000000 --- a/Android.bp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2014 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. - -// Build the java code -// ============================================================ - -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -filegroup { - name: "ethernet-service-updatable-sources", - srcs: [ - "java/**/*.java", - ], - path: "java", - visibility: [ - "//frameworks/opt/net/ethernet/tests", - "//packages/modules/Connectivity/service-t", - ], -} diff --git a/TEST_MAPPING b/TEST_MAPPING deleted file mode 100644 index 24c152b..0000000 --- a/TEST_MAPPING +++ /dev/null @@ -1,12 +0,0 @@ -{ - "presubmit": [ - { - "name": "EthernetServiceTests" - } - ], - "imports": [ - { - "path": "packages/modules/Connectivity" - } - ] -} diff --git a/java/com/android/server/ethernet/EthernetConfigStore.java b/java/com/android/server/ethernet/EthernetConfigStore.java deleted file mode 100644 index 6b623f4..0000000 --- a/java/com/android/server/ethernet/EthernetConfigStore.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2014 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 android.annotation.Nullable; -import android.net.IpConfiguration; -import android.os.Environment; -import android.util.ArrayMap; - -import com.android.server.net.IpConfigStore; - - -/** - * This class provides an API to store and manage Ethernet network configuration. - */ -public class EthernetConfigStore { - private static final String ipConfigFile = Environment.getDataDirectory() + - "/misc/ethernet/ipconfig.txt"; - - private IpConfigStore mStore = new IpConfigStore(); - private ArrayMap mIpConfigurations; - private IpConfiguration mIpConfigurationForDefaultInterface; - private final Object mSync = new Object(); - - public EthernetConfigStore() { - mIpConfigurations = new ArrayMap<>(0); - } - - public void read() { - synchronized (mSync) { - ArrayMap configs = - IpConfigStore.readIpConfigurations(ipConfigFile); - - // This configuration may exist in old file versions when there was only a single active - // Ethernet interface. - if (configs.containsKey("0")) { - mIpConfigurationForDefaultInterface = configs.remove("0"); - } - - mIpConfigurations = configs; - } - } - - public void write(String iface, IpConfiguration config) { - boolean modified; - - synchronized (mSync) { - if (config == null) { - modified = mIpConfigurations.remove(iface) != null; - } else { - IpConfiguration oldConfig = mIpConfigurations.put(iface, config); - modified = !config.equals(oldConfig); - } - - if (modified) { - mStore.writeIpConfigurations(ipConfigFile, mIpConfigurations); - } - } - } - - public ArrayMap getIpConfigurations() { - synchronized (mSync) { - return new ArrayMap<>(mIpConfigurations); - } - } - - @Nullable - public IpConfiguration getIpConfigurationForDefaultInterface() { - synchronized (mSync) { - return mIpConfigurationForDefaultInterface == null - ? null : new IpConfiguration(mIpConfigurationForDefaultInterface); - } - } -} diff --git a/java/com/android/server/ethernet/EthernetNetworkAgent.java b/java/com/android/server/ethernet/EthernetNetworkAgent.java deleted file mode 100644 index 57fbce7..0000000 --- a/java/com/android/server/ethernet/EthernetNetworkAgent.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2021 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 android.content.Context; -import android.net.LinkProperties; -import android.net.NetworkAgent; -import android.net.NetworkAgentConfig; -import android.net.NetworkCapabilities; -import android.net.NetworkProvider; -import android.net.NetworkScore; -import android.os.Looper; -import android.annotation.NonNull; -import android.annotation.Nullable; - -public class EthernetNetworkAgent extends NetworkAgent { - - private static final String TAG = "EthernetNetworkAgent"; - - public interface Callbacks { - void onNetworkUnwanted(); - } - - private final Callbacks mCallbacks; - - EthernetNetworkAgent( - @NonNull Context context, - @NonNull Looper looper, - @NonNull NetworkCapabilities nc, - @NonNull LinkProperties lp, - @NonNull NetworkAgentConfig config, - @Nullable NetworkProvider provider, - @NonNull Callbacks cb) { - super(context, looper, TAG, nc, lp, new NetworkScore.Builder().build(), config, provider); - mCallbacks = cb; - } - - @Override - public void onNetworkUnwanted() { - mCallbacks.onNetworkUnwanted(); - } - - // sendLinkProperties is final in NetworkAgent, so it cannot be mocked. - public void sendLinkPropertiesImpl(LinkProperties lp) { - sendLinkProperties(lp); - } - - public Callbacks getCallbacks() { - return mCallbacks; - } -} diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java deleted file mode 100644 index d910629..0000000 --- a/java/com/android/server/ethernet/EthernetNetworkFactory.java +++ /dev/null @@ -1,785 +0,0 @@ -/* - * Copyright (C) 2014 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 android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.content.res.Resources; -import android.net.ConnectivityManager; -import android.net.ConnectivityResources; -import android.net.EthernetManager; -import android.net.EthernetNetworkSpecifier; -import android.net.EthernetNetworkManagementException; -import android.net.INetworkInterfaceOutcomeReceiver; -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; -import android.net.NetworkProvider; -import android.net.NetworkRequest; -import android.net.NetworkSpecifier; -import android.net.ip.IIpClient; -import android.net.ip.IpClientCallbacks; -import android.net.ip.IpClientManager; -import android.net.ip.IpClientUtil; -import android.net.shared.ProvisioningConfiguration; -import android.os.ConditionVariable; -import android.os.Handler; -import android.os.Looper; -import android.os.RemoteException; -import android.text.TextUtils; -import android.util.AndroidRuntimeException; -import android.util.Log; -import android.util.SparseArray; - -import com.android.connectivity.resources.R; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; -import com.android.net.module.util.InterfaceParams; - -import java.io.FileDescriptor; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; - -/** - * {@link NetworkFactory} that represents Ethernet networks. - * - * This class reports a static network score of 70 when it is tracking an interface and that - * interface's link is up, and a score of 0 otherwise. - */ -public class EthernetNetworkFactory extends NetworkFactory { - private final static String TAG = EthernetNetworkFactory.class.getSimpleName(); - final static boolean DBG = true; - - private final static int NETWORK_SCORE = 70; - private static final String NETWORK_TYPE = "Ethernet"; - private static final String LEGACY_TCP_BUFFER_SIZES = - "524288,1048576,3145728,524288,1048576,2097152"; - - private final ConcurrentHashMap mTrackingInterfaces = - new ConcurrentHashMap<>(); - private final Handler mHandler; - private final Context mContext; - final Dependencies mDeps; - - public static class Dependencies { - public void makeIpClient(Context context, String iface, IpClientCallbacks callbacks) { - IpClientUtil.makeIpClient(context, iface, callbacks); - } - - public IpClientManager makeIpClientManager(@NonNull final IIpClient ipClient) { - return new IpClientManager(ipClient, TAG); - } - - public EthernetNetworkAgent makeEthernetNetworkAgent(Context context, Looper looper, - NetworkCapabilities nc, LinkProperties lp, NetworkAgentConfig config, - NetworkProvider provider, EthernetNetworkAgent.Callbacks cb) { - return new EthernetNetworkAgent(context, looper, nc, lp, config, provider, cb); - } - - public InterfaceParams getNetworkInterfaceByName(String name) { - return InterfaceParams.getByName(name); - } - - // TODO: remove legacy resource fallback after migrating its overlays. - private String getPlatformTcpBufferSizes(Context context) { - final Resources r = context.getResources(); - final int resId = r.getIdentifier("config_ethernet_tcp_buffers", "string", - context.getPackageName()); - return r.getString(resId); - } - - public String getTcpBufferSizesFromResource(Context context) { - final String tcpBufferSizes; - final String platformTcpBufferSizes = getPlatformTcpBufferSizes(context); - if (!LEGACY_TCP_BUFFER_SIZES.equals(platformTcpBufferSizes)) { - // Platform resource is not the historical default: use the overlay. - tcpBufferSizes = platformTcpBufferSizes; - } else { - final ConnectivityResources resources = new ConnectivityResources(context); - tcpBufferSizes = resources.get().getString(R.string.config_ethernet_tcp_buffers); - } - return tcpBufferSizes; - } - } - - public static class ConfigurationException extends AndroidRuntimeException { - public ConfigurationException(String msg) { - super(msg); - } - } - - public EthernetNetworkFactory(Handler handler, Context context) { - this(handler, context, new Dependencies()); - } - - @VisibleForTesting - EthernetNetworkFactory(Handler handler, Context context, Dependencies deps) { - super(handler.getLooper(), context, NETWORK_TYPE, createDefaultNetworkCapabilities()); - - mHandler = handler; - mContext = context; - mDeps = deps; - - setScoreFilter(NETWORK_SCORE); - } - - @Override - public boolean acceptRequest(NetworkRequest request) { - if (DBG) { - Log.d(TAG, "acceptRequest, request: " + request); - } - - return networkForRequest(request) != null; - } - - @Override - protected void needNetworkFor(NetworkRequest networkRequest) { - NetworkInterfaceState network = networkForRequest(networkRequest); - - if (network == null) { - Log.e(TAG, "needNetworkFor, failed to get a network for " + networkRequest); - return; - } - - if (++network.refCount == 1) { - network.start(); - } - } - - @Override - protected void releaseNetworkFor(NetworkRequest networkRequest) { - NetworkInterfaceState network = networkForRequest(networkRequest); - if (network == null) { - Log.e(TAG, "releaseNetworkFor, failed to get a network for " + networkRequest); - return; - } - - if (--network.refCount == 0) { - network.stop(); - } - } - - /** - * Returns an array of available interface names. The array is sorted: unrestricted interfaces - * goes first, then sorted by name. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected String[] getAvailableInterfaces(boolean includeRestricted) { - return mTrackingInterfaces.values() - .stream() - .filter(iface -> !iface.isRestricted() || includeRestricted) - .sorted((iface1, iface2) -> { - int r = Boolean.compare(iface1.isRestricted(), iface2.isRestricted()); - return r == 0 ? iface1.name.compareTo(iface2.name) : r; - }) - .map(iface -> iface.name) - .toArray(String[]::new); - } - - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected 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; - } - - final NetworkCapabilities nc = new NetworkCapabilities.Builder(capabilities) - .setNetworkSpecifier(new EthernetNetworkSpecifier(ifaceName)) - .build(); - - if (DBG) { - Log.d(TAG, "addInterface, iface: " + ifaceName + ", capabilities: " + nc); - } - - final NetworkInterfaceState iface = new NetworkInterfaceState( - ifaceName, hwAddress, mHandler, mContext, ipConfig, nc, this, mDeps); - mTrackingInterfaces.put(ifaceName, iface); - updateCapabilityFilter(); - } - - @VisibleForTesting - protected int getInterfaceState(@NonNull String iface) { - final NetworkInterfaceState interfaceState = mTrackingInterfaces.get(iface); - if (interfaceState == null) { - return EthernetManager.STATE_ABSENT; - } else if (!interfaceState.mLinkUp) { - return EthernetManager.STATE_LINK_DOWN; - } else { - return EthernetManager.STATE_LINK_UP; - } - } - - /** - * 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 or null. If - * {@code null} is passed, the existing IpConfiguration is not updated. - * @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 INetworkInterfaceOutcomeReceiver} to notify callers of - * completion. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected void updateInterface(@NonNull final String ifaceName, - @Nullable final IpConfiguration ipConfig, - @Nullable final NetworkCapabilities capabilities, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (!hasInterface(ifaceName)) { - maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); - return; - } - - final NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName); - iface.updateInterface(ipConfig, capabilities, listener); - mTrackingInterfaces.put(ifaceName, iface); - updateCapabilityFilter(); - } - - private static NetworkCapabilities mixInCapabilities(NetworkCapabilities nc, - NetworkCapabilities addedNc) { - final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(nc); - for (int transport : addedNc.getTransportTypes()) builder.addTransportType(transport); - for (int capability : addedNc.getCapabilities()) builder.addCapability(capability); - return builder.build(); - } - - private void updateCapabilityFilter() { - NetworkCapabilities capabilitiesFilter = createDefaultNetworkCapabilities(); - for (NetworkInterfaceState iface: mTrackingInterfaces.values()) { - capabilitiesFilter = mixInCapabilities(capabilitiesFilter, iface.mCapabilities); - } - - if (DBG) Log.d(TAG, "updateCapabilityFilter: " + capabilitiesFilter); - setCapabilityFilter(capabilitiesFilter); - } - - private static NetworkCapabilities createDefaultNetworkCapabilities() { - return NetworkCapabilities.Builder - .withoutDefaultCapabilities() - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET).build(); - } - - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected void removeInterface(String interfaceName) { - NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName); - if (iface != null) { - iface.maybeSendNetworkManagementCallbackForAbort(); - iface.stop(); - } - - updateCapabilityFilter(); - } - - /** Returns true if state has been modified */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected boolean updateInterfaceLinkState(@NonNull final String ifaceName, final boolean up, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (!hasInterface(ifaceName)) { - maybeSendNetworkManagementCallbackForUntracked(ifaceName, listener); - return false; - } - - if (DBG) { - Log.d(TAG, "updateInterfaceLinkState, iface: " + ifaceName + ", up: " + up); - } - - NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName); - return iface.updateLinkState(up, listener); - } - - private void maybeSendNetworkManagementCallbackForUntracked( - String ifaceName, INetworkInterfaceOutcomeReceiver listener) { - maybeSendNetworkManagementCallback(listener, null, - new EthernetNetworkManagementException( - ifaceName + " can't be updated as it is not available.")); - } - - @VisibleForTesting - protected boolean hasInterface(String ifaceName) { - return mTrackingInterfaces.containsKey(ifaceName); - } - - private NetworkInterfaceState networkForRequest(NetworkRequest request) { - String requestedIface = null; - - NetworkSpecifier specifier = request.getNetworkSpecifier(); - if (specifier instanceof EthernetNetworkSpecifier) { - requestedIface = ((EthernetNetworkSpecifier) specifier) - .getInterfaceName(); - } - - NetworkInterfaceState network = null; - if (!TextUtils.isEmpty(requestedIface)) { - NetworkInterfaceState n = mTrackingInterfaces.get(requestedIface); - if (n != null && request.canBeSatisfiedBy(n.mCapabilities)) { - network = n; - } - } else { - for (NetworkInterfaceState n : mTrackingInterfaces.values()) { - if (request.canBeSatisfiedBy(n.mCapabilities) && n.mLinkUp) { - network = n; - break; - } - } - } - - if (DBG) { - Log.i(TAG, "networkForRequest, request: " + request + ", network: " + network); - } - - return network; - } - - private static void maybeSendNetworkManagementCallback( - @Nullable final INetworkInterfaceOutcomeReceiver listener, - @Nullable final String iface, - @Nullable final EthernetNetworkManagementException e) { - if (null == listener) { - return; - } - - try { - if (iface != null) { - listener.onResult(iface); - } else { - listener.onError(e); - } - } catch (RemoteException re) { - Log.e(TAG, "Can't send onComplete for network management callback", re); - } - } - - @VisibleForTesting - static class NetworkInterfaceState { - final String name; - - private final String mHwAddress; - private final Handler mHandler; - private final Context mContext; - private final NetworkFactory mNetworkFactory; - private final Dependencies mDeps; - - 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 EthernetIpClientCallback mIpClientCallback; - private @Nullable EthernetNetworkAgent mNetworkAgent; - private @Nullable IpConfiguration mIpConfig; - - /** - * A map of TRANSPORT_* types to legacy transport types available for each type an ethernet - * interface could propagate. - * - * There are no legacy type equivalents to LOWPAN or WIFI_AWARE. These types are set to - * TYPE_NONE to match the behavior of their own network factories. - */ - private static final SparseArray sTransports = new SparseArray(); - static { - sTransports.put(NetworkCapabilities.TRANSPORT_ETHERNET, - ConnectivityManager.TYPE_ETHERNET); - sTransports.put(NetworkCapabilities.TRANSPORT_BLUETOOTH, - ConnectivityManager.TYPE_BLUETOOTH); - sTransports.put(NetworkCapabilities.TRANSPORT_WIFI, ConnectivityManager.TYPE_WIFI); - sTransports.put(NetworkCapabilities.TRANSPORT_CELLULAR, - ConnectivityManager.TYPE_MOBILE); - sTransports.put(NetworkCapabilities.TRANSPORT_LOWPAN, ConnectivityManager.TYPE_NONE); - sTransports.put(NetworkCapabilities.TRANSPORT_WIFI_AWARE, - ConnectivityManager.TYPE_NONE); - } - - long refCount = 0; - - private class EthernetIpClientCallback extends IpClientCallbacks { - private final ConditionVariable mIpClientStartCv = new ConditionVariable(false); - private final ConditionVariable mIpClientShutdownCv = new ConditionVariable(false); - @Nullable INetworkInterfaceOutcomeReceiver mNetworkManagementListener; - - EthernetIpClientCallback(@Nullable final INetworkInterfaceOutcomeReceiver listener) { - mNetworkManagementListener = listener; - } - - @Override - public void onIpClientCreated(IIpClient ipClient) { - mIpClient = mDeps.makeIpClientManager(ipClient); - mIpClientStartCv.open(); - } - - private void awaitIpClientStart() { - mIpClientStartCv.block(); - } - - private void awaitIpClientShutdown() { - mIpClientShutdownCv.block(); - } - - // At the time IpClient is stopped, an IpClient event may have already been posted on - // the back of the handler and is awaiting execution. Once that event is executed, the - // associated callback object may not be valid anymore - // (NetworkInterfaceState#mIpClientCallback points to a different object / null). - private boolean isCurrentCallback() { - return this == mIpClientCallback; - } - - private void handleIpEvent(final @NonNull Runnable r) { - mHandler.post(() -> { - if (!isCurrentCallback()) { - Log.i(TAG, "Ignoring stale IpClientCallbacks " + this); - return; - } - r.run(); - }); - } - - @Override - public void onProvisioningSuccess(LinkProperties newLp) { - handleIpEvent(() -> onIpLayerStarted(newLp, mNetworkManagementListener)); - } - - @Override - public void onProvisioningFailure(LinkProperties newLp) { - // This cannot happen due to provisioning timeout, because our timeout is 0. It can - // happen due to errors while provisioning or on provisioning loss. - handleIpEvent(() -> onIpLayerStopped(mNetworkManagementListener)); - } - - @Override - public void onLinkPropertiesChange(LinkProperties newLp) { - handleIpEvent(() -> updateLinkProperties(newLp)); - } - - @Override - public void onReachabilityLost(String logMsg) { - handleIpEvent(() -> updateNeighborLostEvent(logMsg)); - } - - @Override - public void onQuit() { - mIpClient = null; - mIpClientShutdownCv.open(); - } - } - - NetworkInterfaceState(String ifaceName, String hwAddress, Handler handler, Context context, - @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; - mHwAddress = hwAddress; - } - - /** - * Determines the legacy transport type from a NetworkCapabilities transport type. Defaults - * to legacy TYPE_NONE if there is no known conversion - */ - private static int getLegacyType(int transport) { - 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); - } - - void updateInterface(@Nullable final IpConfiguration ipConfig, - @Nullable final NetworkCapabilities capabilities, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (DBG) { - Log.d(TAG, "updateInterface, iface: " + name - + ", ipConfig: " + ipConfig + ", old ipConfig: " + mIpConfig - + ", capabilities: " + capabilities + ", old capabilities: " + mCapabilities - + ", listener: " + listener - ); - } - - if (null != ipConfig){ - mIpConfig = ipConfig; - } - if (null != capabilities) { - 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() { - return !mCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); - } - - private void start() { - start(null); - } - - private void start(@Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (mIpClient != null) { - if (DBG) Log.d(TAG, "IpClient already started"); - return; - } - if (DBG) { - Log.d(TAG, String.format("Starting Ethernet IpClient(%s)", name)); - } - - mIpClientCallback = new EthernetIpClientCallback(listener); - mDeps.makeIpClient(mContext, name, mIpClientCallback); - mIpClientCallback.awaitIpClientStart(); - - if (sTcpBufferSizes == null) { - sTcpBufferSizes = mDeps.getTcpBufferSizesFromResource(mContext); - } - provisionIpClient(mIpClient, mIpConfig, sTcpBufferSizes); - } - - void onIpLayerStarted(@NonNull final LinkProperties linkProperties, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (mNetworkAgent != null) { - Log.e(TAG, "Already have a NetworkAgent - aborting new request"); - stop(); - return; - } - mLinkProperties = linkProperties; - - // Create our NetworkAgent. - final NetworkAgentConfig config = new NetworkAgentConfig.Builder() - .setLegacyType(mLegacyType) - .setLegacyTypeName(NETWORK_TYPE) - .setLegacyExtraInfo(mHwAddress) - .build(); - mNetworkAgent = mDeps.makeEthernetNetworkAgent(mContext, mHandler.getLooper(), - mCapabilities, mLinkProperties, config, mNetworkFactory.getProvider(), - new EthernetNetworkAgent.Callbacks() { - @Override - public void onNetworkUnwanted() { - // if mNetworkAgent is null, we have already called stop. - if (mNetworkAgent == null) return; - - if (this == mNetworkAgent.getCallbacks()) { - stop(); - } else { - Log.d(TAG, "Ignoring unwanted as we have a more modern " + - "instance"); - } - } - }); - mNetworkAgent.register(); - mNetworkAgent.markConnected(); - realizeNetworkManagementCallback(name, null); - } - - void onIpLayerStopped(@Nullable final INetworkInterfaceOutcomeReceiver listener) { - // 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 EthernetNetworkManagementException( - "The IP provisioning request has been aborted.")); - } - - // Must be called on the handler thread - private void realizeNetworkManagementCallback(@Nullable final String iface, - @Nullable final EthernetNetworkManagementException e) { - ensureRunningOnEthernetHandlerThread(); - if (null == mIpClientCallback) { - return; - } - - EthernetNetworkFactory.maybeSendNetworkManagementCallback( - mIpClientCallback.mNetworkManagementListener, iface, 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()); - } - } - - void updateLinkProperties(LinkProperties linkProperties) { - mLinkProperties = linkProperties; - if (mNetworkAgent != null) { - mNetworkAgent.sendLinkPropertiesImpl(linkProperties); - } - } - - void updateNeighborLostEvent(String logMsg) { - Log.i(TAG, "updateNeighborLostEvent " + logMsg); - // Reachability lost will be seen only if the gateway is not reachable. - // Since ethernet FW doesn't have the mechanism to scan for new networks - // like WiFi, simply restart. - // If there is a better network, that will become default and apps - // will be able to use internet. If ethernet gets connected again, - // and has backhaul connectivity, it will become default. - restart(); - } - - /** Returns true if state has been modified */ - boolean updateLinkState(final boolean up, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (mLinkUp == up) { - EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, null, - new EthernetNetworkManagementException( - "No changes with requested link state " + up + " for " + name)); - return false; - } - mLinkUp = up; - - if (!up) { // was up, goes down - // Send an abort on a provisioning request callback if necessary before stopping. - maybeSendNetworkManagementCallbackForAbort(); - stop(); - // If only setting the interface down, send a callback to signal completion. - EthernetNetworkFactory.maybeSendNetworkManagementCallback(listener, name, null); - } else { // was down, goes up - stop(); - start(listener); - } - - return true; - } - - void stop() { - // Invalidate all previous start requests - if (mIpClient != null) { - mIpClient.shutdown(); - mIpClientCallback.awaitIpClientShutdown(); - mIpClient = null; - } - mIpClientCallback = null; - - if (mNetworkAgent != null) { - mNetworkAgent.unregister(); - mNetworkAgent = null; - } - mLinkProperties.clear(); - } - - private static void provisionIpClient(@NonNull final IpClientManager ipClient, - @NonNull final IpConfiguration config, @NonNull final String tcpBufferSizes) { - if (config.getProxySettings() == ProxySettings.STATIC || - config.getProxySettings() == ProxySettings.PAC) { - ipClient.setHttpProxy(config.getHttpProxy()); - } - - if (!TextUtils.isEmpty(tcpBufferSizes)) { - ipClient.setTcpBufferSizes(tcpBufferSizes); - } - - ipClient.startProvisioning(createProvisioningConfiguration(config)); - } - - private static ProvisioningConfiguration createProvisioningConfiguration( - @NonNull final IpConfiguration config) { - if (config.getIpAssignment() == IpAssignment.STATIC) { - return new ProvisioningConfiguration.Builder() - .withStaticConfiguration(config.getStaticIpConfiguration()) - .build(); - } - return new ProvisioningConfiguration.Builder() - .withProvisioningTimeoutMs(0) - .build(); - } - - void restart() { - restart(null); - } - - void restart(@Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (DBG) Log.d(TAG, "reconnecting Ethernet"); - stop(); - start(listener); - } - - @Override - public String toString() { - return getClass().getSimpleName() + "{ " - + "refCount: " + refCount + ", " - + "iface: " + name + ", " - + "up: " + mLinkUp + ", " - + "hwAddress: " + mHwAddress + ", " - + "networkCapabilities: " + mCapabilities + ", " - + "networkAgent: " + mNetworkAgent + ", " - + "ipClient: " + mIpClient + "," - + "linkProperties: " + mLinkProperties - + "}"; - } - } - - void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { - super.dump(fd, pw, args); - pw.println(getClass().getSimpleName()); - pw.println("Tracking interfaces:"); - pw.increaseIndent(); - for (String iface: mTrackingInterfaces.keySet()) { - NetworkInterfaceState ifaceState = mTrackingInterfaces.get(iface); - pw.println(iface + ":" + ifaceState); - pw.increaseIndent(); - if (null == ifaceState.mIpClient) { - pw.println("IpClient is null"); - } - pw.decreaseIndent(); - } - pw.decreaseIndent(); - } -} diff --git a/java/com/android/server/ethernet/EthernetService.java b/java/com/android/server/ethernet/EthernetService.java deleted file mode 100644 index d405fd5..0000000 --- a/java/com/android/server/ethernet/EthernetService.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2014 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 android.content.Context; -import android.net.INetd; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; - -import java.util.Objects; - -// TODO: consider renaming EthernetServiceImpl to EthernetService and deleting this file. -public final class EthernetService { - private static final String TAG = "EthernetService"; - private static final String THREAD_NAME = "EthernetServiceThread"; - - private static INetd getNetd(Context context) { - final INetd netd = - INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)); - Objects.requireNonNull(netd, "could not get netd instance"); - return netd; - } - - public static EthernetServiceImpl create(Context context) { - final HandlerThread handlerThread = new HandlerThread(THREAD_NAME); - handlerThread.start(); - final Handler handler = new Handler(handlerThread.getLooper()); - final EthernetNetworkFactory factory = new EthernetNetworkFactory(handler, context); - return new EthernetServiceImpl(context, handler, - new EthernetTracker(context, handler, factory, getNetd(context))); - } -} diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java deleted file mode 100644 index 5e830ad..0000000 --- a/java/com/android/server/ethernet/EthernetServiceImpl.java +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright (C) 2014 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 android.net.NetworkCapabilities.TRANSPORT_TEST; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.content.pm.PackageManager; -import android.net.IEthernetManager; -import android.net.IEthernetServiceListener; -import android.net.INetworkInterfaceOutcomeReceiver; -import android.net.ITetheredInterfaceCallback; -import android.net.EthernetNetworkUpdateRequest; -import android.net.IpConfiguration; -import android.net.NetworkCapabilities; -import android.os.Binder; -import android.os.Handler; -import android.os.RemoteException; -import android.util.Log; -import android.util.PrintWriterPrinter; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; -import com.android.net.module.util.PermissionUtils; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * EthernetServiceImpl handles remote Ethernet operation requests by implementing - * the IEthernetManager interface. - */ -public class EthernetServiceImpl extends IEthernetManager.Stub { - private static final String TAG = "EthernetServiceImpl"; - - @VisibleForTesting - final AtomicBoolean mStarted = new AtomicBoolean(false); - private final Context mContext; - private final Handler mHandler; - private final EthernetTracker mTracker; - - EthernetServiceImpl(@NonNull final Context context, @NonNull final Handler handler, - @NonNull final EthernetTracker tracker) { - mContext = context; - mHandler = handler; - mTracker = tracker; - } - - private void enforceAutomotiveDevice(final @NonNull String methodName) { - PermissionUtils.enforceSystemFeature(mContext, PackageManager.FEATURE_AUTOMOTIVE, - methodName + " is only available on automotive devices."); - } - - private boolean checkUseRestrictedNetworksPermission() { - return PermissionUtils.checkAnyPermissionOf(mContext, - android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS); - } - - public void start() { - Log.i(TAG, "Starting Ethernet service"); - mTracker.start(); - mStarted.set(true); - } - - private void throwIfEthernetNotStarted() { - if (!mStarted.get()) { - throw new IllegalStateException("System isn't ready to change ethernet configurations"); - } - } - - @Override - public String[] getAvailableInterfaces() throws RemoteException { - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - return mTracker.getInterfaces(checkUseRestrictedNetworksPermission()); - } - - /** - * Get Ethernet configuration - * @return the Ethernet Configuration, contained in {@link IpConfiguration}. - */ - @Override - public IpConfiguration getConfiguration(String iface) { - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - if (mTracker.isRestrictedInterface(iface)) { - PermissionUtils.enforceRestrictedNetworkPermission(mContext, TAG); - } - - return new IpConfiguration(mTracker.getIpConfiguration(iface)); - } - - /** - * Set Ethernet configuration - */ - @Override - public void setConfiguration(String iface, IpConfiguration config) { - throwIfEthernetNotStarted(); - - PermissionUtils.enforceNetworkStackPermission(mContext); - if (mTracker.isRestrictedInterface(iface)) { - PermissionUtils.enforceRestrictedNetworkPermission(mContext, TAG); - } - - // TODO: this does not check proxy settings, gateways, etc. - // Fix this by making IpConfiguration a complete representation of static configuration. - mTracker.updateIpConfiguration(iface, new IpConfiguration(config)); - } - - /** - * Indicates whether given interface is available. - */ - @Override - public boolean isAvailable(String iface) { - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - if (mTracker.isRestrictedInterface(iface)) { - PermissionUtils.enforceRestrictedNetworkPermission(mContext, TAG); - } - - return mTracker.isTrackingInterface(iface); - } - - /** - * Adds a listener. - * @param listener A {@link IEthernetServiceListener} to add. - */ - public void addListener(IEthernetServiceListener listener) throws RemoteException { - Objects.requireNonNull(listener, "listener must not be null"); - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - mTracker.addListener(listener, checkUseRestrictedNetworksPermission()); - } - - /** - * Removes a listener. - * @param listener A {@link IEthernetServiceListener} to remove. - */ - public void removeListener(IEthernetServiceListener listener) { - if (listener == null) { - throw new IllegalArgumentException("listener must not be null"); - } - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - mTracker.removeListener(listener); - } - - @Override - public void setIncludeTestInterfaces(boolean include) { - PermissionUtils.enforceNetworkStackPermissionOr(mContext, - android.Manifest.permission.NETWORK_SETTINGS); - mTracker.setIncludeTestInterfaces(include); - } - - @Override - public void requestTetheredInterface(ITetheredInterfaceCallback callback) { - Objects.requireNonNull(callback, "callback must not be null"); - PermissionUtils.enforceNetworkStackPermissionOr(mContext, - android.Manifest.permission.NETWORK_SETTINGS); - mTracker.requestTetheredInterface(callback); - } - - @Override - public void releaseTetheredInterface(ITetheredInterfaceCallback callback) { - Objects.requireNonNull(callback, "callback must not be null"); - PermissionUtils.enforceNetworkStackPermissionOr(mContext, - android.Manifest.permission.NETWORK_SETTINGS); - mTracker.releaseTetheredInterface(callback); - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump EthernetService from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid()); - return; - } - - pw.println("Current Ethernet state: "); - pw.increaseIndent(); - mTracker.dump(fd, pw, args); - pw.decreaseIndent(); - - pw.println("Handler:"); - pw.increaseIndent(); - mHandler.dump(new PrintWriterPrinter(pw), "EthernetServiceImpl"); - pw.decreaseIndent(); - } - - private void enforceNetworkManagementPermission() { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.MANAGE_ETHERNET_NETWORKS, - "EthernetServiceImpl"); - } - - private void enforceManageTestNetworksPermission() { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.MANAGE_TEST_NETWORKS, - "EthernetServiceImpl"); - } - - private void maybeValidateTestCapabilities(final String iface, - @Nullable final NetworkCapabilities nc) { - if (!mTracker.isValidTestInterface(iface)) { - return; - } - // For test interfaces, only null or capabilities that include TRANSPORT_TEST are - // allowed. - if (nc != null && !nc.hasTransport(TRANSPORT_TEST)) { - throw new IllegalArgumentException( - "Updates to test interfaces must have NetworkCapabilities.TRANSPORT_TEST."); - } - } - - private void enforceAdminPermission(final String iface, boolean enforceAutomotive, - final String logMessage) { - if (mTracker.isValidTestInterface(iface)) { - enforceManageTestNetworksPermission(); - } else { - enforceNetworkManagementPermission(); - if (enforceAutomotive) { - enforceAutomotiveDevice(logMessage); - } - } - } - - @Override - public void updateConfiguration(@NonNull final String iface, - @NonNull final EthernetNetworkUpdateRequest request, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - Objects.requireNonNull(iface); - Objects.requireNonNull(request); - throwIfEthernetNotStarted(); - - // TODO: validate that iface is listed in overlay config_ethernet_interfaces - // only automotive devices are allowed to set the NetworkCapabilities using this API - enforceAdminPermission(iface, request.getNetworkCapabilities() != null, - "updateConfiguration() with non-null capabilities"); - maybeValidateTestCapabilities(iface, request.getNetworkCapabilities()); - - mTracker.updateConfiguration( - iface, request.getIpConfiguration(), request.getNetworkCapabilities(), listener); - } - - @Override - public void connectNetwork(@NonNull final String iface, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - Log.i(TAG, "connectNetwork called with: iface=" + iface + ", listener=" + listener); - Objects.requireNonNull(iface); - throwIfEthernetNotStarted(); - - enforceAdminPermission(iface, true, "connectNetwork()"); - - mTracker.connectNetwork(iface, listener); - } - - @Override - public void disconnectNetwork(@NonNull final String iface, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - Log.i(TAG, "disconnectNetwork called with: iface=" + iface + ", listener=" + listener); - Objects.requireNonNull(iface); - throwIfEthernetNotStarted(); - - enforceAdminPermission(iface, true, "connectNetwork()"); - - mTracker.disconnectNetwork(iface, listener); - } - - @Override - public void setEthernetEnabled(boolean enabled) { - PermissionUtils.enforceNetworkStackPermissionOr(mContext, - android.Manifest.permission.NETWORK_SETTINGS); - - mTracker.setEthernetEnabled(enabled); - } - - @Override - public List getInterfaceList() { - PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG); - return mTracker.getInterfaceList(); - } -} diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java deleted file mode 100644 index c291b3f..0000000 --- a/java/com/android/server/ethernet/EthernetTracker.java +++ /dev/null @@ -1,942 +0,0 @@ -/* - * Copyright (C) 2018 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 android.net.EthernetManager.ETHERNET_STATE_DISABLED; -import static android.net.EthernetManager.ETHERNET_STATE_ENABLED; -import static android.net.TestNetworkManager.TEST_TAP_PREFIX; - -import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.content.res.Resources; -import android.net.ConnectivityResources; -import android.net.EthernetManager; -import android.net.IEthernetServiceListener; -import android.net.INetworkInterfaceOutcomeReceiver; -import android.net.INetd; -import android.net.ITetheredInterfaceCallback; -import android.net.InterfaceConfigurationParcel; -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.ConditionVariable; -import android.os.Handler; -import android.os.RemoteCallbackList; -import android.os.RemoteException; -import android.os.ServiceSpecificException; -import android.text.TextUtils; -import android.util.ArrayMap; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; -import com.android.net.module.util.BaseNetdUnsolicitedEventListener; -import com.android.net.module.util.NetdUtils; -import com.android.net.module.util.PermissionUtils; - -import java.io.FileDescriptor; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Tracks Ethernet interfaces and manages interface configurations. - * - *

Interfaces may have different {@link android.net.NetworkCapabilities}. This mapping is defined - * in {@code config_ethernet_interfaces}. Notably, some interfaces could be marked as restricted by - * not specifying {@link android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED} flag. - * Interfaces could have associated {@link android.net.IpConfiguration}. - * Ethernet Interfaces may be present at boot time or appear after boot (e.g., for Ethernet adapters - * connected over USB). This class supports multiple interfaces. When an interface appears on the - * system (or is present at boot time) this class will start tracking it and bring it up. Only - * interfaces whose names match the {@code config_ethernet_iface_regex} regular expression are - * tracked. - * - *

All public or package private methods must be thread-safe unless stated otherwise. - */ -@VisibleForTesting(visibility = PACKAGE) -public class EthernetTracker { - private static final int INTERFACE_MODE_CLIENT = 1; - private static final int INTERFACE_MODE_SERVER = 2; - - private static final String TAG = EthernetTracker.class.getSimpleName(); - private static final boolean DBG = EthernetNetworkFactory.DBG; - - private static final String TEST_IFACE_REGEXP = TEST_TAP_PREFIX + "\\d+"; - private static final String LEGACY_IFACE_REGEXP = "eth\\d"; - - /** - * Interface names we track. This is a product-dependent regular expression, plus, - * if setIncludeTestInterfaces is true, any test interfaces. - */ - private volatile String mIfaceMatch; - /** - * Track test interfaces if true, don't track otherwise. - */ - private boolean mIncludeTestInterfaces = false; - - /** Mapping between {iface name | mac address} -> {NetworkCapabilities} */ - private final ConcurrentHashMap mNetworkCapabilities = - new ConcurrentHashMap<>(); - private final ConcurrentHashMap mIpConfigurations = - new ConcurrentHashMap<>(); - - private final Context mContext; - private final INetd mNetd; - private final Handler mHandler; - private final EthernetNetworkFactory mFactory; - private final EthernetConfigStore mConfigStore; - private final Dependencies mDeps; - - private final RemoteCallbackList mListeners = - new RemoteCallbackList<>(); - private final TetheredInterfaceRequestList mTetheredInterfaceRequests = - new TetheredInterfaceRequestList(); - - // Used only on the handler thread - private String mDefaultInterface; - private int mDefaultInterfaceMode = INTERFACE_MODE_CLIENT; - // Tracks whether clients were notified that the tethered interface is available - private boolean mTetheredInterfaceWasAvailable = false; - private volatile IpConfiguration mIpConfigForDefaultInterface; - - private int mEthernetState = ETHERNET_STATE_ENABLED; - - private class TetheredInterfaceRequestList extends - RemoteCallbackList { - @Override - public void onCallbackDied(ITetheredInterfaceCallback cb, Object cookie) { - mHandler.post(EthernetTracker.this::maybeUntetherDefaultInterface); - } - } - - public static class Dependencies { - // TODO: remove legacy resource fallback after migrating its overlays. - private String getPlatformRegexResource(Context context) { - final Resources r = context.getResources(); - final int resId = - r.getIdentifier("config_ethernet_iface_regex", "string", context.getPackageName()); - return r.getString(resId); - } - - // TODO: remove legacy resource fallback after migrating its overlays. - private String[] getPlatformInterfaceConfigs(Context context) { - final Resources r = context.getResources(); - final int resId = r.getIdentifier("config_ethernet_interfaces", "array", - context.getPackageName()); - return r.getStringArray(resId); - } - - public String getInterfaceRegexFromResource(Context context) { - final String platformRegex = getPlatformRegexResource(context); - final String match; - if (!LEGACY_IFACE_REGEXP.equals(platformRegex)) { - // Platform resource is not the historical default: use the overlay - match = platformRegex; - } else { - final ConnectivityResources resources = new ConnectivityResources(context); - match = resources.get().getString( - com.android.connectivity.resources.R.string.config_ethernet_iface_regex); - } - return match; - } - - public String[] getInterfaceConfigFromResource(Context context) { - final String[] platformInterfaceConfigs = getPlatformInterfaceConfigs(context); - final String[] interfaceConfigs; - if (platformInterfaceConfigs.length != 0) { - // Platform resource is not the historical default: use the overlay - interfaceConfigs = platformInterfaceConfigs; - } else { - final ConnectivityResources resources = new ConnectivityResources(context); - interfaceConfigs = resources.get().getStringArray( - com.android.connectivity.resources.R.array.config_ethernet_interfaces); - } - return interfaceConfigs; - } - } - - EthernetTracker(@NonNull final Context context, @NonNull final Handler handler, - @NonNull final EthernetNetworkFactory factory, @NonNull final INetd netd) { - this(context, handler, factory, netd, new Dependencies()); - } - - @VisibleForTesting - EthernetTracker(@NonNull final Context context, @NonNull final Handler handler, - @NonNull final EthernetNetworkFactory factory, @NonNull final INetd netd, - @NonNull final Dependencies deps) { - mContext = context; - mHandler = handler; - mFactory = factory; - mNetd = netd; - mDeps = deps; - - // Interface match regex. - updateIfaceMatchRegexp(); - - // Read default Ethernet interface configuration from resources - final String[] interfaceConfigs = mDeps.getInterfaceConfigFromResource(context); - for (String strConfig : interfaceConfigs) { - parseEthernetConfig(strConfig); - } - - mConfigStore = new EthernetConfigStore(); - } - - void start() { - mFactory.register(); - mConfigStore.read(); - - // Default interface is just the first one we want to track. - mIpConfigForDefaultInterface = mConfigStore.getIpConfigurationForDefaultInterface(); - final ArrayMap configs = mConfigStore.getIpConfigurations(); - for (int i = 0; i < configs.size(); i++) { - mIpConfigurations.put(configs.keyAt(i), configs.valueAt(i)); - } - - try { - PermissionUtils.enforceNetworkStackPermission(mContext); - mNetd.registerUnsolicitedEventListener(new InterfaceObserver()); - } catch (RemoteException | ServiceSpecificException e) { - Log.e(TAG, "Could not register InterfaceObserver " + e); - } - - mHandler.post(this::trackAvailableInterfaces); - } - - void updateIpConfiguration(String iface, IpConfiguration ipConfiguration) { - if (DBG) { - Log.i(TAG, "updateIpConfiguration, iface: " + iface + ", cfg: " + ipConfiguration); - } - writeIpConfiguration(iface, ipConfiguration); - mHandler.post(() -> { - mFactory.updateInterface(iface, ipConfiguration, null, null); - broadcastInterfaceStateChange(iface); - }); - } - - private void writeIpConfiguration(@NonNull final String iface, - @NonNull final IpConfiguration ipConfig) { - mConfigStore.write(iface, ipConfig); - mIpConfigurations.put(iface, ipConfig); - } - - private IpConfiguration getIpConfigurationForCallback(String iface, int state) { - return (state == EthernetManager.STATE_ABSENT) ? null : getOrCreateIpConfiguration(iface); - } - - private void ensureRunningOnEthernetServiceThread() { - if (mHandler.getLooper().getThread() != Thread.currentThread()) { - throw new IllegalStateException( - "Not running on EthernetService thread: " - + Thread.currentThread().getName()); - } - } - - /** - * Broadcast the link state or IpConfiguration change of existing Ethernet interfaces to all - * listeners. - */ - protected void broadcastInterfaceStateChange(@NonNull String iface) { - ensureRunningOnEthernetServiceThread(); - final int state = mFactory.getInterfaceState(iface); - final int role = getInterfaceRole(iface); - final IpConfiguration config = getIpConfigurationForCallback(iface, state); - final int n = mListeners.beginBroadcast(); - for (int i = 0; i < n; i++) { - try { - mListeners.getBroadcastItem(i).onInterfaceStateChanged(iface, state, role, config); - } catch (RemoteException e) { - // Do nothing here. - } - } - mListeners.finishBroadcast(); - } - - /** - * Unicast the interface state or IpConfiguration change of existing Ethernet interfaces to a - * specific listener. - */ - protected void unicastInterfaceStateChange(@NonNull IEthernetServiceListener listener, - @NonNull String iface) { - ensureRunningOnEthernetServiceThread(); - final int state = mFactory.getInterfaceState(iface); - final int role = getInterfaceRole(iface); - final IpConfiguration config = getIpConfigurationForCallback(iface, state); - try { - listener.onInterfaceStateChanged(iface, state, role, config); - } catch (RemoteException e) { - // Do nothing here. - } - } - - @VisibleForTesting(visibility = PACKAGE) - protected void updateConfiguration(@NonNull final String iface, - @Nullable final IpConfiguration ipConfig, - @Nullable final NetworkCapabilities capabilities, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - if (DBG) { - Log.i(TAG, "updateConfiguration, iface: " + iface + ", capabilities: " + capabilities - + ", ipConfig: " + ipConfig); - } - - final IpConfiguration localIpConfig = ipConfig == null - ? null : new IpConfiguration(ipConfig); - if (ipConfig != null) { - writeIpConfiguration(iface, localIpConfig); - } - - if (null != capabilities) { - mNetworkCapabilities.put(iface, capabilities); - } - mHandler.post(() -> { - mFactory.updateInterface(iface, localIpConfig, capabilities, listener); - broadcastInterfaceStateChange(iface); - }); - } - - @VisibleForTesting(visibility = PACKAGE) - protected void connectNetwork(@NonNull final String iface, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - mHandler.post(() -> updateInterfaceState(iface, true, listener)); - } - - @VisibleForTesting(visibility = PACKAGE) - protected void disconnectNetwork(@NonNull final String iface, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - mHandler.post(() -> updateInterfaceState(iface, false, listener)); - } - - IpConfiguration getIpConfiguration(String iface) { - return mIpConfigurations.get(iface); - } - - @VisibleForTesting(visibility = PACKAGE) - protected boolean isTrackingInterface(String iface) { - return mFactory.hasInterface(iface); - } - - String[] getInterfaces(boolean includeRestricted) { - return mFactory.getAvailableInterfaces(includeRestricted); - } - - List getInterfaceList() { - final List interfaceList = new ArrayList(); - final String[] ifaces; - try { - ifaces = mNetd.interfaceGetList(); - } catch (RemoteException e) { - Log.e(TAG, "Could not get list of interfaces " + e); - return interfaceList; - } - final String ifaceMatch = mIfaceMatch; - for (String iface : ifaces) { - if (iface.matches(ifaceMatch)) interfaceList.add(iface); - } - return interfaceList; - } - - /** - * Returns true if given interface was configured as restricted (doesn't have - * NET_CAPABILITY_NOT_RESTRICTED) capability. Otherwise, returns false. - */ - boolean isRestrictedInterface(String iface) { - final NetworkCapabilities nc = mNetworkCapabilities.get(iface); - return nc != null && !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); - } - - void addListener(IEthernetServiceListener listener, boolean canUseRestrictedNetworks) { - mHandler.post(() -> { - if (!mListeners.register(listener, new ListenerInfo(canUseRestrictedNetworks))) { - // Remote process has already died - return; - } - for (String iface : getInterfaces(canUseRestrictedNetworks)) { - unicastInterfaceStateChange(listener, iface); - } - - unicastEthernetStateChange(listener, mEthernetState); - }); - } - - void removeListener(IEthernetServiceListener listener) { - mHandler.post(() -> mListeners.unregister(listener)); - } - - public void setIncludeTestInterfaces(boolean include) { - mHandler.post(() -> { - mIncludeTestInterfaces = include; - updateIfaceMatchRegexp(); - mHandler.post(() -> trackAvailableInterfaces()); - }); - } - - public void requestTetheredInterface(ITetheredInterfaceCallback callback) { - mHandler.post(() -> { - if (!mTetheredInterfaceRequests.register(callback)) { - // Remote process has already died - return; - } - if (mDefaultInterfaceMode == INTERFACE_MODE_SERVER) { - if (mTetheredInterfaceWasAvailable) { - notifyTetheredInterfaceAvailable(callback, mDefaultInterface); - } - return; - } - - setDefaultInterfaceMode(INTERFACE_MODE_SERVER); - }); - } - - public void releaseTetheredInterface(ITetheredInterfaceCallback callback) { - mHandler.post(() -> { - mTetheredInterfaceRequests.unregister(callback); - maybeUntetherDefaultInterface(); - }); - } - - private void notifyTetheredInterfaceAvailable(ITetheredInterfaceCallback cb, String iface) { - try { - cb.onAvailable(iface); - } catch (RemoteException e) { - Log.e(TAG, "Error sending tethered interface available callback", e); - } - } - - private void notifyTetheredInterfaceUnavailable(ITetheredInterfaceCallback cb) { - try { - cb.onUnavailable(); - } catch (RemoteException e) { - Log.e(TAG, "Error sending tethered interface available callback", e); - } - } - - private void maybeUntetherDefaultInterface() { - if (mTetheredInterfaceRequests.getRegisteredCallbackCount() > 0) return; - if (mDefaultInterfaceMode == INTERFACE_MODE_CLIENT) return; - setDefaultInterfaceMode(INTERFACE_MODE_CLIENT); - } - - private void setDefaultInterfaceMode(int mode) { - Log.d(TAG, "Setting default interface mode to " + mode); - mDefaultInterfaceMode = mode; - if (mDefaultInterface != null) { - removeInterface(mDefaultInterface); - addInterface(mDefaultInterface); - } - } - - private int getInterfaceRole(final String iface) { - if (!mFactory.hasInterface(iface)) return EthernetManager.ROLE_NONE; - final int mode = getInterfaceMode(iface); - return (mode == INTERFACE_MODE_CLIENT) - ? EthernetManager.ROLE_CLIENT - : EthernetManager.ROLE_SERVER; - } - - private int getInterfaceMode(final String iface) { - if (iface.equals(mDefaultInterface)) { - return mDefaultInterfaceMode; - } - return INTERFACE_MODE_CLIENT; - } - - private void removeInterface(String iface) { - mFactory.removeInterface(iface); - maybeUpdateServerModeInterfaceState(iface, false); - } - - private void stopTrackingInterface(String iface) { - removeInterface(iface); - if (iface.equals(mDefaultInterface)) { - mDefaultInterface = null; - } - broadcastInterfaceStateChange(iface); - } - - private void addInterface(String iface) { - InterfaceConfigurationParcel config = null; - // Bring up the interface so we get link status indications. - try { - PermissionUtils.enforceNetworkStackPermission(mContext); - NetdUtils.setInterfaceUp(mNetd, iface); - config = NetdUtils.getInterfaceConfigParcel(mNetd, iface); - } catch (IllegalStateException e) { - // Either the system is crashing or the interface has disappeared. Just ignore the - // error; we haven't modified any state because we only do that if our calls succeed. - Log.e(TAG, "Error upping interface " + iface, e); - } - - if (config == null) { - Log.e(TAG, "Null interface config parcelable for " + iface + ". Bailing out."); - return; - } - - final String hwAddress = config.hwAddr; - - NetworkCapabilities nc = mNetworkCapabilities.get(iface); - if (nc == null) { - // Try to resolve using mac address - nc = mNetworkCapabilities.get(hwAddress); - if (nc == null) { - final boolean isTestIface = iface.matches(TEST_IFACE_REGEXP); - nc = createDefaultNetworkCapabilities(isTestIface); - } - } - - final int mode = getInterfaceMode(iface); - if (mode == INTERFACE_MODE_CLIENT) { - IpConfiguration ipConfiguration = getOrCreateIpConfiguration(iface); - Log.d(TAG, "Tracking interface in client mode: " + iface); - mFactory.addInterface(iface, hwAddress, ipConfiguration, nc); - } else { - maybeUpdateServerModeInterfaceState(iface, true); - } - - // Note: if the interface already has link (e.g., if we crashed and got - // restarted while it was running), we need to fake a link up notification so we - // start configuring it. - if (NetdUtils.hasFlag(config, "running")) { - updateInterfaceState(iface, true); - } - } - - private void updateInterfaceState(String iface, boolean up) { - updateInterfaceState(iface, up, null /* listener */); - } - - private void updateInterfaceState(@NonNull final String iface, final boolean up, - @Nullable final INetworkInterfaceOutcomeReceiver listener) { - final int mode = getInterfaceMode(iface); - final boolean factoryLinkStateUpdated = (mode == INTERFACE_MODE_CLIENT) - && mFactory.updateInterfaceLinkState(iface, up, listener); - - if (factoryLinkStateUpdated) { - broadcastInterfaceStateChange(iface); - } - } - - private void maybeUpdateServerModeInterfaceState(String iface, boolean available) { - if (available == mTetheredInterfaceWasAvailable || !iface.equals(mDefaultInterface)) return; - - Log.d(TAG, (available ? "Tracking" : "No longer tracking") - + " interface in server mode: " + iface); - - final int pendingCbs = mTetheredInterfaceRequests.beginBroadcast(); - for (int i = 0; i < pendingCbs; i++) { - ITetheredInterfaceCallback item = mTetheredInterfaceRequests.getBroadcastItem(i); - if (available) { - notifyTetheredInterfaceAvailable(item, iface); - } else { - notifyTetheredInterfaceUnavailable(item); - } - } - mTetheredInterfaceRequests.finishBroadcast(); - mTetheredInterfaceWasAvailable = available; - } - - private void maybeTrackInterface(String iface) { - if (!iface.matches(mIfaceMatch)) { - return; - } - - // If we don't already track this interface, and if this interface matches - // our regex, start tracking it. - if (mFactory.hasInterface(iface) || iface.equals(mDefaultInterface)) { - if (DBG) Log.w(TAG, "Ignoring already-tracked interface " + iface); - return; - } - if (DBG) Log.i(TAG, "maybeTrackInterface: " + iface); - - // TODO: avoid making an interface default if it has configured NetworkCapabilities. - if (mDefaultInterface == null) { - mDefaultInterface = iface; - } - - if (mIpConfigForDefaultInterface != null) { - updateIpConfiguration(iface, mIpConfigForDefaultInterface); - mIpConfigForDefaultInterface = null; - } - - addInterface(iface); - - broadcastInterfaceStateChange(iface); - } - - private void trackAvailableInterfaces() { - try { - final String[] ifaces = mNetd.interfaceGetList(); - for (String iface : ifaces) { - maybeTrackInterface(iface); - } - } catch (RemoteException | ServiceSpecificException e) { - Log.e(TAG, "Could not get list of interfaces " + e); - } - } - - private class InterfaceObserver extends BaseNetdUnsolicitedEventListener { - - @Override - public void onInterfaceLinkStateChanged(String iface, boolean up) { - if (DBG) { - Log.i(TAG, "interfaceLinkStateChanged, iface: " + iface + ", up: " + up); - } - mHandler.post(() -> updateInterfaceState(iface, up)); - } - - @Override - public void onInterfaceAdded(String iface) { - if (DBG) { - Log.i(TAG, "onInterfaceAdded, iface: " + iface); - } - mHandler.post(() -> maybeTrackInterface(iface)); - } - - @Override - public void onInterfaceRemoved(String iface) { - if (DBG) { - Log.i(TAG, "onInterfaceRemoved, iface: " + iface); - } - mHandler.post(() -> stopTrackingInterface(iface)); - } - } - - private static class ListenerInfo { - - boolean canUseRestrictedNetworks = false; - - ListenerInfo(boolean canUseRestrictedNetworks) { - this.canUseRestrictedNetworks = canUseRestrictedNetworks; - } - } - - /** - * Parses an Ethernet interface configuration - * - * @param configString represents an Ethernet configuration in the following format: {@code - * ;[Network Capabilities];[IP config];[Override Transport]} - */ - private void parseEthernetConfig(String configString) { - final EthernetTrackerConfig config = createEthernetTrackerConfig(configString); - NetworkCapabilities nc = createNetworkCapabilities( - !TextUtils.isEmpty(config.mCapabilities) /* clear default capabilities */, - config.mCapabilities, config.mTransport).build(); - mNetworkCapabilities.put(config.mIface, nc); - - if (null != config.mIpConfig) { - IpConfiguration ipConfig = parseStaticIpConfiguration(config.mIpConfig); - mIpConfigurations.put(config.mIface, ipConfig); - } - } - - @VisibleForTesting - static EthernetTrackerConfig createEthernetTrackerConfig(@NonNull final String configString) { - Objects.requireNonNull(configString, "EthernetTrackerConfig requires non-null config"); - return new EthernetTrackerConfig(configString.split(";", /* limit of tokens */ 4)); - } - - private static NetworkCapabilities createDefaultNetworkCapabilities(boolean isTestIface) { - NetworkCapabilities.Builder builder = createNetworkCapabilities( - false /* clear default capabilities */, null, null) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); - - if (isTestIface) { - builder.addTransportType(NetworkCapabilities.TRANSPORT_TEST); - } else { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - } - - return builder.build(); - } - - /** - * Parses a static list of network capabilities - * - * @param clearDefaultCapabilities Indicates whether or not to clear any default capabilities - * @param commaSeparatedCapabilities A comma separated string list of integer encoded - * NetworkCapability.NET_CAPABILITY_* values - * @param overrideTransport A string representing a single integer encoded override transport - * type. Must be one of the NetworkCapability.TRANSPORT_* - * values. TRANSPORT_VPN is not supported. Errors with input - * will cause the override to be ignored. - */ - @VisibleForTesting - static NetworkCapabilities.Builder createNetworkCapabilities( - boolean clearDefaultCapabilities, @Nullable String commaSeparatedCapabilities, - @Nullable String overrideTransport) { - - final NetworkCapabilities.Builder builder = clearDefaultCapabilities - ? NetworkCapabilities.Builder.withoutDefaultCapabilities() - : new NetworkCapabilities.Builder(); - - // Determine the transport type. If someone has tried to define an override transport then - // attempt to add it. Since we can only have one override, all errors with it will - // gracefully default back to TRANSPORT_ETHERNET and warn the user. VPN is not allowed as an - // override type. Wifi Aware and LoWPAN are currently unsupported as well. - int transport = NetworkCapabilities.TRANSPORT_ETHERNET; - if (!TextUtils.isEmpty(overrideTransport)) { - try { - int parsedTransport = Integer.valueOf(overrideTransport); - if (parsedTransport == NetworkCapabilities.TRANSPORT_VPN - || parsedTransport == NetworkCapabilities.TRANSPORT_WIFI_AWARE - || parsedTransport == NetworkCapabilities.TRANSPORT_LOWPAN) { - Log.e(TAG, "Override transport '" + parsedTransport + "' is not supported. " - + "Defaulting to TRANSPORT_ETHERNET"); - } else { - transport = parsedTransport; - } - } catch (NumberFormatException nfe) { - Log.e(TAG, "Override transport type '" + overrideTransport + "' " - + "could not be parsed. Defaulting to TRANSPORT_ETHERNET"); - } - } - - // Apply the transport. If the user supplied a valid number that is not a valid transport - // then adding will throw an exception. Default back to TRANSPORT_ETHERNET if that happens - try { - builder.addTransportType(transport); - } catch (IllegalArgumentException iae) { - Log.e(TAG, transport + " is not a valid NetworkCapability.TRANSPORT_* value. " - + "Defaulting to TRANSPORT_ETHERNET"); - builder.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET); - } - - builder.setLinkUpstreamBandwidthKbps(100 * 1000); - builder.setLinkDownstreamBandwidthKbps(100 * 1000); - - if (!TextUtils.isEmpty(commaSeparatedCapabilities)) { - for (String strNetworkCapability : commaSeparatedCapabilities.split(",")) { - if (!TextUtils.isEmpty(strNetworkCapability)) { - try { - builder.addCapability(Integer.valueOf(strNetworkCapability)); - } catch (NumberFormatException nfe) { - Log.e(TAG, "Capability '" + strNetworkCapability + "' could not be parsed"); - } catch (IllegalArgumentException iae) { - Log.e(TAG, strNetworkCapability + " is not a valid " - + "NetworkCapability.NET_CAPABILITY_* value"); - } - } - } - } - // Ethernet networks have no way to update the following capabilities, so they always - // have them. - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); - - return builder; - } - - /** - * Parses static IP configuration. - * - * @param staticIpConfig represents static IP configuration in the following format: {@code - * ip= gateway= dns= - * domains=} - */ - @VisibleForTesting - static IpConfiguration parseStaticIpConfiguration(String staticIpConfig) { - final StaticIpConfiguration.Builder staticIpConfigBuilder = - new StaticIpConfiguration.Builder(); - - for (String keyValueAsString : staticIpConfig.trim().split(" ")) { - if (TextUtils.isEmpty(keyValueAsString)) continue; - - String[] pair = keyValueAsString.split("="); - if (pair.length != 2) { - throw new IllegalArgumentException("Unexpected token: " + keyValueAsString - + " in " + staticIpConfig); - } - - String key = pair[0]; - String value = pair[1]; - - switch (key) { - case "ip": - staticIpConfigBuilder.setIpAddress(new LinkAddress(value)); - break; - case "domains": - staticIpConfigBuilder.setDomains(value); - break; - case "gateway": - staticIpConfigBuilder.setGateway(InetAddress.parseNumericAddress(value)); - break; - case "dns": { - ArrayList dnsAddresses = new ArrayList<>(); - for (String address: value.split(",")) { - dnsAddresses.add(InetAddress.parseNumericAddress(address)); - } - staticIpConfigBuilder.setDnsServers(dnsAddresses); - break; - } - default : { - throw new IllegalArgumentException("Unexpected key: " + key - + " in " + staticIpConfig); - } - } - } - return createIpConfiguration(staticIpConfigBuilder.build()); - } - - private static IpConfiguration createIpConfiguration( - @NonNull final StaticIpConfiguration staticIpConfig) { - return new IpConfiguration.Builder().setStaticIpConfiguration(staticIpConfig).build(); - } - - private IpConfiguration getOrCreateIpConfiguration(String iface) { - IpConfiguration ret = mIpConfigurations.get(iface); - if (ret != null) return ret; - ret = new IpConfiguration(); - ret.setIpAssignment(IpAssignment.DHCP); - ret.setProxySettings(ProxySettings.NONE); - return ret; - } - - private void updateIfaceMatchRegexp() { - final String match = mDeps.getInterfaceRegexFromResource(mContext); - mIfaceMatch = mIncludeTestInterfaces - ? "(" + match + "|" + TEST_IFACE_REGEXP + ")" - : match; - Log.d(TAG, "Interface match regexp set to '" + mIfaceMatch + "'"); - } - - /** - * Validate if a given interface is valid for testing. - * - * @param iface the name of the interface to validate. - * @return {@code true} if test interfaces are enabled and the given {@code iface} has a test - * interface prefix, {@code false} otherwise. - */ - public boolean isValidTestInterface(@NonNull final String iface) { - return mIncludeTestInterfaces && iface.matches(TEST_IFACE_REGEXP); - } - - private void postAndWaitForRunnable(Runnable r) { - final ConditionVariable cv = new ConditionVariable(); - if (mHandler.post(() -> { - r.run(); - cv.open(); - })) { - cv.block(2000L); - } - } - - @VisibleForTesting(visibility = PACKAGE) - protected void setEthernetEnabled(boolean enabled) { - mHandler.post(() -> { - int newState = enabled ? ETHERNET_STATE_ENABLED : ETHERNET_STATE_DISABLED; - if (mEthernetState == newState) return; - - mEthernetState = newState; - - if (enabled) { - trackAvailableInterfaces(); - } else { - // TODO: maybe also disable server mode interface as well. - untrackFactoryInterfaces(); - } - broadcastEthernetStateChange(mEthernetState); - }); - } - - private void untrackFactoryInterfaces() { - for (String iface : mFactory.getAvailableInterfaces(true /* includeRestricted */)) { - stopTrackingInterface(iface); - } - } - - private void unicastEthernetStateChange(@NonNull IEthernetServiceListener listener, - int state) { - ensureRunningOnEthernetServiceThread(); - try { - listener.onEthernetStateChanged(state); - } catch (RemoteException e) { - // Do nothing here. - } - } - - private void broadcastEthernetStateChange(int state) { - ensureRunningOnEthernetServiceThread(); - final int n = mListeners.beginBroadcast(); - for (int i = 0; i < n; i++) { - try { - mListeners.getBroadcastItem(i).onEthernetStateChanged(state); - } catch (RemoteException e) { - // Do nothing here. - } - } - mListeners.finishBroadcast(); - } - - void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { - postAndWaitForRunnable(() -> { - pw.println(getClass().getSimpleName()); - pw.println("Ethernet interface name filter: " + mIfaceMatch); - pw.println("Default interface: " + mDefaultInterface); - pw.println("Default interface mode: " + mDefaultInterfaceMode); - pw.println("Tethered interface requests: " - + mTetheredInterfaceRequests.getRegisteredCallbackCount()); - pw.println("Listeners: " + mListeners.getRegisteredCallbackCount()); - pw.println("IP Configurations:"); - pw.increaseIndent(); - for (String iface : mIpConfigurations.keySet()) { - pw.println(iface + ": " + mIpConfigurations.get(iface)); - } - pw.decreaseIndent(); - pw.println(); - - pw.println("Network Capabilities:"); - pw.increaseIndent(); - for (String iface : mNetworkCapabilities.keySet()) { - pw.println(iface + ": " + mNetworkCapabilities.get(iface)); - } - pw.decreaseIndent(); - pw.println(); - - mFactory.dump(fd, pw, args); - }); - } - - @VisibleForTesting - static class EthernetTrackerConfig { - 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"); - 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/Android.bp b/tests/Android.bp deleted file mode 100644 index 6cfebdc..0000000 --- a/tests/Android.bp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (C) 2018 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 { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -// TODO: merge the tests into service-connectivity tests after -// ethernet service migration completes. So far just import the -// ethernet service source to fix the dependencies. -android_test { - name: "EthernetServiceTests", - - srcs: [ - ":ethernet-service-updatable-sources", - ":services.connectivity-ethernet-sources", - "java/**/*.java", - ], - - certificate: "platform", - platform_apis: true, - - libs: [ - "android.test.runner", - "android.test.base", - "android.test.mock", - "framework-connectivity.impl", - "framework-connectivity-t.impl", - "ServiceConnectivityResources", - ], - - static_libs: [ - "androidx.test.rules", - "frameworks-base-testutils", - "mockito-target-minus-junit4", - "net-tests-utils", - "services.core", - "services.net", - ], - test_suites: ["general-tests"], -} diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml deleted file mode 100644 index cd875b0..0000000 --- a/tests/AndroidManifest.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - diff --git a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java deleted file mode 100644 index 4d3e4d3..0000000 --- a/tests/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java +++ /dev/null @@ -1,783 +0,0 @@ -/* - * 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.assertNotSame; -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.EthernetNetworkManagementException; -import android.net.INetworkInterfaceOutcomeReceiver; -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.connectivity.resources.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 INetworkInterfaceOutcomeReceiver 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(mDeps.getTcpBufferSizesFromResource(eq(mContext))).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 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(); - assertEquals(listener.expectOnResult(), TEST_IFACE); - } - - @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(); - assertEquals(listener.expectOnResult(), TEST_IFACE); - } - - @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()); - assertEquals(listener.expectOnResult(), TEST_IFACE); - } - - @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(); - listener.expectOnErrorWithMessage("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(); - listener.expectOnErrorWithMessage("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()); - } - - private IpClientCallbacks getStaleIpClientCallbacks() throws Exception { - createAndVerifyProvisionedInterface(TEST_IFACE); - final IpClientCallbacks staleIpClientCallbacks = mIpClientCallbacks; - mNetFactory.removeInterface(TEST_IFACE); - verifyStop(); - assertNotSame(mIpClientCallbacks, staleIpClientCallbacks); - return staleIpClientCallbacks; - } - - @Test - public void testIgnoreOnIpLayerStartedCallbackForStaleCallback() throws Exception { - initEthernetNetworkFactory(); - final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); - - staleIpClientCallbacks.onProvisioningSuccess(new LinkProperties()); - mLooper.dispatchAll(); - - verify(mIpClient, never()).startProvisioning(any()); - verify(mNetworkAgent, never()).register(); - } - - @Test - public void testIgnoreOnIpLayerStoppedCallbackForStaleCallback() throws Exception { - initEthernetNetworkFactory(); - when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams); - final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); - - staleIpClientCallbacks.onProvisioningFailure(new LinkProperties()); - mLooper.dispatchAll(); - - verify(mIpClient, never()).startProvisioning(any()); - } - - @Test - public void testIgnoreLinkPropertiesCallbackForStaleCallback() throws Exception { - initEthernetNetworkFactory(); - final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); - final LinkProperties lp = new LinkProperties(); - - staleIpClientCallbacks.onLinkPropertiesChange(lp); - mLooper.dispatchAll(); - - verify(mNetworkAgent, never()).sendLinkPropertiesImpl(eq(lp)); - } - - @Test - public void testIgnoreNeighborLossCallbackForStaleCallback() throws Exception { - initEthernetNetworkFactory(); - final IpClientCallbacks staleIpClientCallbacks = getStaleIpClientCallbacks(); - - staleIpClientCallbacks.onReachabilityLost("Neighbor Lost"); - mLooper.dispatchAll(); - - 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 INetworkInterfaceOutcomeReceiver { - private final CompletableFuture mResult = new CompletableFuture<>(); - private final CompletableFuture mError = - new CompletableFuture<>(); - - @Override - public void onResult(@NonNull String iface) { - mResult.complete(iface); - } - - @Override - public void onError(@NonNull EthernetNetworkManagementException exception) { - mError.complete(exception); - } - - String expectOnResult() throws Exception { - return mResult.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); - } - - EthernetNetworkManagementException expectOnError() throws Exception { - return mError.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); - } - - void expectOnErrorWithMessage(String msg) throws Exception { - assertTrue(expectOnError().getMessage().contains(msg)); - } - - @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(); - - assertEquals(listener.expectOnResult(), TEST_IFACE); - } - - @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(); - }); - - assertEquals(successfulListener.expectOnResult(), TEST_IFACE); - } - - 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(); - - failedListener.expectOnErrorWithMessage("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(); - - assertEquals(listener.expectOnResult(), TEST_IFACE); - 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(); - listener.expectOnErrorWithMessage("can't be updated as it is not available"); - } - - @Test - public void testUpdateInterfaceWithNullIpConfiguration() throws Exception { - initEthernetNetworkFactory(); - createAndVerifyProvisionedInterface(TEST_IFACE); - - final IpConfiguration initialIpConfig = createStaticIpConfig(); - mNetFactory.updateInterface(TEST_IFACE, initialIpConfig, null /*capabilities*/, - null /*listener*/); - triggerOnProvisioningSuccess(); - verifyRestart(initialIpConfig); - - // TODO: have verifyXyz functions clear invocations. - clearInvocations(mDeps); - clearInvocations(mIpClient); - clearInvocations(mNetworkAgent); - - - // verify that sending a null ipConfig does not update the current ipConfig. - mNetFactory.updateInterface(TEST_IFACE, null /*ipConfig*/, null /*capabilities*/, - null /*listener*/); - triggerOnProvisioningSuccess(); - verifyRestart(initialIpConfig); - } -} diff --git a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java deleted file mode 100644 index dd1f1ed..0000000 --- a/tests/java/com/android/server/ethernet/EthernetServiceImplTest.java +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright (C) 2021 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 android.net.NetworkCapabilities.TRANSPORT_TEST; - -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.fail; - -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.Manifest; -import android.annotation.NonNull; -import android.content.Context; -import android.content.pm.PackageManager; -import android.net.INetworkInterfaceOutcomeReceiver; -import android.net.EthernetNetworkUpdateRequest; -import android.net.IpConfiguration; -import android.net.NetworkCapabilities; -import android.os.Handler; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class EthernetServiceImplTest { - private static final String TEST_IFACE = "test123"; - private static final EthernetNetworkUpdateRequest UPDATE_REQUEST = - new EthernetNetworkUpdateRequest.Builder() - .setIpConfiguration(new IpConfiguration()) - .setNetworkCapabilities(new NetworkCapabilities.Builder().build()) - .build(); - private static final EthernetNetworkUpdateRequest UPDATE_REQUEST_WITHOUT_CAPABILITIES = - new EthernetNetworkUpdateRequest.Builder() - .setIpConfiguration(new IpConfiguration()) - .build(); - private static final EthernetNetworkUpdateRequest UPDATE_REQUEST_WITHOUT_IP_CONFIG = - new EthernetNetworkUpdateRequest.Builder() - .setNetworkCapabilities(new NetworkCapabilities.Builder().build()) - .build(); - private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; - private EthernetServiceImpl mEthernetServiceImpl; - @Mock private Context mContext; - @Mock private Handler mHandler; - @Mock private EthernetTracker mEthernetTracker; - @Mock private PackageManager mPackageManager; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - doReturn(mPackageManager).when(mContext).getPackageManager(); - mEthernetServiceImpl = new EthernetServiceImpl(mContext, mHandler, mEthernetTracker); - mEthernetServiceImpl.mStarted.set(true); - toggleAutomotiveFeature(true); - shouldTrackIface(TEST_IFACE, true); - } - - private void toggleAutomotiveFeature(final boolean isEnabled) { - doReturn(isEnabled) - .when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); - } - - private void shouldTrackIface(@NonNull final String iface, final boolean shouldTrack) { - doReturn(shouldTrack).when(mEthernetTracker).isTrackingInterface(iface); - } - - @Test - public void testSetConfigurationRejectsWhenEthNotStarted() { - mEthernetServiceImpl.mStarted.set(false); - assertThrows(IllegalStateException.class, () -> { - mEthernetServiceImpl.setConfiguration("" /* iface */, new IpConfiguration()); - }); - } - - @Test - public void testUpdateConfigurationRejectsWhenEthNotStarted() { - mEthernetServiceImpl.mStarted.set(false); - assertThrows(IllegalStateException.class, () -> { - mEthernetServiceImpl.updateConfiguration( - "" /* iface */, UPDATE_REQUEST, null /* listener */); - }); - } - - @Test - public void testConnectNetworkRejectsWhenEthNotStarted() { - mEthernetServiceImpl.mStarted.set(false); - assertThrows(IllegalStateException.class, () -> { - mEthernetServiceImpl.connectNetwork("" /* iface */, null /* listener */); - }); - } - - @Test - public void testDisconnectNetworkRejectsWhenEthNotStarted() { - mEthernetServiceImpl.mStarted.set(false); - assertThrows(IllegalStateException.class, () -> { - mEthernetServiceImpl.disconnectNetwork("" /* iface */, null /* listener */); - }); - } - - @Test - public void testUpdateConfigurationRejectsNullIface() { - assertThrows(NullPointerException.class, () -> { - mEthernetServiceImpl.updateConfiguration(null, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - @Test - public void testConnectNetworkRejectsNullIface() { - assertThrows(NullPointerException.class, () -> { - mEthernetServiceImpl.connectNetwork(null /* iface */, NULL_LISTENER); - }); - } - - @Test - public void testDisconnectNetworkRejectsNullIface() { - assertThrows(NullPointerException.class, () -> { - mEthernetServiceImpl.disconnectNetwork(null /* iface */, NULL_LISTENER); - }); - } - - @Test - public void testUpdateConfigurationWithCapabilitiesRejectsWithoutAutomotiveFeature() { - toggleAutomotiveFeature(false); - assertThrows(UnsupportedOperationException.class, () -> { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - @Test - public void testUpdateConfigurationWithCapabilitiesWithAutomotiveFeature() { - toggleAutomotiveFeature(false); - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST_WITHOUT_CAPABILITIES, - NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration(eq(TEST_IFACE), - eq(UPDATE_REQUEST_WITHOUT_CAPABILITIES.getIpConfiguration()), - eq(UPDATE_REQUEST_WITHOUT_CAPABILITIES.getNetworkCapabilities()), isNull()); - } - - @Test - public void testConnectNetworkRejectsWithoutAutomotiveFeature() { - toggleAutomotiveFeature(false); - assertThrows(UnsupportedOperationException.class, () -> { - mEthernetServiceImpl.connectNetwork("" /* iface */, NULL_LISTENER); - }); - } - - @Test - public void testDisconnectNetworkRejectsWithoutAutomotiveFeature() { - toggleAutomotiveFeature(false); - assertThrows(UnsupportedOperationException.class, () -> { - mEthernetServiceImpl.disconnectNetwork("" /* iface */, NULL_LISTENER); - }); - } - - private void denyManageEthPermission() { - doThrow(new SecurityException("")).when(mContext) - .enforceCallingOrSelfPermission( - eq(Manifest.permission.MANAGE_ETHERNET_NETWORKS), anyString()); - } - - private void denyManageTestNetworksPermission() { - doThrow(new SecurityException("")).when(mContext) - .enforceCallingOrSelfPermission( - eq(Manifest.permission.MANAGE_TEST_NETWORKS), anyString()); - } - - @Test - public void testUpdateConfigurationRejectsWithoutManageEthPermission() { - denyManageEthPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - @Test - public void testConnectNetworkRejectsWithoutManageEthPermission() { - denyManageEthPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER); - }); - } - - @Test - public void testDisconnectNetworkRejectsWithoutManageEthPermission() { - denyManageEthPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - }); - } - - private void enableTestInterface() { - when(mEthernetTracker.isValidTestInterface(eq(TEST_IFACE))).thenReturn(true); - } - - @Test - public void testUpdateConfigurationRejectsTestRequestWithoutTestPermission() { - enableTestInterface(); - denyManageTestNetworksPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - @Test - public void testConnectNetworkRejectsTestRequestWithoutTestPermission() { - enableTestInterface(); - denyManageTestNetworksPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER); - }); - } - - @Test - public void testDisconnectNetworkRejectsTestRequestWithoutTestPermission() { - enableTestInterface(); - denyManageTestNetworksPermission(); - assertThrows(SecurityException.class, () -> { - mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - }); - } - - @Test - public void testUpdateConfiguration() { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration( - eq(TEST_IFACE), - eq(UPDATE_REQUEST.getIpConfiguration()), - eq(UPDATE_REQUEST.getNetworkCapabilities()), eq(NULL_LISTENER)); - } - - @Test - public void testConnectNetwork() { - mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER); - verify(mEthernetTracker).connectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); - } - - @Test - public void testDisconnectNetwork() { - mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - verify(mEthernetTracker).disconnectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); - } - - @Test - public void testUpdateConfigurationAcceptsTestRequestWithNullCapabilities() { - enableTestInterface(); - final EthernetNetworkUpdateRequest request = - new EthernetNetworkUpdateRequest - .Builder() - .setIpConfiguration(new IpConfiguration()).build(); - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, request, NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration(eq(TEST_IFACE), - eq(request.getIpConfiguration()), - eq(request.getNetworkCapabilities()), isNull()); - } - - @Test - public void testUpdateConfigurationAcceptsRequestWithNullIpConfiguration() { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST_WITHOUT_IP_CONFIG, - NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration(eq(TEST_IFACE), - eq(UPDATE_REQUEST_WITHOUT_IP_CONFIG.getIpConfiguration()), - eq(UPDATE_REQUEST_WITHOUT_IP_CONFIG.getNetworkCapabilities()), isNull()); - } - - @Test - public void testUpdateConfigurationRejectsInvalidTestRequest() { - enableTestInterface(); - assertThrows(IllegalArgumentException.class, () -> { - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); - }); - } - - private EthernetNetworkUpdateRequest createTestNetworkUpdateRequest() { - final NetworkCapabilities nc = new NetworkCapabilities - .Builder(UPDATE_REQUEST.getNetworkCapabilities()) - .addTransportType(TRANSPORT_TEST).build(); - - return new EthernetNetworkUpdateRequest - .Builder(UPDATE_REQUEST) - .setNetworkCapabilities(nc).build(); - } - - @Test - public void testUpdateConfigurationForTestRequestDoesNotRequireAutoOrEthernetPermission() { - enableTestInterface(); - toggleAutomotiveFeature(false); - denyManageEthPermission(); - final EthernetNetworkUpdateRequest request = createTestNetworkUpdateRequest(); - - mEthernetServiceImpl.updateConfiguration(TEST_IFACE, request, NULL_LISTENER); - verify(mEthernetTracker).updateConfiguration( - eq(TEST_IFACE), - eq(request.getIpConfiguration()), - eq(request.getNetworkCapabilities()), eq(NULL_LISTENER)); - } - - @Test - public void testConnectNetworkForTestRequestDoesNotRequireAutoOrNetPermission() { - enableTestInterface(); - toggleAutomotiveFeature(false); - denyManageEthPermission(); - - mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER); - verify(mEthernetTracker).connectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); - } - - @Test - public void testDisconnectNetworkForTestRequestDoesNotRequireAutoOrNetPermission() { - enableTestInterface(); - toggleAutomotiveFeature(false); - denyManageEthPermission(); - - mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - verify(mEthernetTracker).disconnectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); - } - - private void denyPermissions(String... permissions) { - for (String permission: permissions) { - doReturn(PackageManager.PERMISSION_DENIED).when(mContext) - .checkCallingOrSelfPermission(eq(permission)); - } - } - - @Test - public void testSetEthernetEnabled() { - denyPermissions(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); - mEthernetServiceImpl.setEthernetEnabled(true); - verify(mEthernetTracker).setEthernetEnabled(true); - reset(mEthernetTracker); - - denyPermissions(Manifest.permission.NETWORK_STACK); - mEthernetServiceImpl.setEthernetEnabled(false); - verify(mEthernetTracker).setEthernetEnabled(false); - reset(mEthernetTracker); - - denyPermissions(Manifest.permission.NETWORK_SETTINGS); - try { - mEthernetServiceImpl.setEthernetEnabled(true); - fail("Should get SecurityException"); - } catch (SecurityException e) { } - verify(mEthernetTracker, never()).setEthernetEnabled(false); - } -} diff --git a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/java/com/android/server/ethernet/EthernetTrackerTest.java deleted file mode 100644 index b1831c4..0000000 --- a/tests/java/com/android/server/ethernet/EthernetTrackerTest.java +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Copyright (C) 2018 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 android.net.TestNetworkManager.TEST_TAP_PREFIX; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.content.res.Resources; -import android.net.EthernetManager; -import android.net.InetAddresses; -import android.net.INetworkInterfaceOutcomeReceiver; -import android.net.IEthernetServiceListener; -import android.net.INetd; -import android.net.IpConfiguration; -import android.net.IpConfiguration.IpAssignment; -import android.net.IpConfiguration.ProxySettings; -import android.net.InterfaceConfigurationParcel; -import android.net.LinkAddress; -import android.net.NetworkCapabilities; -import android.net.StaticIpConfiguration; -import android.os.HandlerThread; -import android.os.RemoteException; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.connectivity.resources.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; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class EthernetTrackerTest { - private static final String TEST_IFACE = "test123"; - private static final int TIMEOUT_MS = 1_000; - private static final String THREAD_NAME = "EthernetServiceThread"; - private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; - private EthernetTracker tracker; - private HandlerThread mHandlerThread; - @Mock private Context mContext; - @Mock private EthernetNetworkFactory mFactory; - @Mock private INetd mNetd; - @Mock private EthernetTracker.Dependencies mDeps; - - @Before - public void setUp() throws RemoteException { - MockitoAnnotations.initMocks(this); - initMockResources(); - when(mFactory.updateInterfaceLinkState(anyString(), anyBoolean(), any())).thenReturn(false); - when(mNetd.interfaceGetList()).thenReturn(new String[0]); - mHandlerThread = new HandlerThread(THREAD_NAME); - mHandlerThread.start(); - tracker = new EthernetTracker(mContext, mHandlerThread.getThreadHandler(), mFactory, mNetd, - mDeps); - } - - @After - public void cleanUp() { - mHandlerThread.quitSafely(); - } - - private void initMockResources() { - when(mDeps.getInterfaceRegexFromResource(eq(mContext))).thenReturn(""); - when(mDeps.getInterfaceConfigFromResource(eq(mContext))).thenReturn(new String[0]); - } - - private void waitForIdle() { - HandlerUtils.waitForIdle(mHandlerThread, TIMEOUT_MS); - } - - /** - * Test: Creation of various valid static IP configurations - */ - @Test - public void createStaticIpConfiguration() { - // Empty gives default StaticIPConfiguration object - assertStaticConfiguration(new StaticIpConfiguration(), ""); - - // Setting only the IP address properly cascades and assumes defaults - assertStaticConfiguration(new StaticIpConfiguration.Builder() - .setIpAddress(new LinkAddress("192.0.2.10/24")).build(), "ip=192.0.2.10/24"); - - final ArrayList dnsAddresses = new ArrayList<>(); - dnsAddresses.add(InetAddresses.parseNumericAddress("4.4.4.4")); - dnsAddresses.add(InetAddresses.parseNumericAddress("8.8.8.8")); - // Setting other fields properly cascades them - assertStaticConfiguration(new StaticIpConfiguration.Builder() - .setIpAddress(new LinkAddress("192.0.2.10/24")) - .setDnsServers(dnsAddresses) - .setGateway(InetAddresses.parseNumericAddress("192.0.2.1")) - .setDomains("android").build(), - "ip=192.0.2.10/24 dns=4.4.4.4,8.8.8.8 gateway=192.0.2.1 domains=android"); - - // Verify order doesn't matter - assertStaticConfiguration(new StaticIpConfiguration.Builder() - .setIpAddress(new LinkAddress("192.0.2.10/24")) - .setDnsServers(dnsAddresses) - .setGateway(InetAddresses.parseNumericAddress("192.0.2.1")) - .setDomains("android").build(), - "domains=android ip=192.0.2.10/24 gateway=192.0.2.1 dns=4.4.4.4,8.8.8.8 "); - } - - /** - * Test: Attempt creation of various bad static IP configurations - */ - @Test - public void createStaticIpConfiguration_Bad() { - assertStaticConfigurationFails("ip=192.0.2.1/24 gateway= blah=20.20.20.20"); // Unknown key - assertStaticConfigurationFails("ip=192.0.2.1"); // mask is missing - assertStaticConfigurationFails("ip=a.b.c"); // not a valid ip address - assertStaticConfigurationFails("dns=4.4.4.4,1.2.3.A"); // not valid ip address in dns - assertStaticConfigurationFails("="); // Key and value is empty - assertStaticConfigurationFails("ip="); // Value is empty - assertStaticConfigurationFails("ip=192.0.2.1/24 gateway="); // Gateway is empty - } - - private void assertStaticConfigurationFails(String config) { - try { - EthernetTracker.parseStaticIpConfiguration(config); - fail("Expected to fail: " + config); - } catch (IllegalArgumentException e) { - // expected - } - } - - private void assertStaticConfiguration(StaticIpConfiguration expectedStaticIpConfig, - String configAsString) { - final IpConfiguration expectedIpConfiguration = new IpConfiguration(); - expectedIpConfiguration.setIpAssignment(IpAssignment.STATIC); - expectedIpConfiguration.setProxySettings(ProxySettings.NONE); - expectedIpConfiguration.setStaticIpConfiguration(expectedStaticIpConfig); - - assertEquals(expectedIpConfiguration, - EthernetTracker.parseStaticIpConfiguration(configAsString)); - } - - private NetworkCapabilities.Builder makeEthernetCapabilitiesBuilder(boolean clearAll) { - final NetworkCapabilities.Builder builder = - clearAll ? NetworkCapabilities.Builder.withoutDefaultCapabilities() - : new NetworkCapabilities.Builder(); - return builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); - } - - /** - * Test: Attempt to create a capabilties with various valid sets of capabilities/transports - */ - @Test - public void createNetworkCapabilities() { - - // Particularly common expected results - NetworkCapabilities defaultEthernetCleared = - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .build(); - - NetworkCapabilities ethernetClearedWithCommonCaps = - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .addCapability(12) - .addCapability(13) - .addCapability(14) - .addCapability(15) - .build(); - - // Empty capabilities and transports lists with a "please clear defaults" should - // yield an empty capabilities set with TRANPORT_ETHERNET - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", ""); - - // Empty capabilities and transports without the clear defaults flag should return the - // default capabilities set with TRANSPORT_ETHERNET - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(false /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .build(), - false, "", ""); - - // A list of capabilities without the clear defaults flag should return the default - // capabilities, mixed with the desired capabilities, and TRANSPORT_ETHERNET - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(false /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) - .addCapability(11) - .addCapability(12) - .build(), - false, "11,12", ""); - - // Adding a list of capabilities with a clear defaults will leave exactly those capabilities - // with a default TRANSPORT_ETHERNET since no overrides are specified - assertParsedNetworkCapabilities(ethernetClearedWithCommonCaps, true, "12,13,14,15", ""); - - // Adding any invalid capabilities to the list will cause them to be ignored - assertParsedNetworkCapabilities(ethernetClearedWithCommonCaps, true, "12,13,14,15,65,73", ""); - assertParsedNetworkCapabilities(ethernetClearedWithCommonCaps, true, "12,13,14,15,abcdefg", ""); - - // Adding a valid override transport will remove the default TRANSPORT_ETHERNET transport - // and apply only the override to the capabiltities object - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(0) - .build(), - true, "", "0"); - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(1) - .build(), - true, "", "1"); - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(2) - .build(), - true, "", "2"); - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addTransportType(3) - .build(), - true, "", "3"); - - // "4" is TRANSPORT_VPN, which is unsupported. Should default back to TRANPORT_ETHERNET - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", "4"); - - // "5" is TRANSPORT_WIFI_AWARE, which is currently supported due to no legacy TYPE_NONE - // conversion. When that becomes available, this test must be updated - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", "5"); - - // "6" is TRANSPORT_LOWPAN, which is currently supported due to no legacy TYPE_NONE - // conversion. When that becomes available, this test must be updated - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", "6"); - - // Adding an invalid override transport will leave the transport as TRANSPORT_ETHERNET - assertParsedNetworkCapabilities(defaultEthernetCleared,true, "", "100"); - assertParsedNetworkCapabilities(defaultEthernetCleared, true, "", "abcdefg"); - - // Ensure the adding of both capabilities and transports work - assertParsedNetworkCapabilities( - makeEthernetCapabilitiesBuilder(true /* clearAll */) - .setLinkUpstreamBandwidthKbps(100000) - .setLinkDownstreamBandwidthKbps(100000) - .addCapability(12) - .addCapability(13) - .addCapability(14) - .addCapability(15) - .addTransportType(3) - .build(), - true, "12,13,14,15", "3"); - - // Ensure order does not matter for capability list - assertParsedNetworkCapabilities(ethernetClearedWithCommonCaps, true, "13,12,15,14", ""); - } - - private void assertParsedNetworkCapabilities(NetworkCapabilities expectedNetworkCapabilities, - boolean clearCapabilties, String configCapabiltiies,String configTransports) { - assertEquals(expectedNetworkCapabilities, - EthernetTracker.createNetworkCapabilities(clearCapabilties, configCapabiltiies, - configTransports).build()); - } - - @Test - public void testCreateEthernetTrackerConfigReturnsCorrectValue() { - final String capabilities = "2"; - final String ipConfig = "3"; - final String transport = "4"; - final String configString = String.join(";", TEST_IFACE, capabilities, ipConfig, transport); - - final EthernetTracker.EthernetTrackerConfig config = - EthernetTracker.createEthernetTrackerConfig(configString); - - assertEquals(TEST_IFACE, config.mIface); - assertEquals(capabilities, config.mCapabilities); - assertEquals(ipConfig, config.mIpConfig); - assertEquals(transport, config.mTransport); - } - - @Test - public void testCreateEthernetTrackerConfigThrowsNpeWithNullInput() { - assertThrows(NullPointerException.class, - () -> EthernetTracker.createEthernetTrackerConfig(null)); - } - - @Test - public void testUpdateConfiguration() { - final NetworkCapabilities capabilities = new NetworkCapabilities.Builder().build(); - final LinkAddress linkAddr = new LinkAddress("192.0.2.2/25"); - final StaticIpConfiguration staticIpConfig = - new StaticIpConfiguration.Builder().setIpAddress(linkAddr).build(); - final IpConfiguration ipConfig = - new IpConfiguration.Builder().setStaticIpConfiguration(staticIpConfig).build(); - final INetworkInterfaceOutcomeReceiver listener = null; - - tracker.updateConfiguration(TEST_IFACE, ipConfig, capabilities, listener); - waitForIdle(); - - verify(mFactory).updateInterface( - eq(TEST_IFACE), eq(ipConfig), eq(capabilities), eq(listener)); - } - - @Test - public void testConnectNetworkCorrectlyCallsFactory() { - tracker.connectNetwork(TEST_IFACE, NULL_LISTENER); - waitForIdle(); - - verify(mFactory).updateInterfaceLinkState(eq(TEST_IFACE), eq(true /* up */), - eq(NULL_LISTENER)); - } - - @Test - public void testDisconnectNetworkCorrectlyCallsFactory() { - tracker.disconnectNetwork(TEST_IFACE, NULL_LISTENER); - waitForIdle(); - - verify(mFactory).updateInterfaceLinkState(eq(TEST_IFACE), eq(false /* up */), - eq(NULL_LISTENER)); - } - - @Test - public void testIsValidTestInterfaceIsFalseWhenTestInterfacesAreNotIncluded() { - final String validIfaceName = TEST_TAP_PREFIX + "123"; - tracker.setIncludeTestInterfaces(false); - waitForIdle(); - - final boolean isValidTestInterface = tracker.isValidTestInterface(validIfaceName); - - assertFalse(isValidTestInterface); - } - - @Test - public void testIsValidTestInterfaceIsFalseWhenTestInterfaceNameIsInvalid() { - final String invalidIfaceName = "123" + TEST_TAP_PREFIX; - tracker.setIncludeTestInterfaces(true); - waitForIdle(); - - final boolean isValidTestInterface = tracker.isValidTestInterface(invalidIfaceName); - - assertFalse(isValidTestInterface); - } - - @Test - public void testIsValidTestInterfaceIsTrueWhenTestInterfacesIncludedAndValidName() { - final String validIfaceName = TEST_TAP_PREFIX + "123"; - tracker.setIncludeTestInterfaces(true); - waitForIdle(); - - final boolean isValidTestInterface = tracker.isValidTestInterface(validIfaceName); - - assertTrue(isValidTestInterface); - } - - public static class EthernetStateListener extends IEthernetServiceListener.Stub { - @Override - public void onEthernetStateChanged(int state) { } - - @Override - public void onInterfaceStateChanged(String iface, int state, int role, - IpConfiguration configuration) { } - } - - @Test - public void testListenEthernetStateChange() throws Exception { - final String testIface = "testtap123"; - final String testHwAddr = "11:22:33:44:55:66"; - final InterfaceConfigurationParcel ifaceParcel = new InterfaceConfigurationParcel(); - ifaceParcel.ifName = testIface; - ifaceParcel.hwAddr = testHwAddr; - ifaceParcel.flags = new String[] {INetd.IF_STATE_UP}; - - tracker.setIncludeTestInterfaces(true); - waitForIdle(); - - when(mNetd.interfaceGetList()).thenReturn(new String[] {testIface}); - when(mNetd.interfaceGetCfg(eq(testIface))).thenReturn(ifaceParcel); - doReturn(new String[] {testIface}).when(mFactory).getAvailableInterfaces(anyBoolean()); - doReturn(EthernetManager.STATE_LINK_UP).when(mFactory).getInterfaceState(eq(testIface)); - - final EthernetStateListener listener = spy(new EthernetStateListener()); - tracker.addListener(listener, true /* canUseRestrictedNetworks */); - // Check default state. - waitForIdle(); - verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_LINK_UP), - anyInt(), any()); - verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_ENABLED)); - reset(listener); - - doReturn(EthernetManager.STATE_ABSENT).when(mFactory).getInterfaceState(eq(testIface)); - tracker.setEthernetEnabled(false); - waitForIdle(); - verify(mFactory).removeInterface(eq(testIface)); - verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_DISABLED)); - verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_ABSENT), - anyInt(), any()); - reset(listener); - - doReturn(EthernetManager.STATE_LINK_UP).when(mFactory).getInterfaceState(eq(testIface)); - tracker.setEthernetEnabled(true); - waitForIdle(); - verify(mFactory).addInterface(eq(testIface), eq(testHwAddr), any(), any()); - verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_ENABLED)); - verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_LINK_UP), - anyInt(), any()); - } -} -- cgit v1.2.3