Merge "[Thread] use TestRule to filter Thread tests" into main am: f8ffba4c04

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/2996876

Change-Id: I12335903cb53a8d4e1fa5d4a8273646814537501
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Kangping Dong 2024-03-17 14:34:18 +00:00 • committed by Automerger Merge Worker
commit 51d9b9f241
8 changed files with 255 additions and 130 deletions

View file

@ -74,42 +74,61 @@ import java.util.Arrays;
public final class ActiveOperationalDataset implements Parcelable {
/** The maximum length of the Active Operational Dataset TLV array in bytes. */
public static final int LENGTH_MAX_DATASET_TLVS = 254;
/** The length of Extended PAN ID in bytes. */
public static final int LENGTH_EXTENDED_PAN_ID = 8;
/** The minimum length of Network Name as UTF-8 bytes. */
public static final int LENGTH_MIN_NETWORK_NAME_BYTES = 1;
/** The maximum length of Network Name as UTF-8 bytes. */
public static final int LENGTH_MAX_NETWORK_NAME_BYTES = 16;
/** The length of Network Key in bytes. */
public static final int LENGTH_NETWORK_KEY = 16;
/** The length of Mesh-Local Prefix in bits. */
public static final int LENGTH_MESH_LOCAL_PREFIX_BITS = 64;
/** The length of PSKc in bytes. */
public static final int LENGTH_PSKC = 16;
/** The 2.4 GHz channel page. */
public static final int CHANNEL_PAGE_24_GHZ = 0;
/** The minimum 2.4GHz channel. */
public static final int CHANNEL_MIN_24_GHZ = 11;
/** The maximum 2.4GHz channel. */
public static final int CHANNEL_MAX_24_GHZ = 26;
/** @hide */
@VisibleForTesting public static final int TYPE_CHANNEL = 0;
/** @hide */
@VisibleForTesting public static final int TYPE_PAN_ID = 1;
/** @hide */
@VisibleForTesting public static final int TYPE_EXTENDED_PAN_ID = 2;
/** @hide */
@VisibleForTesting public static final int TYPE_NETWORK_NAME = 3;
/** @hide */
@VisibleForTesting public static final int TYPE_PSKC = 4;
/** @hide */
@VisibleForTesting public static final int TYPE_NETWORK_KEY = 5;
/** @hide */
@VisibleForTesting public static final int TYPE_MESH_LOCAL_PREFIX = 7;
/** @hide */
@VisibleForTesting public static final int TYPE_SECURITY_POLICY = 12;
/** @hide */
@VisibleForTesting public static final int TYPE_ACTIVE_TIMESTAMP = 14;
/** @hide */
@VisibleForTesting public static final int TYPE_CHANNEL_MASK = 53;
@ -975,8 +994,10 @@ public final class ActiveOperationalDataset implements Parcelable {
public static final class SecurityPolicy {
/** The default Rotation Time in hours. */
public static final int DEFAULT_ROTATION_TIME_HOURS = 672;
/** The minimum length of Security Policy flags in bytes. */
public static final int LENGTH_MIN_SECURITY_POLICY_FLAGS = 1;
/** The length of Rotation Time TLV value in bytes. */
private static final int LENGTH_SECURITY_POLICY_ROTATION_TIME = 2;

View file

@ -46,10 +46,13 @@ import java.io.InputStream;
*/
public class ThreadPersistentSettings {
private static final String TAG = "ThreadPersistentSettings";
/** File name used for storing settings. */
private static final String FILE_NAME = "ThreadPersistentSettings.xml";
/** Current config store data version. This will be incremented for any additions. */
private static final int CURRENT_SETTINGS_STORE_DATA_VERSION = 1;
/**
* Stores the version of the data. This can be used to handle migration of data if some
* non-backward compatible change introduced.

View file

@ -40,7 +40,6 @@ import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeNotNull;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@ -60,7 +59,8 @@ import android.net.thread.ThreadNetworkController.StateCallback;
import android.net.thread.ThreadNetworkException;
import android.net.thread.ThreadNetworkManager;
import android.net.thread.utils.TapTestNetworkTracker;
import android.os.Build;
import android.net.thread.utils.ThreadFeatureCheckerRule;
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
import android.os.HandlerThread;
import android.os.OutcomeReceiver;
@ -69,16 +69,12 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.LargeTest;
import com.android.net.module.util.ArrayTrackRecord;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.FunctionalUtils.ThrowingRunnable;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
@ -99,8 +95,7 @@ import java.util.function.Predicate;
/** CTS tests for {@link ThreadNetworkController}. */
@LargeTest
@RunWith(DevSdkIgnoreRunner.class)
@IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) // Thread is available on only U+
@RequiresThreadFeature
public class ThreadNetworkControllerTest {
private static final int JOIN_TIMEOUT_MILLIS = 30 * 1000;
private static final int LEAVE_TIMEOUT_MILLIS = 2_000;
@ -114,7 +109,7 @@ public class ThreadNetworkControllerTest {
private static final String THREAD_NETWORK_PRIVILEGED =
"android.permission.THREAD_NETWORK_PRIVILEGED";
@Rule public DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
@Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
private final Context mContext = ApplicationProvider.getApplicationContext();
private ExecutorService mExecutor;
@ -127,14 +122,10 @@ public class ThreadNetworkControllerTest {
@Before
public void setUp() throws Exception {
ThreadNetworkManager manager = mContext.getSystemService(ThreadNetworkManager.class);
if (manager != null) {
mController = manager.getAllThreadNetworkControllers().get(0);
}
// TODO: we will also need it in tearDown(), it's better to have a Rule to skip
// tests if a feature is not available.
assumeNotNull(mController);
mController =
mContext.getSystemService(ThreadNetworkManager.class)
.getAllThreadNetworkControllers()
.get(0);
mGrantedPermissions = new HashSet<String>();
mExecutor = Executors.newSingleThreadExecutor();
@ -147,9 +138,6 @@ public class ThreadNetworkControllerTest {
@After
public void tearDown() throws Exception {
if (mController == null) {
return;
}
dropAllPermissions();
leaveAndWait(mController);
tearDownTestNetwork();

View file

@ -24,8 +24,6 @@ import static android.net.thread.utils.IntegrationTestUtils.RESTART_JOIN_TIMEOUT
import static android.net.thread.utils.IntegrationTestUtils.isExpectedIcmpv6Packet;
import static android.net.thread.utils.IntegrationTestUtils.isFromIpv6Source;
import static android.net.thread.utils.IntegrationTestUtils.isInMulticastGroup;
import static android.net.thread.utils.IntegrationTestUtils.isMulticastRoutingSupported;
import static android.net.thread.utils.IntegrationTestUtils.isSimulatedThreadRadioSupported;
import static android.net.thread.utils.IntegrationTestUtils.isToIpv6Destination;
import static android.net.thread.utils.IntegrationTestUtils.newPacketReader;
import static android.net.thread.utils.IntegrationTestUtils.pollForPacket;
@ -44,8 +42,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assume.assumeNotNull;
import static org.junit.Assume.assumeTrue;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@ -56,6 +52,10 @@ import android.net.MacAddress;
import android.net.thread.utils.FullThreadDevice;
import android.net.thread.utils.InfraNetworkDevice;
import android.net.thread.utils.OtDaemonController;
import android.net.thread.utils.ThreadFeatureCheckerRule;
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresIpv6MulticastRouting;
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresSimulationThreadDevice;
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
import android.os.Handler;
import android.os.HandlerThread;
@ -68,6 +68,7 @@ import com.android.testutils.TestNetworkTracker;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -82,19 +83,11 @@ import java.util.function.Predicate;
/** Integration test cases for Thread Border Routing feature. */
@RunWith(AndroidJUnit4.class)
@RequiresThreadFeature
@RequiresSimulationThreadDevice
@LargeTest
public class BorderRoutingTest {
private static final String TAG = BorderRoutingTest.class.getSimpleName();
private final Context mContext = ApplicationProvider.getApplicationContext();
private ThreadNetworkController mController;
private OtDaemonController mOtCtl;
private HandlerThread mHandlerThread;
private Handler mHandler;
private TestNetworkTracker mInfraNetworkTracker;
private List<FullThreadDevice> mFtds;
private TapPacketReader mInfraNetworkReader;
private InfraNetworkDevice mInfraDevice;
private static final int NUM_FTD = 2;
private static final Inet6Address GROUP_ADDR_SCOPE_5 =
(Inet6Address) InetAddresses.parseNumericAddress("ff05::1234");
@ -114,17 +107,25 @@ public class BorderRoutingTest {
private static final ActiveOperationalDataset DEFAULT_DATASET =
ActiveOperationalDataset.fromThreadTlvs(DEFAULT_DATASET_TLVS);
@Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
private final Context mContext = ApplicationProvider.getApplicationContext();
private ThreadNetworkController mController;
private OtDaemonController mOtCtl;
private HandlerThread mHandlerThread;
private Handler mHandler;
private TestNetworkTracker mInfraNetworkTracker;
private List<FullThreadDevice> mFtds;
private TapPacketReader mInfraNetworkReader;
private InfraNetworkDevice mInfraDevice;
@Before
public void setUp() throws Exception {
assumeTrue(isSimulatedThreadRadioSupported());
final ThreadNetworkManager manager = mContext.getSystemService(ThreadNetworkManager.class);
if (manager != null) {
mController = manager.getAllThreadNetworkControllers().get(0);
}
// Run the tests on only devices where the Thread feature is available
assumeNotNull(mController);
// TODO: b/323301831 - This is a workaround to avoid unnecessary delay to re-form a network
mOtCtl = new OtDaemonController();
mOtCtl.factoryReset();
@ -151,10 +152,6 @@ public class BorderRoutingTest {
@After
public void tearDown() throws Exception {
if (mController == null) {
return;
}
runAsShell(
PERMISSION_THREAD_NETWORK_PRIVILEGED,
NETWORK_SETTINGS,
@ -225,8 +222,6 @@ public class BorderRoutingTest {
@Test
public void unicastRouting_borderRouterSendsUdpToThreadDevice_datagramReceived()
throws Exception {
assumeTrue(isSimulatedThreadRadioSupported());
/*
* <pre>
* Topology:
@ -260,9 +255,9 @@ public class BorderRoutingTest {
}
@Test
@RequiresIpv6MulticastRouting
public void multicastRouting_ftdSubscribedMulticastAddress_infraLinkJoinsMulticastGroup()
throws Exception {
assumeTrue(isMulticastRoutingSupported());
/*
* <pre>
* Topology:
@ -281,10 +276,10 @@ public class BorderRoutingTest {
}
@Test
@RequiresIpv6MulticastRouting
public void
multicastRouting_ftdSubscribedScope3MulticastAddress_infraLinkNotJoinMulticastGroup()
throws Exception {
assumeTrue(isMulticastRoutingSupported());
/*
* <pre>
* Topology:
@ -303,9 +298,9 @@ public class BorderRoutingTest {
}
@Test
@RequiresIpv6MulticastRouting
public void multicastRouting_ftdSubscribedMulticastAddress_canPingfromInfraLink()
throws Exception {
assumeTrue(isMulticastRoutingSupported());
/*
* <pre>
* Topology:
@ -325,9 +320,9 @@ public class BorderRoutingTest {
}
@Test
@RequiresIpv6MulticastRouting
public void multicastRouting_inboundForwarding_afterBrRejoinFtdRepliesSubscribedAddress()
throws Exception {
assumeTrue(isMulticastRoutingSupported());
// TODO (b/327311034): Testing bbr state switch from primary mode to secondary mode and back
// to primary mode requires an additional BR in the Thread network. This is not currently
@ -335,9 +330,9 @@ public class BorderRoutingTest {
}
@Test
@RequiresIpv6MulticastRouting
public void multicastRouting_ftdSubscribedScope3MulticastAddress_cannotPingfromInfraLink()
throws Exception {
assumeTrue(isMulticastRoutingSupported());
/*
* <pre>
* Topology:
@ -357,9 +352,9 @@ public class BorderRoutingTest {
}
@Test
@RequiresIpv6MulticastRouting
public void multicastRouting_ftdNotSubscribedMulticastAddress_cannotPingFromInfraDevice()
throws Exception {
assumeTrue(isMulticastRoutingSupported());
/*
* <pre>
* Topology:
@ -378,9 +373,9 @@ public class BorderRoutingTest {
}
@Test
@RequiresIpv6MulticastRouting
public void multicastRouting_multipleFtdsSubscribedDifferentAddresses_canPingFromInfraDevice()
throws Exception {
assumeTrue(isMulticastRoutingSupported());
/*
* <pre>
* Topology:
@ -414,9 +409,9 @@ public class BorderRoutingTest {
}
@Test
@RequiresIpv6MulticastRouting
public void multicastRouting_multipleFtdsSubscribedSameAddress_canPingFromInfraDevice()
throws Exception {
assumeTrue(isMulticastRoutingSupported());
/*
* <pre>
* Topology:
@ -449,8 +444,8 @@ public class BorderRoutingTest {
}
@Test
@RequiresIpv6MulticastRouting
public void multicastRouting_outboundForwarding_scopeLargerThan3IsForwarded() throws Exception {
assumeTrue(isMulticastRoutingSupported());
/*
* <pre>
* Topology:
@ -474,9 +469,9 @@ public class BorderRoutingTest {
}
@Test
@RequiresIpv6MulticastRouting
public void multicastRouting_outboundForwarding_scopeSmallerThan4IsNotForwarded()
throws Exception {
assumeTrue(isMulticastRoutingSupported());
/*
* <pre>
* Topology:
@ -497,8 +492,8 @@ public class BorderRoutingTest {
}
@Test
@RequiresIpv6MulticastRouting
public void multicastRouting_outboundForwarding_llaToScope4IsNotForwarded() throws Exception {
assumeTrue(isMulticastRoutingSupported());
/*
* <pre>
* Topology:
@ -520,8 +515,8 @@ public class BorderRoutingTest {
}
@Test
@RequiresIpv6MulticastRouting
public void multicastRouting_outboundForwarding_mlaToScope4IsNotForwarded() throws Exception {
assumeTrue(isMulticastRoutingSupported());
/*
* <pre>
* Topology:
@ -546,9 +541,9 @@ public class BorderRoutingTest {
}
@Test
@RequiresIpv6MulticastRouting
public void multicastRouting_infraNetworkSwitch_ftdRepliesToSubscribedAddress()
throws Exception {
assumeTrue(isMulticastRoutingSupported());
/*
* <pre>
* Topology:
@ -575,8 +570,8 @@ public class BorderRoutingTest {
}
@Test
@RequiresIpv6MulticastRouting
public void multicastRouting_infraNetworkSwitch_outboundPacketIsForwarded() throws Exception {
assumeTrue(isMulticastRoutingSupported());
/*
* <pre>
* Topology:

View file

@ -24,7 +24,6 @@ import static android.net.thread.utils.IntegrationTestUtils.RESTART_JOIN_TIMEOUT
import static android.net.thread.utils.IntegrationTestUtils.SERVICE_DISCOVERY_TIMEOUT;
import static android.net.thread.utils.IntegrationTestUtils.discoverForServiceLost;
import static android.net.thread.utils.IntegrationTestUtils.discoverService;
import static android.net.thread.utils.IntegrationTestUtils.isSimulatedThreadRadioSupported;
import static android.net.thread.utils.IntegrationTestUtils.resolveService;
import static android.net.thread.utils.IntegrationTestUtils.resolveServiceUntil;
import static android.net.thread.utils.IntegrationTestUtils.waitFor;
@ -36,8 +35,6 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static org.junit.Assert.assertThrows;
import static org.junit.Assume.assumeNotNull;
import static org.junit.Assume.assumeTrue;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
@ -47,6 +44,9 @@ import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.net.thread.utils.FullThreadDevice;
import android.net.thread.utils.TapTestNetworkTracker;
import android.net.thread.utils.ThreadFeatureCheckerRule;
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresSimulationThreadDevice;
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
import android.os.HandlerThread;
import androidx.test.core.app.ApplicationProvider;
@ -58,6 +58,7 @@ import com.google.common.truth.Correspondence;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -73,18 +74,13 @@ import java.util.concurrent.TimeoutException;
/** Integration test cases for Service Discovery feature. */
@RunWith(AndroidJUnit4.class)
@RequiresThreadFeature
@RequiresSimulationThreadDevice
@LargeTest
@Ignore("TODO: b/328527773 - enable the test when it's stable")
public class ServiceDiscoveryTest {
private static final String TAG = ServiceDiscoveryTest.class.getSimpleName();
private static final int NUM_FTD = 3;
private final Context mContext = ApplicationProvider.getApplicationContext();
private HandlerThread mHandlerThread;
private ThreadNetworkController mController;
private NsdManager mNsdManager;
private TapTestNetworkTracker mTestNetworkTracker;
private List<FullThreadDevice> mFtds;
// A valid Thread Active Operational Dataset generated from OpenThread CLI "dataset init new".
private static final byte[] DEFAULT_DATASET_TLVS =
@ -100,6 +96,16 @@ public class ServiceDiscoveryTest {
private static final Correspondence<byte[], byte[]> BYTE_ARRAY_EQUALITY =
Correspondence.from(Arrays::equals, "is equivalent to");
@Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
private final Context mContext = ApplicationProvider.getApplicationContext();
private HandlerThread mHandlerThread;
private ThreadNetworkController mController;
private NsdManager mNsdManager;
private TapTestNetworkTracker mTestNetworkTracker;
private List<FullThreadDevice> mFtds;
@Before
public void setUp() throws Exception {
final ThreadNetworkManager manager = mContext.getSystemService(ThreadNetworkManager.class);
@ -107,12 +113,6 @@ public class ServiceDiscoveryTest {
mController = manager.getAllThreadNetworkControllers().get(0);
}
// Run the tests on only devices where the Thread feature is available.
assumeNotNull(mController);
// Run the tests only when the device uses simulated Thread radio.
assumeTrue(isSimulatedThreadRadioSupported());
// BR forms a network.
CompletableFuture<Void> joinFuture = new CompletableFuture<>();
runAsShell(
@ -150,12 +150,6 @@ public class ServiceDiscoveryTest {
@After
public void tearDown() throws Exception {
if (mController == null) {
return;
}
if (!isSimulatedThreadRadioSupported()) {
return;
}
for (FullThreadDevice ftd : mFtds) {
// Clear registered SRP hosts and services
if (ftd.isSrpHostRegistered()) {

View file

@ -33,14 +33,14 @@ import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static org.junit.Assume.assumeNotNull;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import android.annotation.Nullable;
import android.content.Context;
import android.net.thread.ThreadNetworkController.StateCallback;
import android.net.thread.utils.OtDaemonController;
import android.net.thread.utils.ThreadFeatureCheckerRule;
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
import android.os.SystemClock;
import androidx.test.core.app.ApplicationProvider;
@ -49,6 +49,7 @@ import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -58,12 +59,9 @@ import java.util.concurrent.CompletableFuture;
/** Tests for E2E Android Thread integration with ot-daemon, ConnectivityService, etc.. */
@LargeTest
@RequiresThreadFeature
@RunWith(AndroidJUnit4.class)
public class ThreadIntegrationTest {
private final Context mContext = ApplicationProvider.getApplicationContext();
private ThreadNetworkController mController;
private OtDaemonController mOtCtl;
// A valid Thread Active Operational Dataset generated from OpenThread CLI "dataset init new".
private static final byte[] DEFAULT_DATASET_TLVS =
base16().decode(
@ -75,16 +73,18 @@ public class ThreadIntegrationTest {
private static final ActiveOperationalDataset DEFAULT_DATASET =
ActiveOperationalDataset.fromThreadTlvs(DEFAULT_DATASET_TLVS);
@Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
private final Context mContext = ApplicationProvider.getApplicationContext();
private ThreadNetworkController mController;
private OtDaemonController mOtCtl;
@Before
public void setUp() throws Exception {
final ThreadNetworkManager manager = mContext.getSystemService(ThreadNetworkManager.class);
if (manager != null) {
mController = manager.getAllThreadNetworkControllers().get(0);
}
// Run the tests on only devices where the Thread feature is available
assumeNotNull(mController);
mController =
mContext.getSystemService(ThreadNetworkManager.class)
.getAllThreadNetworkControllers()
.get(0);
mOtCtl = new OtDaemonController();
leaveAndWait(mController);
@ -94,10 +94,6 @@ public class ThreadIntegrationTest {
@After
public void tearDown() throws Exception {
if (mController == null) {
return;
}
setTestUpStreamNetworkAndWait(mController, null);
leaveAndWait(mController);
}

View file

@ -20,7 +20,6 @@ import static android.system.OsConstants.IPPROTO_ICMPV6;
import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_PIO;
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
import static com.android.testutils.DeviceInfoUtils.isKernelVersionAtLeast;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
@ -32,8 +31,6 @@ import android.net.nsd.NsdServiceInfo;
import android.net.thread.ThreadNetworkController;
import android.os.Handler;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.VintfRuntimeInfo;
import androidx.annotation.NonNull;
@ -65,8 +62,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** Static utility methods relating to Thread integration tests. */
public final class IntegrationTestUtils {
@ -79,35 +74,8 @@ public final class IntegrationTestUtils {
public static final Duration CALLBACK_TIMEOUT = Duration.ofSeconds(1);
public static final Duration SERVICE_DISCOVERY_TIMEOUT = Duration.ofSeconds(20);
private static final String KERNEL_VERSION_MULTICAST_ROUTING_SUPPORTED = "5.15.0";
private static final int KERNEL_ANDROID_VERSION_MULTICAST_ROUTING_SUPPORTED = 14;
private IntegrationTestUtils() {}
/** Returns whether the device supports simulated Thread radio. */
public static boolean isSimulatedThreadRadioSupported() {
// The integration test uses SIMULATION Thread radio so that it only supports CuttleFish.
return SystemProperties.get("ro.product.model").startsWith("Cuttlefish");
}
public static boolean isMulticastRoutingSupported() {
return isKernelVersionAtLeast(KERNEL_VERSION_MULTICAST_ROUTING_SUPPORTED)
&& isKernelAndroidVersionAtLeast(
KERNEL_ANDROID_VERSION_MULTICAST_ROUTING_SUPPORTED);
}
private static boolean isKernelAndroidVersionAtLeast(int n) {
final String osRelease = VintfRuntimeInfo.getOsRelease();
final Pattern pattern = Pattern.compile("android(\\d+)");
Matcher matcher = pattern.matcher(osRelease);
if (matcher.find()) {
int version = Integer.parseInt(matcher.group(1));
return (version >= n);
}
return false;
}
/**
* Waits for the given {@link Supplier} to be true until given timeout.
*

View file

@ -0,0 +1,160 @@
/*
* Copyright (C) 2024 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 android.net.thread.utils;
import static com.android.testutils.DeviceInfoUtils.isKernelVersionAtLeast;
import static org.junit.Assume.assumeTrue;
import android.content.Context;
import android.net.thread.ThreadNetworkManager;
import android.os.SystemProperties;
import android.os.VintfRuntimeInfo;
import androidx.test.core.app.ApplicationProvider;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A rule used to skip Thread tests when the device doesn't support a specific feature indicated by
* {@code ThreadFeatureCheckerRule.Requires*}.
*/
public final class ThreadFeatureCheckerRule implements TestRule {
private static final String KERNEL_VERSION_MULTICAST_ROUTING_SUPPORTED = "5.15.0";
private static final int KERNEL_ANDROID_VERSION_MULTICAST_ROUTING_SUPPORTED = 14;
/**
* Annotates a test class or method requires the Thread feature to run.
*
* <p>In Absence of the Thread feature, the test class or method will be ignored.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface RequiresThreadFeature {}
/**
* Annotates a test class or method requires the kernel IPv6 multicast routing feature to run.
*
* <p>In Absence of the multicast routing feature, the test class or method will be ignored.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface RequiresIpv6MulticastRouting {}
/**
* Annotates a test class or method requires the simulation Thread device (i.e. ot-cli-ftd) to
* run.
*
* <p>In Absence of the simulation device, the test class or method will be ignored.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface RequiresSimulationThreadDevice {}
@Override
public Statement apply(final Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
if (hasAnnotation(RequiresThreadFeature.class, description)) {
assumeTrue(
"Skipping test because the Thread feature is unavailable",
hasThreadFeature());
}
if (hasAnnotation(RequiresIpv6MulticastRouting.class, description)) {
assumeTrue(
"Skipping test because kernel IPv6 multicast routing is unavailable",
hasIpv6MulticastRouting());
}
if (hasAnnotation(RequiresSimulationThreadDevice.class, description)) {
assumeTrue(
"Skipping test because simulation Thread device is unavailable",
hasSimulationThreadDevice());
}
base.evaluate();
}
};
}
/** Returns {@code true} if a test method or the test class is annotated with annotation. */
private <T extends Annotation> boolean hasAnnotation(
Class<T> annotationClass, Description description) {
// Method annotation
boolean hasAnnotation = description.getAnnotation(annotationClass) != null;
// Class annotation
Class<?> clazz = description.getTestClass();
while (!hasAnnotation && clazz != Object.class) {
hasAnnotation |= clazz.getAnnotation(annotationClass) != null;
clazz = clazz.getSuperclass();
}
return hasAnnotation;
}
/** Returns {@code true} if this device has the Thread feature supported. */
private static boolean hasThreadFeature() {
final Context context = ApplicationProvider.getApplicationContext();
return context.getSystemService(ThreadNetworkManager.class) != null;
}
/**
* Returns {@code true} if this device has the kernel IPv6 multicast routing feature enabled.
*/
private static boolean hasIpv6MulticastRouting() {
// The kernel IPv6 multicast routing (i.e. IPV6_MROUTE) is enabled on kernel version
// android14-5.15.0 and later
return isKernelVersionAtLeast(KERNEL_VERSION_MULTICAST_ROUTING_SUPPORTED)
&& isKernelAndroidVersionAtLeast(
KERNEL_ANDROID_VERSION_MULTICAST_ROUTING_SUPPORTED);
}
/**
* Returns {@code true} if the android version in the kernel version of this device is equal to
* or larger than the given {@code minVersion}.
*/
private static boolean isKernelAndroidVersionAtLeast(int minVersion) {
final String osRelease = VintfRuntimeInfo.getOsRelease();
final Pattern pattern = Pattern.compile("android(\\d+)");
Matcher matcher = pattern.matcher(osRelease);
if (matcher.find()) {
int version = Integer.parseInt(matcher.group(1));
return (version >= minVersion);
}
return false;
}
/** Returns {@code true} if the simulation Thread device is supported. */
private static boolean hasSimulationThreadDevice() {
// Simulation radio is supported on only Cuttlefish
return SystemProperties.get("ro.product.model").startsWith("Cuttlefish");
}
}