[Thread] fix ServiceDiscoveryTest

The test was flaky because we didn't give enough time for SRP
registration to complete.

The waiting should be counted as started when the SRP server is available).
Previously we started the waiting before the availability of SRP server
so the counted waiting time may be longer than expected which causes
the wait to timeout and fail the test case.

This CL also extracts some common utils from integration test cases to
the IntegrationTestUtils class.

Bug: 353836718
Test: Locally passed 50 times in a row

Change-Id: I2f648a21d57025b082296b4e562c46432ab04ff8
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 22e7a98..11c4819 100644
--- a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
@@ -45,6 +45,7 @@
 
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.fail;
+
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 
 import android.content.Context;
diff --git a/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java b/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
index 8c63d37..b6d9aa3 100644
--- a/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
+++ b/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
@@ -17,12 +17,13 @@
 package android.net.thread;
 
 import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
-import static android.net.thread.utils.IntegrationTestUtils.JOIN_TIMEOUT;
+import static android.net.thread.utils.IntegrationTestUtils.DEFAULT_DATASET;
 import static android.net.thread.utils.IntegrationTestUtils.getIpv6LinkAddresses;
 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.isToIpv6Destination;
+import static android.net.thread.utils.IntegrationTestUtils.joinNetworkAndWaitForOmr;
 import static android.net.thread.utils.IntegrationTestUtils.newPacketReader;
 import static android.net.thread.utils.IntegrationTestUtils.pollForPacket;
 import static android.net.thread.utils.IntegrationTestUtils.sendUdpMessage;
@@ -33,7 +34,6 @@
 import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
 import static com.android.testutils.TestPermissionUtil.runAsShell;
 
-import static com.google.common.io.BaseEncoding.base16;
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
@@ -95,17 +95,6 @@
     private static final Inet6Address GROUP_ADDR_SCOPE_3 =
             (Inet6Address) InetAddresses.parseNumericAddress("ff03::1234");
 
-    // A valid Thread Active Operational Dataset generated from OpenThread CLI "dataset init new".
-    private static final byte[] DEFAULT_DATASET_TLVS =
-            base16().decode(
-                            "0E080000000000010000000300001335060004001FFFE002"
-                                    + "08ACC214689BC40BDF0708FD64DB1225F47E0B0510F26B31"
-                                    + "53760F519A63BAFDDFFC80D2AF030F4F70656E5468726561"
-                                    + "642D643961300102D9A00410A245479C836D551B9CA557F7"
-                                    + "B9D351B40C0402A0FFF8");
-    private static final ActiveOperationalDataset DEFAULT_DATASET =
-            ActiveOperationalDataset.fromThreadTlvs(DEFAULT_DATASET_TLVS);
-
     @Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
 
     private final Context mContext = ApplicationProvider.getApplicationContext();
@@ -171,7 +160,7 @@
          */
 
         FullThreadDevice ftd = mFtds.get(0);
-        startFtdChild(ftd);
+        joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
 
         mInfraDevice.sendEchoRequest(ftd.getOmrAddress());
 
@@ -193,7 +182,7 @@
 
         startInfraDeviceAndWaitForOnLinkAddr();
         FullThreadDevice ftd = mFtds.get(0);
-        startFtdChild(ftd);
+        joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
 
         mInfraDevice.sendEchoRequest(ftd.getOmrAddress());
 
@@ -213,7 +202,7 @@
          */
 
         FullThreadDevice ftd = mFtds.get(0);
-        startFtdChild(ftd);
+        joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
         Inet6Address ftdOmr = ftd.getOmrAddress();
         // Create a new infra network and let Thread prefer it
         TestNetworkTracker oldInfraNetworkTracker = mInfraNetworkTracker;
@@ -243,7 +232,7 @@
          */
 
         FullThreadDevice ftd = mFtds.get(0);
-        startFtdChild(ftd);
+        joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
         Inet6Address ftdOmr = requireNonNull(ftd.getOmrAddress());
         Inet6Address ftdMlEid = requireNonNull(ftd.getMlEid());
 
@@ -285,7 +274,7 @@
          */
 
         FullThreadDevice ftd = mFtds.get(0);
-        startFtdChild(ftd);
+        joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
 
         ftd.subscribeMulticastAddress(GROUP_ADDR_SCOPE_5);
 
@@ -307,7 +296,7 @@
          */
 
         FullThreadDevice ftd = mFtds.get(0);
-        startFtdChild(ftd);
+        joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
 
         ftd.subscribeMulticastAddress(GROUP_ADDR_SCOPE_3);
 
@@ -328,7 +317,7 @@
          */
 
         FullThreadDevice ftd = mFtds.get(0);
-        startFtdChild(ftd);
+        joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
         subscribeMulticastAddressAndWait(ftd, GROUP_ADDR_SCOPE_5);
 
         mInfraDevice.sendEchoRequest(GROUP_ADDR_SCOPE_5);
@@ -360,7 +349,7 @@
          */
 
         FullThreadDevice ftd = mFtds.get(0);
-        startFtdChild(ftd);
+        joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
         ftd.subscribeMulticastAddress(GROUP_ADDR_SCOPE_3);
 
         mInfraDevice.sendEchoRequest(GROUP_ADDR_SCOPE_3);
@@ -382,7 +371,7 @@
          */
 
         FullThreadDevice ftd = mFtds.get(0);
-        startFtdChild(ftd);
+        joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
 
         mInfraDevice.sendEchoRequest(GROUP_ADDR_SCOPE_4);
 
@@ -407,11 +396,11 @@
          */
 
         FullThreadDevice ftd1 = mFtds.get(0);
-        startFtdChild(ftd1);
+        joinNetworkAndWaitForOmr(ftd1, DEFAULT_DATASET);
         subscribeMulticastAddressAndWait(ftd1, GROUP_ADDR_SCOPE_5);
 
         FullThreadDevice ftd2 = mFtds.get(1);
-        startFtdChild(ftd2);
+        joinNetworkAndWaitForOmr(ftd2, DEFAULT_DATASET);
         subscribeMulticastAddressAndWait(ftd2, GROUP_ADDR_SCOPE_4);
 
         mInfraDevice.sendEchoRequest(GROUP_ADDR_SCOPE_5);
@@ -443,11 +432,11 @@
          */
 
         FullThreadDevice ftd1 = mFtds.get(0);
-        startFtdChild(ftd1);
+        joinNetworkAndWaitForOmr(ftd1, DEFAULT_DATASET);
         subscribeMulticastAddressAndWait(ftd1, GROUP_ADDR_SCOPE_5);
 
         FullThreadDevice ftd2 = mFtds.get(1);
-        startFtdChild(ftd2);
+        joinNetworkAndWaitForOmr(ftd2, DEFAULT_DATASET);
         subscribeMulticastAddressAndWait(ftd2, GROUP_ADDR_SCOPE_5);
 
         mInfraDevice.sendEchoRequest(GROUP_ADDR_SCOPE_5);
@@ -473,7 +462,7 @@
          */
 
         FullThreadDevice ftd = mFtds.get(0);
-        startFtdChild(ftd);
+        joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
         Inet6Address ftdOmr = ftd.getOmrAddress();
 
         ftd.ping(GROUP_ADDR_SCOPE_5);
@@ -499,7 +488,7 @@
          */
 
         FullThreadDevice ftd = mFtds.get(0);
-        startFtdChild(ftd);
+        joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
 
         ftd.ping(GROUP_ADDR_SCOPE_3);
 
@@ -521,7 +510,7 @@
          */
 
         FullThreadDevice ftd = mFtds.get(0);
-        startFtdChild(ftd);
+        joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
         Inet6Address ftdLla = ftd.getLinkLocalAddress();
         assertNotNull(ftdLla);
 
@@ -544,7 +533,7 @@
          */
 
         FullThreadDevice ftd = mFtds.get(0);
-        startFtdChild(ftd);
+        joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
         List<Inet6Address> ftdMlas = ftd.getMeshLocalAddresses();
         assertFalse(ftdMlas.isEmpty());
 
@@ -571,7 +560,7 @@
          */
 
         FullThreadDevice ftd = mFtds.get(0);
-        startFtdChild(ftd);
+        joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
         subscribeMulticastAddressAndWait(ftd, GROUP_ADDR_SCOPE_5);
         Inet6Address ftdOmr = ftd.getOmrAddress();
 
@@ -599,7 +588,7 @@
          */
 
         FullThreadDevice ftd = mFtds.get(0);
-        startFtdChild(ftd);
+        joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
         Inet6Address ftdOmr = ftd.getOmrAddress();
 
         // Destroy infra link and re-create
@@ -629,15 +618,6 @@
         runAsShell(MANAGE_TEST_NETWORKS, () -> mInfraNetworkTracker.teardown());
     }
 
-    private void startFtdChild(FullThreadDevice ftd) throws Exception {
-        ftd.factoryReset();
-        ftd.joinNetwork(DEFAULT_DATASET);
-        ftd.waitForStateAnyOf(List.of("router", "child"), JOIN_TIMEOUT);
-        waitFor(() -> ftd.getOmrAddress() != null, Duration.ofSeconds(60));
-        Inet6Address ftdOmr = ftd.getOmrAddress();
-        assertNotNull(ftdOmr);
-    }
-
     private void startInfraDeviceAndWaitForOnLinkAddr() throws Exception {
         mInfraDevice =
                 new InfraNetworkDevice(MacAddress.fromString("1:2:3:4:5:6"), mInfraNetworkReader);
diff --git a/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java b/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
index e10f134..2afca5f 100644
--- a/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
+++ b/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
@@ -18,10 +18,10 @@
 
 import static android.net.InetAddresses.parseNumericAddress;
 import static android.net.nsd.NsdManager.PROTOCOL_DNS_SD;
-import static android.net.thread.utils.IntegrationTestUtils.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.joinNetworkAndWaitForOmr;
 import static android.net.thread.utils.IntegrationTestUtils.resolveService;
 import static android.net.thread.utils.IntegrationTestUtils.resolveServiceUntil;
 import static android.net.thread.utils.IntegrationTestUtils.waitFor;
@@ -168,8 +168,7 @@
 
         // Creates Full Thread Devices (FTD) and let them join the network.
         for (FullThreadDevice ftd : mFtds) {
-            ftd.joinNetwork(DEFAULT_DATASET);
-            ftd.waitForStateAnyOf(List.of("router", "child"), JOIN_TIMEOUT);
+            joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
         }
 
         int randomId = new Random().nextInt(10_000);
@@ -223,8 +222,7 @@
 
         // Creates a Full Thread Devices (FTD) and let it join the network.
         FullThreadDevice ftd = mFtds.get(0);
-        ftd.joinNetwork(DEFAULT_DATASET);
-        ftd.waitForStateAnyOf(List.of("router", "child"), JOIN_TIMEOUT);
+        joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
         ftd.setSrpHostname("my-host");
         ftd.setSrpHostAddresses(List.of((Inet6Address) parseNumericAddress("2001:db8::1")));
         ftd.addSrpService(
@@ -279,8 +277,7 @@
 
         // Creates a Full Thread Devices (FTD) and let it join the network.
         FullThreadDevice ftd = mFtds.get(0);
-        ftd.joinNetwork(DEFAULT_DATASET);
-        ftd.waitForStateAnyOf(List.of("router", "child"), JOIN_TIMEOUT);
+        joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
         ftd.setSrpHostname("my-host");
         ftd.setSrpHostAddresses(
                 List.of(
@@ -346,8 +343,7 @@
         mRegistrationListeners.add(listener);
         for (int i = 0; i < NUM_FTD; ++i) {
             FullThreadDevice ftd = mFtds.get(i);
-            ftd.joinNetwork(DEFAULT_DATASET);
-            ftd.waitForStateAnyOf(List.of("router", "child"), JOIN_TIMEOUT);
+            joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET);
             ftd.setDnsServerAddress(mOtCtl.getMlEid().getHostAddress());
         }
         final ArrayList<NsdServiceInfo> browsedServices = new ArrayList<>();
@@ -409,8 +405,7 @@
          * </pre>
          */
         FullThreadDevice srpClient = mFtds.get(0);
-        srpClient.joinNetwork(DEFAULT_DATASET);
-        srpClient.waitForStateAnyOf(List.of("router", "child"), JOIN_TIMEOUT);
+        joinNetworkAndWaitForOmr(srpClient, DEFAULT_DATASET);
         srpClient.setSrpHostname("my-host");
         srpClient.setSrpHostAddresses(List.of((Inet6Address) parseNumericAddress("2001::1")));
         srpClient.addSrpService(
@@ -421,8 +416,7 @@
                 Map.of("key1", bytes(0x01, 0x02), "key2", bytes(0x03)));
 
         FullThreadDevice dnsClient = mFtds.get(1);
-        dnsClient.joinNetwork(DEFAULT_DATASET);
-        dnsClient.waitForStateAnyOf(List.of("router", "child"), JOIN_TIMEOUT);
+        joinNetworkAndWaitForOmr(dnsClient, DEFAULT_DATASET);
         dnsClient.setDnsServerAddress(mOtCtl.getMlEid().getHostAddress());
 
         NsdServiceInfo browsedService = dnsClient.browseService("_test._udp.default.service.arpa.");
diff --git a/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java b/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
index c0a8eea..8440bbc 100644
--- a/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
+++ b/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
@@ -282,6 +282,7 @@
         for (String subtype : subtypes) {
             fullServiceType.append(",").append(subtype);
         }
+        waitForSrpServer();
         executeCommand(
                 "srp client service add %s %s %d %d %d %s",
                 serviceName,
@@ -492,6 +493,22 @@
         return -1;
     }
 
+    /** Waits for an SRP server to be present in Network Data */
+    private void waitForSrpServer() throws TimeoutException {
+        // CLI output:
+        // > srp client server
+        // [fd64:db12:25f4:7e0b:1bfc:6344:25ac:2dd7]:53538
+        // Done
+        waitFor(
+                () -> {
+                    final String serverAddr = executeCommand("srp client server").get(0);
+                    final int lastColonIndex = serverAddr.lastIndexOf(':');
+                    final int port = Integer.parseInt(serverAddr.substring(lastColonIndex + 1));
+                    return port > 0;
+                },
+                SERVICE_DISCOVERY_TIMEOUT);
+    }
+
     @FormatMethod
     private List<String> executeCommand(String commandFormat, Object... args) {
         return executeCommand(String.format(commandFormat, args));
diff --git a/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java b/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java
index ada46c8..7b0c415 100644
--- a/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java
+++ b/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java
@@ -22,8 +22,11 @@
 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.google.common.io.BaseEncoding.base16;
 import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
 
+import static org.junit.Assert.assertNotNull;
+
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static java.util.concurrent.TimeUnit.SECONDS;
 
@@ -36,6 +39,7 @@
 import android.net.TestNetworkInterface;
 import android.net.nsd.NsdManager;
 import android.net.nsd.NsdServiceInfo;
+import android.net.thread.ActiveOperationalDataset;
 import android.net.thread.ThreadNetworkController;
 import android.os.Build;
 import android.os.Handler;
@@ -84,6 +88,17 @@
     public static final Duration CALLBACK_TIMEOUT = Duration.ofSeconds(1);
     public static final Duration SERVICE_DISCOVERY_TIMEOUT = Duration.ofSeconds(20);
 
+    // A valid Thread Active Operational Dataset generated from OpenThread CLI "dataset init new".
+    private static final byte[] DEFAULT_DATASET_TLVS =
+            base16().decode(
+                            "0E080000000000010000000300001335060004001FFFE002"
+                                    + "08ACC214689BC40BDF0708FD64DB1225F47E0B0510F26B31"
+                                    + "53760F519A63BAFDDFFC80D2AF030F4F70656E5468726561"
+                                    + "642D643961300102D9A00410A245479C836D551B9CA557F7"
+                                    + "B9D351B40C0402A0FFF8");
+    public static final ActiveOperationalDataset DEFAULT_DATASET =
+            ActiveOperationalDataset.fromThreadTlvs(DEFAULT_DATASET_TLVS);
+
     private IntegrationTestUtils() {}
 
     /**
@@ -413,6 +428,22 @@
         return networkFuture.get(timeout.toSeconds(), SECONDS);
     }
 
+    /**
+     * Let the FTD join the specified Thread network and wait for border routing to be available.
+     *
+     * @return the OMR address
+     */
+    public static Inet6Address joinNetworkAndWaitForOmr(
+            FullThreadDevice ftd, ActiveOperationalDataset dataset) throws Exception {
+        ftd.factoryReset();
+        ftd.joinNetwork(dataset);
+        ftd.waitForStateAnyOf(List.of("router", "child"), JOIN_TIMEOUT);
+        waitFor(() -> ftd.getOmrAddress() != null, Duration.ofSeconds(60));
+        Inet6Address ftdOmr = ftd.getOmrAddress();
+        assertNotNull(ftdOmr);
+        return ftdOmr;
+    }
+
     private static class DefaultDiscoveryListener implements NsdManager.DiscoveryListener {
         @Override
         public void onStartDiscoveryFailed(String serviceType, int errorCode) {}