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
This commit is contained in:
James Mattis
2022-02-26 22:16:46 -08:00
parent ed41dfeb44
commit b0cc8f09a4
4 changed files with 176 additions and 5 deletions

View File

@@ -16,6 +16,8 @@
package com.android.server.ethernet; package com.android.server.ethernet;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.content.Context; import android.content.Context;
@@ -26,6 +28,7 @@ import android.net.IEthernetNetworkManagementListener;
import android.net.ITetheredInterfaceCallback; import android.net.ITetheredInterfaceCallback;
import android.net.EthernetNetworkUpdateRequest; import android.net.EthernetNetworkUpdateRequest;
import android.net.IpConfiguration; import android.net.IpConfiguration;
import android.net.NetworkCapabilities;
import android.os.Binder; import android.os.Binder;
import android.os.Handler; import android.os.Handler;
import android.os.RemoteException; import android.os.RemoteException;
@@ -206,6 +209,12 @@ public class EthernetServiceImpl extends IEthernetManager.Stub {
"EthernetServiceImpl"); "EthernetServiceImpl");
} }
private void enforceManageTestNetworksPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MANAGE_TEST_NETWORKS,
"EthernetServiceImpl");
}
/** /**
* Validate the state of ethernet for APIs tied to network management. * 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(iface, "Pass a non-null iface.");
Objects.requireNonNull(methodName, "Pass a non-null methodName."); 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); enforceAutomotiveDevice(methodName);
enforceNetworkManagementPermission(); enforceNetworkManagementPermission();
}
logIfEthernetNotStarted(); 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 @Override
public void updateConfiguration(@NonNull final String iface, public void updateConfiguration(@NonNull final String iface,
@NonNull final EthernetNetworkUpdateRequest request, @NonNull final EthernetNetworkUpdateRequest request,
@Nullable final IEthernetNetworkManagementListener listener) { @Nullable final IEthernetNetworkManagementListener listener) {
Log.i(TAG, "updateConfiguration called with: iface=" + iface
+ ", request=" + request + ", listener=" + listener);
validateNetworkManagementState(iface, "updateConfiguration()"); 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 // TODO: validate that iface is listed in overlay config_ethernet_interfaces
mTracker.updateConfiguration( mTracker.updateConfiguration(

View File

@@ -86,6 +86,9 @@ public class EthernetTracker {
* if setIncludeTestInterfaces is true, any test interfaces. * if setIncludeTestInterfaces is true, any test interfaces.
*/ */
private String mIfaceMatch; private String mIfaceMatch;
/**
* Track test interfaces if true, don't track otherwise.
*/
private boolean mIncludeTestInterfaces = false; private boolean mIncludeTestInterfaces = false;
/** Mapping between {iface name | mac address} -> {NetworkCapabilities} */ /** Mapping between {iface name | mac address} -> {NetworkCapabilities} */
@@ -738,6 +741,17 @@ public class EthernetTracker {
Log.d(TAG, "Interface match regexp set to '" + mIfaceMatch + "'"); 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) { private void postAndWaitForRunnable(Runnable r) {
final ConditionVariable cv = new ConditionVariable(); final ConditionVariable cv = new ConditionVariable();
if (mHandler.post(() -> { if (mHandler.post(() -> {

View File

@@ -16,6 +16,8 @@
package com.android.server.ethernet; package com.android.server.ethernet;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.anyString; 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.doReturn;
import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.Manifest; import android.Manifest;
import android.annotation.NonNull; import android.annotation.NonNull;
@@ -162,6 +165,12 @@ public class EthernetServiceImplTest {
eq(Manifest.permission.MANAGE_ETHERNET_NETWORKS), anyString()); eq(Manifest.permission.MANAGE_ETHERNET_NETWORKS), anyString());
} }
private void denyManageTestNetworksPermission() {
doThrow(new SecurityException("")).when(mContext)
.enforceCallingOrSelfPermission(
eq(Manifest.permission.MANAGE_TEST_NETWORKS), anyString());
}
@Test @Test
public void testUpdateConfigurationRejectsWithoutManageEthPermission() { public void testUpdateConfigurationRejectsWithoutManageEthPermission() {
denyManageEthPermission(); 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 @Test
public void testUpdateConfiguration() { public void testUpdateConfiguration() {
mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER);
@@ -206,4 +246,56 @@ public class EthernetServiceImplTest {
mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER);
verify(mEthernetTracker).disconnectNetwork(eq(TEST_IFACE), eq(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));
}
} }

View File

@@ -16,8 +16,12 @@
package com.android.server.ethernet; package com.android.server.ethernet;
import static android.net.TestNetworkManager.TEST_TAP_PREFIX;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -39,6 +43,7 @@ import android.net.LinkAddress;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.net.StaticIpConfiguration; import android.net.StaticIpConfiguration;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.RemoteException;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.AndroidJUnit4;
@@ -71,10 +76,11 @@ public class EthernetTrackerTest {
@Mock Resources mResources; @Mock Resources mResources;
@Before @Before
public void setUp() { public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
initMockResources(); initMockResources();
when(mFactory.updateInterfaceLinkState(anyString(), anyBoolean(), any())).thenReturn(false); when(mFactory.updateInterfaceLinkState(anyString(), anyBoolean(), any())).thenReturn(false);
when(mNetd.interfaceGetList()).thenReturn(new String[0]);
mHandlerThread = new HandlerThread(THREAD_NAME); mHandlerThread = new HandlerThread(THREAD_NAME);
mHandlerThread.start(); mHandlerThread.start();
tracker = new EthernetTracker(mContext, mHandlerThread.getThreadHandler(), mFactory, mNetd); tracker = new EthernetTracker(mContext, mHandlerThread.getThreadHandler(), mFactory, mNetd);
@@ -355,4 +361,37 @@ public class EthernetTrackerTest {
verify(mFactory).updateInterfaceLinkState(eq(TEST_IFACE), eq(false /* up */), verify(mFactory).updateInterfaceLinkState(eq(TEST_IFACE), eq(false /* up */),
eq(NULL_LISTENER)); 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);
}
} }