diff --git a/thread/tests/cts/Android.bp b/thread/tests/cts/Android.bp index c1cf0a09eeef9706c6b577b4f9c6535161b81f6f..676eb0e69640ed324c02f647553f8289a0e67434 100644 --- a/thread/tests/cts/Android.bp +++ b/thread/tests/cts/Android.bp @@ -42,7 +42,6 @@ android_test { "guava", "guava-android-testlib", "net-tests-utils", - "ThreadNetworkTestUtils", "truth", ], libs: [ diff --git a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java index 95496568cab9b126c04a11de167f3ff1f8130b5e..3bec36b46b991180b72a17d81e8894cf8bf87cad 100644 --- a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java +++ b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java @@ -17,6 +17,7 @@ package android.net.thread.cts; import static android.Manifest.permission.ACCESS_NETWORK_STATE; +import static android.Manifest.permission.MANAGE_TEST_NETWORKS; import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_CHILD; import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_LEADER; import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_ROUTER; @@ -32,6 +33,7 @@ import static android.net.thread.ThreadNetworkException.ERROR_THREAD_DISABLED; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork; import static com.android.testutils.TestPermissionUtil.runAsShell; import static com.google.common.truth.Truth.assertThat; @@ -46,6 +48,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; import android.content.Context; import android.net.ConnectivityManager; +import android.net.LinkAddress; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; @@ -59,9 +62,7 @@ import android.net.thread.ThreadNetworkController.OperationalDatasetCallback; 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.os.HandlerThread; import android.os.OutcomeReceiver; import androidx.annotation.NonNull; @@ -73,6 +74,7 @@ import com.android.testutils.DevSdkIgnoreRule; import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; import com.android.testutils.DevSdkIgnoreRunner; import com.android.testutils.FunctionalUtils.ThrowingRunnable; +import com.android.testutils.TestNetworkTracker; import org.junit.After; import org.junit.Before; @@ -108,7 +110,7 @@ public class ThreadNetworkControllerTest { private static final int NETWORK_CALLBACK_TIMEOUT_MILLIS = 10 * 1000; private static final int CALLBACK_TIMEOUT_MILLIS = 1_000; private static final int ENABLED_TIMEOUT_MILLIS = 2_000; - private static final int SERVICE_DISCOVERY_TIMEOUT_MILLIS = 30_000; + private static final int SERVICE_DISCOVERY_TIMEOUT_MILLIS = 10 * 1000; private static final String MESHCOP_SERVICE_TYPE = "_meshcop._udp"; private static final String THREAD_NETWORK_PRIVILEGED = "android.permission.THREAD_NETWORK_PRIVILEGED"; @@ -121,8 +123,6 @@ public class ThreadNetworkControllerTest { private NsdManager mNsdManager; private Set<String> mGrantedPermissions; - private HandlerThread mHandlerThread; - private TapTestNetworkTracker mTestNetworkTracker; @Before public void setUp() throws Exception { @@ -141,8 +141,6 @@ public class ThreadNetworkControllerTest { setEnabledAndWait(mController, true); mNsdManager = mContext.getSystemService(NsdManager.class); - mHandlerThread = new HandlerThread(this.getClass().getSimpleName()); - mHandlerThread.start(); } @After @@ -154,7 +152,6 @@ public class ThreadNetworkControllerTest { future.get(LEAVE_TIMEOUT_MILLIS, MILLISECONDS); } dropAllPermissions(); - tearDownTestNetwork(); } @Test @@ -832,7 +829,7 @@ public class ThreadNetworkControllerTest { @Test public void meshcopService_threadEnabledButNotJoined_discoveredButNoNetwork() throws Exception { - setUpTestNetwork(); + TestNetworkTracker testNetwork = setUpTestNetwork(); setEnabledAndWait(mController, true); leaveAndWait(mController); @@ -848,11 +845,13 @@ public class ThreadNetworkControllerTest { assertThat(txtMap.get("rv")).isNotNull(); assertThat(txtMap.get("tv")).isNotNull(); assertThat(txtMap.get("sb")).isNotNull(); + + tearDownTestNetwork(testNetwork); } @Test public void meshcopService_joinedNetwork_discoveredHasNetwork() throws Exception { - setUpTestNetwork(); + TestNetworkTracker testNetwork = setUpTestNetwork(); String networkName = "TestNet" + new Random().nextInt(10_000); joinRandomizedDatasetAndWait(mController, networkName); @@ -873,26 +872,27 @@ public class ThreadNetworkControllerTest { assertThat(txtMap.get("tv")).isNotNull(); assertThat(txtMap.get("sb")).isNotNull(); assertThat(txtMap.get("id").length).isEqualTo(16); + + tearDownTestNetwork(testNetwork); } @Test public void meshcopService_threadDisabled_notDiscovered() throws Exception { - setUpTestNetwork(); + TestNetworkTracker testNetwork = setUpTestNetwork(); CompletableFuture<NsdServiceInfo> serviceLostFuture = new CompletableFuture<>(); NsdManager.DiscoveryListener listener = discoverForServiceLost(MESHCOP_SERVICE_TYPE, serviceLostFuture); setEnabledAndWait(mController, false); + try { - serviceLostFuture.get(SERVICE_DISCOVERY_TIMEOUT_MILLIS, MILLISECONDS); - } catch (InterruptedException | ExecutionException | TimeoutException ignored) { - // It's fine if the service lost event didn't show up. The service may not ever be - // advertised. + serviceLostFuture.get(10_000, MILLISECONDS); } finally { mNsdManager.stopServiceDiscovery(listener); } - assertThrows(TimeoutException.class, () -> discoverService(MESHCOP_SERVICE_TYPE)); + + tearDownTestNetwork(testNetwork); } private static void dropAllPermissions() { @@ -1163,17 +1163,14 @@ public class ThreadNetworkControllerTest { } } - private void setUpTestNetwork() { - assertThat(mTestNetworkTracker).isNull(); - mTestNetworkTracker = new TapTestNetworkTracker(mContext, mHandlerThread.getLooper()); + TestNetworkTracker setUpTestNetwork() { + return runAsShell( + MANAGE_TEST_NETWORKS, + () -> initTestNetwork(mContext, new LinkAddress("2001:db8:123::/64"), 10_000)); } - private void tearDownTestNetwork() throws InterruptedException { - if (mTestNetworkTracker != null) { - mTestNetworkTracker.tearDown(); - } - mHandlerThread.quitSafely(); - mHandlerThread.join(); + void tearDownTestNetwork(TestNetworkTracker testNetwork) { + runAsShell(MANAGE_TEST_NETWORKS, () -> testNetwork.teardown()); } private static class DefaultDiscoveryListener implements NsdManager.DiscoveryListener { diff --git a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java index 60a5f2b618f5090361f1691a2230421d87d5f89a..4948c22c3defa5342b9fc3c8568f38da22138377 100644 --- a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java +++ b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java @@ -16,6 +16,7 @@ package com.android.server.thread; +import static android.Manifest.permission.ACCESS_NETWORK_STATE; import static android.net.thread.ActiveOperationalDataset.CHANNEL_PAGE_24_GHZ; import static android.net.thread.ThreadNetworkController.STATE_DISABLED; import static android.net.thread.ThreadNetworkController.STATE_ENABLED; diff --git a/thread/tests/utils/Android.bp b/thread/tests/utils/Android.bp deleted file mode 100644 index 24e9bb9c8fd8d0ca8e722037ba4b7134da5f32e2..0000000000000000000000000000000000000000 --- a/thread/tests/utils/Android.bp +++ /dev/null @@ -1,37 +0,0 @@ -// -// Copyright (C) 2023 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_team: "trendy_team_fwk_thread_network", - default_applicable_licenses: ["Android-Apache-2.0"], -} - -java_library { - name: "ThreadNetworkTestUtils", - min_sdk_version: "30", - static_libs: [ - "compatibility-device-util-axt", - "net-tests-utils", - "net-utils-device-common", - "net-utils-device-common-bpf", - ], - srcs: [ - "src/**/*.java", - ], - defaults: [ - "framework-connectivity-test-defaults", - ], -} diff --git a/thread/tests/utils/src/android/net/thread/utils/TapTestNetworkTracker.java b/thread/tests/utils/src/android/net/thread/utils/TapTestNetworkTracker.java deleted file mode 100644 index 43f177df06d1b10dd73a18cbf303a5087e58bb06..0000000000000000000000000000000000000000 --- a/thread/tests/utils/src/android/net/thread/utils/TapTestNetworkTracker.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * 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 android.Manifest.permission.MANAGE_TEST_NETWORKS; -import static android.net.InetAddresses.parseNumericAddress; -import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; -import static android.net.NetworkCapabilities.TRANSPORT_TEST; -import static android.system.OsConstants.AF_INET6; -import static android.system.OsConstants.IPPROTO_UDP; -import static android.system.OsConstants.SOCK_DGRAM; - -import static com.android.testutils.RecorderCallback.CallbackEntry.LINK_PROPERTIES_CHANGED; -import static com.android.testutils.TestPermissionUtil.runAsShell; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkAgentConfig; -import android.net.NetworkCapabilities; -import android.net.NetworkRequest; -import android.net.TestNetworkInterface; -import android.net.TestNetworkManager; -import android.net.TestNetworkSpecifier; -import android.os.Looper; -import android.system.ErrnoException; -import android.system.Os; - -import com.android.compatibility.common.util.PollingCheck; -import com.android.testutils.TestableNetworkAgent; -import com.android.testutils.TestableNetworkCallback; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.net.InterfaceAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** A class that can create/destroy a test network based on TAP interface. */ -public final class TapTestNetworkTracker { - private static final Duration TIMEOUT = Duration.ofSeconds(2); - private final Context mContext; - private final Looper mLooper; - private TestNetworkInterface mInterface; - private TestableNetworkAgent mAgent; - private final TestableNetworkCallback mNetworkCallback; - private final ConnectivityManager mConnectivityManager; - - /** - * Constructs a {@link TapTestNetworkTracker}. - * - * <p>It creates a TAP interface (e.g. testtap0) and registers a test network using that - * interface. It also requests the test network by {@link ConnectivityManager#requestNetwork} so - * the test network won't be automatically turned down by {@link - * com.android.server.ConnectivityService}. - */ - public TapTestNetworkTracker(Context context, Looper looper) { - mContext = context; - mLooper = looper; - mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); - mNetworkCallback = new TestableNetworkCallback(); - runAsShell(MANAGE_TEST_NETWORKS, this::setUpTestNetwork); - } - - /** Tears down the test network. */ - public void tearDown() { - runAsShell(MANAGE_TEST_NETWORKS, this::tearDownTestNetwork); - } - - /** Returns the interface name of the test network. */ - public String getInterfaceName() { - return mInterface.getInterfaceName(); - } - - private void setUpTestNetwork() throws Exception { - mInterface = mContext.getSystemService(TestNetworkManager.class).createTapInterface(); - - mConnectivityManager.requestNetwork(newNetworkRequest(), mNetworkCallback); - - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(getInterfaceName()); - mAgent = - new TestableNetworkAgent( - mContext, - mLooper, - newNetworkCapabilities(), - lp, - new NetworkAgentConfig.Builder().build()); - final Network network = mAgent.register(); - mAgent.markConnected(); - - PollingCheck.check( - "No usable address on interface", - TIMEOUT.toMillis(), - () -> hasUsableAddress(network, getInterfaceName())); - - lp.setLinkAddresses(makeLinkAddresses()); - mAgent.sendLinkProperties(lp); - mNetworkCallback.eventuallyExpect( - LINK_PROPERTIES_CHANGED, - TIMEOUT.toMillis(), - l -> !l.getLp().getAddresses().isEmpty()); - } - - private void tearDownTestNetwork() throws IOException { - mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); - mAgent.unregister(); - mInterface.getFileDescriptor().close(); - mAgent.waitForIdle(TIMEOUT.toMillis()); - } - - private NetworkRequest newNetworkRequest() { - return new NetworkRequest.Builder() - .removeCapability(NET_CAPABILITY_TRUSTED) - .addTransportType(TRANSPORT_TEST) - .setNetworkSpecifier(new TestNetworkSpecifier(getInterfaceName())) - .build(); - } - - private NetworkCapabilities newNetworkCapabilities() { - return new NetworkCapabilities() - .removeCapability(NET_CAPABILITY_TRUSTED) - .addTransportType(TRANSPORT_TEST) - .setNetworkSpecifier(new TestNetworkSpecifier(getInterfaceName())); - } - - private List<LinkAddress> makeLinkAddresses() { - List<LinkAddress> linkAddresses = new ArrayList<>(); - List<InterfaceAddress> interfaceAddresses = Collections.emptyList(); - - try { - interfaceAddresses = - NetworkInterface.getByName(getInterfaceName()).getInterfaceAddresses(); - } catch (SocketException ignored) { - // Ignore failures when getting the addresses. - } - - for (InterfaceAddress address : interfaceAddresses) { - linkAddresses.add( - new LinkAddress(address.getAddress(), address.getNetworkPrefixLength())); - } - - return linkAddresses; - } - - private static boolean hasUsableAddress(Network network, String interfaceName) { - try { - if (NetworkInterface.getByName(interfaceName).getInterfaceAddresses().isEmpty()) { - return false; - } - } catch (SocketException e) { - return false; - } - // Check if the link-local address can be used. Address flags are not available without - // elevated permissions, so check that bindSocket works. - try { - FileDescriptor sock = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); - network.bindSocket(sock); - Os.connect(sock, parseNumericAddress("ff02::fb%" + interfaceName), 12345); - Os.close(sock); - } catch (ErrnoException | IOException e) { - return false; - } - return true; - } -}