Have a collection of utilities for netd - NetdUtils

Some files which will be inside mainline module depend on
NetworkManagementService to talk to netd, but after they become
a part of mainline module, they cannot access @hide API of
NetworkManagementService.
So create a NetdUtils to help them to talk to netd.

Bug: 170598012
Test: atest CtsNetTestCasesLatestSdk NetdStaticLibTests
Change-Id: I8bee1204b9533b70844da0b3768427438fd0c890
This commit is contained in:
lucaslin 2021-01-29 19:19:27 +08:00 • committed by Lucas Lin
commit 5c620643b7
5 changed files with 278 additions and 1 deletions

View file

@ -9,7 +9,9 @@ java_library {
],
visibility: [
"//packages/modules/Connectivity/Tethering",
"//frameworks/base/services:__subpackages__"
"//frameworks/base/services:__subpackages__",
"//frameworks/base/packages:__subpackages__",
"//frameworks/libs/net/client-libs/tests:__subpackages__",
],
libs: ["androidx.annotation_annotation"],
static_libs: [

View file

@ -0,0 +1,105 @@
/*
* 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.net.module.util;
import static android.net.INetd.IF_STATE_DOWN;
import static android.net.INetd.IF_STATE_UP;
import android.net.INetd;
import android.net.InterfaceConfigurationParcel;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import java.util.ArrayList;
/**
* Collection of utilities for netd.
*/
public class NetdUtils {
/**
* Get InterfaceConfigurationParcel from netd.
*/
public static InterfaceConfigurationParcel getInterfaceConfigParcel(@NonNull INetd netd,
@NonNull String iface) {
try {
return netd.interfaceGetCfg(iface);
} catch (RemoteException | ServiceSpecificException e) {
throw new IllegalStateException(e);
}
}
private static void validateFlag(String flag) {
if (flag.indexOf(' ') >= 0) {
throw new IllegalArgumentException("flag contains space: " + flag);
}
}
@VisibleForTesting
protected static String[] removeAndAddFlags(@NonNull String[] flags, @NonNull String remove,
@NonNull String add) {
final ArrayList<String> result = new ArrayList<>();
try {
// Validate the add flag first, so that the for-loop can be ignore once the format of
// add flag is invalid.
validateFlag(add);
for (String flag : flags) {
// Simply ignore both of remove and add flags first, then add the add flag after
// exiting the loop to prevent adding the duplicate flag.
if (remove.equals(flag) || add.equals(flag)) continue;
result.add(flag);
}
result.add(add);
return result.toArray(new String[result.size()]);
} catch (IllegalArgumentException iae) {
throw new IllegalStateException("Invalid InterfaceConfigurationParcel", iae);
}
}
/**
* Set interface configuration to netd by passing InterfaceConfigurationParcel.
*/
public static void setInterfaceConfig(INetd netd, InterfaceConfigurationParcel configParcel) {
try {
netd.interfaceSetCfg(configParcel);
} catch (RemoteException | ServiceSpecificException e) {
throw new IllegalStateException(e);
}
}
/**
* Set the given interface up.
*/
public static void setInterfaceUp(INetd netd, String iface) {
final InterfaceConfigurationParcel configParcel = getInterfaceConfigParcel(netd, iface);
configParcel.flags = removeAndAddFlags(configParcel.flags, IF_STATE_DOWN /* remove */,
IF_STATE_UP /* add */);
setInterfaceConfig(netd, configParcel);
}
/**
* Set the given interface down.
*/
public static void setInterfaceDown(INetd netd, String iface) {
final InterfaceConfigurationParcel configParcel = getInterfaceConfigParcel(netd, iface);
configParcel.flags = removeAndAddFlags(configParcel.flags, IF_STATE_UP /* remove */,
IF_STATE_DOWN /* add */);
setInterfaceConfig(netd, configParcel);
}
}

View file

@ -0,0 +1,39 @@
android_library {
name: "NetdStaticLibTestsLib",
srcs: [
"src/**/*.java",
"src/**/*.kt",
],
min_sdk_version: "29",
static_libs: [
"androidx.test.rules",
"mockito-target-extended-minus-junit4",
"net-tests-utils-host-device-common",
"netd-client",
],
libs: [
"android.test.runner",
"android.test.base",
],
visibility: [
// Visible for Tethering and NetworkStack integration test and link NetdStaticLibTestsLib
// there, so that the tests under client-libs can also be run when running tethering and
// NetworkStack MTS.
"//packages/modules/Connectivity/Tethering/tests/integration",
"//packages/modules/NetworkStack/tests/integration",
]
}
android_test {
name: "NetdStaticLibTests",
certificate: "platform",
static_libs: [
"NetdStaticLibTestsLib",
],
jni_libs: [
// For mockito extended
"libdexmakerjvmtiagent",
"libstaticjvmtiagent",
],
test_suites: ["device-tests"],
}

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.frameworks.clientlibs.tests">
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
</application>
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.frameworks.clientlibs.tests"
android:label="Netd Static Library Tests" />
</manifest>

View file

@ -0,0 +1,103 @@
/*
* 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.net.module.util;
import static com.android.testutils.MiscAsserts.assertThrows;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.net.INetd;
import android.net.InterfaceConfigurationParcel;
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;
import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class NetdUtilsTest {
@Mock private INetd mNetd;
private static final String IFACE = "TEST_IFACE";
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
private void setupFlagsForInterfaceConfiguration(String[] flags) throws Exception {
final InterfaceConfigurationParcel config = new InterfaceConfigurationParcel();
config.flags = flags;
when(mNetd.interfaceGetCfg(eq(IFACE))).thenReturn(config);
}
private void verifyMethodsAndArgumentsOfSetInterface(boolean ifaceUp) throws Exception {
final String[] flagsContainDownAndUp = new String[] {"flagA", "down", "flagB", "up"};
final String[] flagsForInterfaceDown = new String[] {"flagA", "down", "flagB"};
final String[] flagsForInterfaceUp = new String[] {"flagA", "up", "flagB"};
final String[] expectedFinalFlags;
setupFlagsForInterfaceConfiguration(flagsContainDownAndUp);
if (ifaceUp) {
// "down" flag will be removed from flagsContainDownAndUp when interface is up. Set
// expectedFinalFlags to flagsForInterfaceUp.
expectedFinalFlags = flagsForInterfaceUp;
NetdUtils.setInterfaceUp(mNetd, IFACE);
} else {
// "up" flag will be removed from flagsContainDownAndUp when interface is down. Set
// expectedFinalFlags to flagsForInterfaceDown.
expectedFinalFlags = flagsForInterfaceDown;
NetdUtils.setInterfaceDown(mNetd, IFACE);
}
verify(mNetd).interfaceSetCfg(
argThat(config ->
// Check if actual flags are the same as expected flags.
// TODO: Have a function in MiscAsserts to check if two arrays are the same.
CollectionUtils.all(Arrays.asList(expectedFinalFlags),
flag -> Arrays.asList(config.flags).contains(flag))
&& CollectionUtils.all(Arrays.asList(config.flags),
flag -> Arrays.asList(expectedFinalFlags).contains(flag))));
}
@Test
public void testSetInterfaceUp() throws Exception {
verifyMethodsAndArgumentsOfSetInterface(true /* ifaceUp */);
}
@Test
public void testSetInterfaceDown() throws Exception {
verifyMethodsAndArgumentsOfSetInterface(false /* ifaceUp */);
}
@Test
public void testRemoveAndAddFlags() throws Exception {
final String[] flags = new String[] {"flagA", "down", "flagB"};
// Add an invalid flag and expect to get an IllegalStateException.
assertThrows(IllegalStateException.class,
() -> NetdUtils.removeAndAddFlags(flags, "down" /* remove */, "u p" /* add */));
}
}