Merge "[Thread] remove queueing mechanism for NsdPublisher" into main
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index e4e6c70..19bcff9 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -74,6 +74,8 @@
"net-utils-device-common-bpf",
"net-utils-device-common-ip",
"net-utils-device-common-netlink",
+ "net-utils-device-common-struct",
+ "net-utils-device-common-struct-base",
"netd-client",
"tetheringstatsprotos",
],
@@ -98,7 +100,6 @@
],
static_libs: [
"NetworkStackApiCurrentShims",
- "net-utils-device-common-struct",
],
apex_available: ["com.android.tethering"],
lint: {
@@ -115,7 +116,6 @@
],
static_libs: [
"NetworkStackApiStableShims",
- "net-utils-device-common-struct",
],
apex_available: ["com.android.tethering"],
lint: {
diff --git a/Tethering/tests/integration/Android.bp b/Tethering/tests/integration/Android.bp
index 07fa733..337d408 100644
--- a/Tethering/tests/integration/Android.bp
+++ b/Tethering/tests/integration/Android.bp
@@ -33,6 +33,7 @@
"net-tests-utils",
"net-utils-device-common",
"net-utils-device-common-bpf",
+ "net-utils-device-common-struct-base",
"testables",
"connectivity-net-module-utils-bpf",
],
diff --git a/Tethering/tests/mts/Android.bp b/Tethering/tests/mts/Android.bp
index a80e49e..c4d5636 100644
--- a/Tethering/tests/mts/Android.bp
+++ b/Tethering/tests/mts/Android.bp
@@ -45,6 +45,7 @@
"junit-params",
"connectivity-net-module-utils-bpf",
"net-utils-device-common-bpf",
+ "net-utils-device-common-struct-base",
],
jni_libs: [
diff --git a/common/Android.bp b/common/Android.bp
index 0048a0a..5fabf41 100644
--- a/common/Android.bp
+++ b/common/Android.bp
@@ -26,7 +26,7 @@
// as the above target may not exist
// depending on the branch
-// The library requires the final artifact to contain net-utils-device-common-struct.
+// The library requires the final artifact to contain net-utils-device-common-struct-base.
java_library {
name: "connectivity-net-module-utils-bpf",
srcs: [
@@ -45,7 +45,7 @@
// For libraries which are statically linked in framework-connectivity, do not
// statically link here because callers of this library might already have a static
// version linked.
- "net-utils-device-common-struct",
+ "net-utils-device-common-struct-base",
],
apex_available: [
"com.android.tethering",
diff --git a/framework/Android.bp b/framework/Android.bp
index 52f2c7c..8787167 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -96,6 +96,7 @@
],
impl_only_static_libs: [
"net-utils-device-common-bpf",
+ "net-utils-device-common-struct-base",
],
libs: [
"androidx.annotation_annotation",
@@ -124,6 +125,7 @@
// Even if the library is included in "impl_only_static_libs" of defaults. This is still
// needed because java_library which doesn't understand "impl_only_static_libs".
"net-utils-device-common-bpf",
+ "net-utils-device-common-struct-base",
],
libs: [
// This cannot be in the defaults clause above because if it were, it would be used
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java b/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java
index 61eb766..0b2003f 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java
@@ -36,6 +36,7 @@
import java.io.IOException;
import java.net.InetSocketAddress;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -134,6 +135,15 @@
mAnnouncer.startSending(info.getServiceId(), announcementInfo,
0L /* initialDelayMs */);
+
+ // Re-announce the services which have the same custom hostname.
+ final String hostname = mRecordRepository.getHostnameForServiceId(info.getServiceId());
+ if (hostname != null) {
+ final List<MdnsAnnouncer.AnnouncementInfo> announcementInfos =
+ new ArrayList<>(mRecordRepository.restartAnnouncingForHostname(hostname));
+ announcementInfos.removeIf((i) -> i.getServiceId() == info.getServiceId());
+ reannounceServices(announcementInfos);
+ }
}
}
@@ -308,17 +318,10 @@
if (hostname != null) {
final List<MdnsProber.ProbingInfo> probingInfos =
mRecordRepository.restartProbingForHostname(hostname);
- for (MdnsProber.ProbingInfo probingInfo : probingInfos) {
- mProber.stop(probingInfo.getServiceId());
- mProber.startProbing(probingInfo);
- }
+ reprobeServices(probingInfos);
final List<MdnsAnnouncer.AnnouncementInfo> announcementInfos =
mRecordRepository.restartAnnouncingForHostname(hostname);
- for (MdnsAnnouncer.AnnouncementInfo announcementInfo : announcementInfos) {
- mAnnouncer.stop(announcementInfo.getServiceId());
- mAnnouncer.startSending(
- announcementInfo.getServiceId(), announcementInfo, 0 /* initialDelayMs */);
- }
+ reannounceServices(announcementInfos);
}
}
@@ -464,4 +467,19 @@
return new byte[0];
}
}
+
+ private void reprobeServices(List<MdnsProber.ProbingInfo> probingInfos) {
+ for (MdnsProber.ProbingInfo probingInfo : probingInfos) {
+ mProber.stop(probingInfo.getServiceId());
+ mProber.startProbing(probingInfo);
+ }
+ }
+
+ private void reannounceServices(List<MdnsAnnouncer.AnnouncementInfo> announcementInfos) {
+ for (MdnsAnnouncer.AnnouncementInfo announcementInfo : announcementInfos) {
+ mAnnouncer.stop(announcementInfo.getServiceId());
+ mAnnouncer.startSending(
+ announcementInfo.getServiceId(), announcementInfo, 0 /* initialDelayMs */);
+ }
+ }
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsPacket.java b/service-t/src/com/android/server/connectivity/mdns/MdnsPacket.java
index 1fabd49..83ecabc 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsPacket.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsPacket.java
@@ -42,7 +42,7 @@
@NonNull
public final List<MdnsRecord> additionalRecords;
- MdnsPacket(int flags,
+ public MdnsPacket(int flags,
@NonNull List<MdnsRecord> questions,
@NonNull List<MdnsRecord> answers,
@NonNull List<MdnsRecord> authorityRecords,
diff --git a/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java b/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java
index d553210..3c11a24 100644
--- a/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java
+++ b/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity.mdns.util;
+import static com.android.server.connectivity.mdns.MdnsConstants.FLAG_TRUNCATED;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.Network;
@@ -23,6 +25,7 @@
import android.os.Handler;
import android.os.SystemClock;
import android.util.ArraySet;
+import android.util.Pair;
import com.android.server.connectivity.mdns.MdnsConstants;
import com.android.server.connectivity.mdns.MdnsPacket;
@@ -30,13 +33,18 @@
import com.android.server.connectivity.mdns.MdnsRecord;
import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
/**
@@ -226,6 +234,100 @@
}
/**
+ * Writes the possible query content of an MdnsPacket into the data buffer.
+ *
+ * <p>This method is specifically for query packets. It writes the question and answer sections
+ * into the data buffer only.
+ *
+ * @param packetCreationBuffer The data buffer for the query content.
+ * @param packet The MdnsPacket to be written into the data buffer.
+ * @return A Pair containing:
+ * 1. The remaining MdnsPacket data that could not fit in the buffer.
+ * 2. The length of the data written to the buffer.
+ */
+ @Nullable
+ private static Pair<MdnsPacket, Integer> writePossibleMdnsPacket(
+ @NonNull byte[] packetCreationBuffer, @NonNull MdnsPacket packet) throws IOException {
+ MdnsPacket remainingPacket;
+ final MdnsPacketWriter writer = new MdnsPacketWriter(packetCreationBuffer);
+ writer.writeUInt16(packet.transactionId); // Transaction ID
+
+ final int flagsPos = writer.getWritePosition();
+ writer.writeUInt16(0); // Flags, written later
+ writer.writeUInt16(0); // questions count, written later
+ writer.writeUInt16(0); // answers count, written later
+ writer.writeUInt16(0); // authority entries count, empty session for query
+ writer.writeUInt16(0); // additional records count, empty session for query
+
+ int writtenQuestions = 0;
+ int writtenAnswers = 0;
+ int lastValidPos = writer.getWritePosition();
+ try {
+ for (MdnsRecord record : packet.questions) {
+ // Questions do not have TTL or data
+ record.writeHeaderFields(writer);
+ writtenQuestions++;
+ lastValidPos = writer.getWritePosition();
+ }
+ for (MdnsRecord record : packet.answers) {
+ record.write(writer, 0L);
+ writtenAnswers++;
+ lastValidPos = writer.getWritePosition();
+ }
+ remainingPacket = null;
+ } catch (IOException e) {
+ // Went over the packet limit; truncate
+ if (writtenQuestions == 0 && writtenAnswers == 0) {
+ // No space to write even one record: just throw (as subclass of IOException)
+ throw e;
+ }
+
+ // Set the last valid position as the final position (not as a rewind)
+ writer.rewind(lastValidPos);
+ writer.clearRewind();
+
+ remainingPacket = new MdnsPacket(packet.flags,
+ packet.questions.subList(
+ writtenQuestions, packet.questions.size()),
+ packet.answers.subList(
+ writtenAnswers, packet.answers.size()),
+ Collections.emptyList(), /* authorityRecords */
+ Collections.emptyList() /* additionalRecords */);
+ }
+
+ final int len = writer.getWritePosition();
+ writer.rewind(flagsPos);
+ writer.writeUInt16(packet.flags | (remainingPacket == null ? 0 : FLAG_TRUNCATED));
+ writer.writeUInt16(writtenQuestions);
+ writer.writeUInt16(writtenAnswers);
+ writer.unrewind();
+
+ return Pair.create(remainingPacket, len);
+ }
+
+ /**
+ * Create Datagram packets from given MdnsPacket and InetSocketAddress.
+ *
+ * <p> If the MdnsPacket is too large for a single DatagramPacket, it will be split into
+ * multiple DatagramPackets.
+ */
+ public static List<DatagramPacket> createQueryDatagramPackets(
+ @NonNull byte[] packetCreationBuffer, @NonNull MdnsPacket packet,
+ @NonNull InetSocketAddress destination) throws IOException {
+ final List<DatagramPacket> datagramPackets = new ArrayList<>();
+ MdnsPacket remainingPacket = packet;
+ while (remainingPacket != null) {
+ final Pair<MdnsPacket, Integer> result =
+ writePossibleMdnsPacket(packetCreationBuffer, remainingPacket);
+ remainingPacket = result.first;
+ final int len = result.second;
+ final byte[] outBuffer = Arrays.copyOfRange(packetCreationBuffer, 0, len);
+ datagramPackets.add(new DatagramPacket(outBuffer, 0, outBuffer.length, destination));
+ }
+ return datagramPackets;
+ }
+
+ /**
* Checks if the MdnsRecord needs to be renewed or not.
*
* <p>As per RFC6762 7.1 no need to query if remaining TTL is more than half the original one,
diff --git a/service/Android.bp b/service/Android.bp
index c35c4f8..1d74efc 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -199,7 +199,9 @@
"PlatformProperties",
"service-connectivity-protos",
"service-connectivity-stats-protos",
- "net-utils-multicast-forwarding-structs",
+ // The required dependency net-utils-device-common-struct-base is in the classpath via
+ // framework-connectivity
+ "net-utils-device-common-struct",
],
apex_available: [
"com.android.tethering",
diff --git a/staticlibs/Android.bp b/staticlibs/Android.bp
index f7b42a6..ede6d3f 100644
--- a/staticlibs/Android.bp
+++ b/staticlibs/Android.bp
@@ -124,6 +124,8 @@
],
}
+// The net-utils-device-common-bpf library requires the callers to contain
+// net-utils-device-common-struct-base.
java_library {
name: "net-utils-device-common-bpf",
srcs: [
@@ -133,9 +135,7 @@
"device/com/android/net/module/util/BpfUtils.java",
"device/com/android/net/module/util/IBpfMap.java",
"device/com/android/net/module/util/JniUtil.java",
- "device/com/android/net/module/util/Struct.java",
"device/com/android/net/module/util/TcUtils.java",
- "framework/com/android/net/module/util/HexDump.java",
],
sdk_version: "module_current",
min_sdk_version: "30",
@@ -146,6 +146,7 @@
libs: [
"androidx.annotation_annotation",
"framework-connectivity.stubs.module_lib",
+ "net-utils-device-common-struct-base",
],
apex_available: [
"com.android.tethering",
@@ -158,12 +159,9 @@
}
java_library {
- name: "net-utils-device-common-struct",
+ name: "net-utils-device-common-struct-base",
srcs: [
- "device/com/android/net/module/util/Ipv6Utils.java",
- "device/com/android/net/module/util/PacketBuilder.java",
"device/com/android/net/module/util/Struct.java",
- "device/com/android/net/module/util/structs/*.java",
],
sdk_version: "module_current",
min_sdk_version: "30",
@@ -176,6 +174,7 @@
],
libs: [
"androidx.annotation_annotation",
+ "framework-annotations-lib", // Required by InetAddressUtils.java
"framework-connectivity.stubs.module_lib",
],
apex_available: [
@@ -188,26 +187,30 @@
},
}
-// The net-utils-multicast-forwarding-structs library requires the callers to
-// contain net-utils-device-common-bpf.
+// The net-utils-device-common-struct library requires the callers to contain
+// net-utils-device-common-struct-base.
java_library {
- name: "net-utils-multicast-forwarding-structs",
+ name: "net-utils-device-common-struct",
srcs: [
- "device/com/android/net/module/util/structs/StructMf6cctl.java",
- "device/com/android/net/module/util/structs/StructMif6ctl.java",
- "device/com/android/net/module/util/structs/StructMrt6Msg.java",
+ "device/com/android/net/module/util/Ipv6Utils.java",
+ "device/com/android/net/module/util/PacketBuilder.java",
+ "device/com/android/net/module/util/structs/*.java",
],
sdk_version: "module_current",
min_sdk_version: "30",
visibility: [
"//packages/modules/Connectivity:__subpackages__",
+ "//packages/modules/NetworkStack:__subpackages__",
],
libs: [
- // Only Struct.java is needed from "net-utils-device-common-bpf"
- "net-utils-device-common-bpf",
+ "androidx.annotation_annotation",
+ "framework-annotations-lib", // Required by IpUtils.java
+ "framework-connectivity.stubs.module_lib",
+ "net-utils-device-common-struct-base",
],
apex_available: [
"com.android.tethering",
+ "//apex_available:platform",
],
lint: {
strict_updatability_linting: true,
@@ -216,7 +219,7 @@
}
// The net-utils-device-common-netlink library requires the callers to contain
-// net-utils-device-common-struct.
+// net-utils-device-common-struct and net-utils-device-common-struct-base.
java_library {
name: "net-utils-device-common-netlink",
srcs: [
@@ -235,6 +238,7 @@
// statically link here because callers of this library might already have a static
// version linked.
"net-utils-device-common-struct",
+ "net-utils-device-common-struct-base",
],
apex_available: [
"com.android.tethering",
@@ -247,7 +251,7 @@
}
// The net-utils-device-common-ip library requires the callers to contain
-// net-utils-device-common-struct.
+// net-utils-device-common-struct and net-utils-device-common-struct-base.
java_library {
// TODO : this target should probably be folded into net-utils-device-common
name: "net-utils-device-common-ip",
diff --git a/staticlibs/tests/unit/Android.bp b/staticlibs/tests/unit/Android.bp
index 4c226cc..fa466f8 100644
--- a/staticlibs/tests/unit/Android.bp
+++ b/staticlibs/tests/unit/Android.bp
@@ -25,6 +25,7 @@
"net-utils-device-common-async",
"net-utils-device-common-bpf",
"net-utils-device-common-ip",
+ "net-utils-device-common-struct-base",
"net-utils-device-common-wear",
],
libs: [
diff --git a/staticlibs/testutils/Android.bp b/staticlibs/testutils/Android.bp
index a8e5a69..9124ac0 100644
--- a/staticlibs/testutils/Android.bp
+++ b/staticlibs/testutils/Android.bp
@@ -40,6 +40,7 @@
"net-utils-device-common-async",
"net-utils-device-common-netlink",
"net-utils-device-common-struct",
+ "net-utils-device-common-struct-base",
"net-utils-device-common-wear",
"modules-utils-build_system",
],
diff --git a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
deleted file mode 100644
index e92c906..0000000
--- a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
+++ /dev/null
@@ -1,81 +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.cts
-
-import android.content.pm.PackageManager.FEATURE_WIFI
-import android.net.ConnectivityManager
-import android.net.NetworkCapabilities
-import android.net.NetworkRequest
-import android.os.Build
-import android.system.OsConstants
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.compatibility.common.util.PropertyUtil.isVendorApiLevelNewerThan
-import com.android.compatibility.common.util.SystemUtil
-import com.android.testutils.DevSdkIgnoreRule
-import com.android.testutils.DevSdkIgnoreRunner
-import com.android.testutils.NetworkStackModuleTest
-import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
-import com.android.testutils.TestableNetworkCallback
-import com.google.common.truth.Truth.assertThat
-import kotlin.test.assertEquals
-import kotlin.test.assertNotNull
-import org.junit.Assume.assumeTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-private const val TIMEOUT_MS = 2000L
-
-@RunWith(DevSdkIgnoreRunner::class)
-@NetworkStackModuleTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
-class ApfIntegrationTest {
- private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
- private val cm by lazy { context.getSystemService(ConnectivityManager::class.java)!! }
- private val pm by lazy { context.packageManager }
- private lateinit var wifiIfaceName: String
- @Before
- fun setUp() {
- assumeTrue(pm.hasSystemFeature(FEATURE_WIFI))
- assumeTrue(isVendorApiLevelNewerThan(Build.VERSION_CODES.TIRAMISU))
- val cb = TestableNetworkCallback()
- cm.requestNetwork(
- NetworkRequest.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .build(),
- cb
- )
- cb.eventuallyExpect<LinkPropertiesChanged>(TIMEOUT_MS) {
- wifiIfaceName = assertNotNull(it.lp.interfaceName)
- true
- }
- assertNotNull(wifiIfaceName)
- }
-
- @Test
- fun testGetApfCapabilities() {
- val capabilities = SystemUtil
- .runShellCommand("cmd network_stack apf $wifiIfaceName capabilities").trim()
- val (version, maxLen, packetFormat) = capabilities.split(",").map { it.toInt() }
- assertEquals(4, version)
- assertThat(maxLen).isAtLeast(1024)
- if (isVendorApiLevelNewerThan(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)) {
- assertThat(maxLen).isAtLeast(2000)
- }
- assertEquals(OsConstants.ARPHRD_ETHER, packetFormat)
- }
-}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt
index 7ac7bee..629ac67 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt
@@ -47,7 +47,6 @@
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.anyString
import org.mockito.Mockito.argThat
-import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.eq
@@ -55,7 +54,6 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.inOrder
private const val LOG_TAG = "testlogtag"
@@ -234,19 +232,49 @@
addServiceAndFinishProbing(TEST_SERVICE_ID_2, TEST_SERVICE_1_CUSTOM_HOST)
doReturn("MyTestHost")
.`when`(repository).getHostnameForServiceId(TEST_SERVICE_ID_1)
- doReturn(TEST_SERVICE_ID_2).`when`(announcementInfo).serviceId
doReturn(listOf(announcementInfo))
.`when`(repository).restartAnnouncingForHostname("MyTestHost")
- clearInvocations(announcer)
+ val inOrder = inOrder(prober, announcer)
// Remove the custom host: the custom host's announcement is stopped and the probed services
// which use that hostname are re-announced.
advertiser.removeService(TEST_SERVICE_ID_1)
- verify(prober).stop(TEST_SERVICE_ID_1)
- verify(announcer, atLeastOnce()).stop(TEST_SERVICE_ID_1)
- verify(announcer).stop(TEST_SERVICE_ID_2)
- verify(announcer).startSending(TEST_SERVICE_ID_2, announcementInfo, 0L /* initialDelayMs */)
+ inOrder.verify(prober).stop(TEST_SERVICE_ID_1)
+ inOrder.verify(announcer).stop(TEST_SERVICE_ID_1)
+ inOrder.verify(announcer).stop(TEST_SERVICE_ID_2)
+ inOrder.verify(announcer).startSending(TEST_SERVICE_ID_2, announcementInfo, 0L /* initialDelayMs */)
+ }
+
+ @Test
+ fun testAddMoreAddressesForCustomHost_restartAnnouncingForProbedServices() {
+ val customHost = NsdServiceInfo().apply {
+ hostname = "MyTestHost"
+ hostAddresses = listOf(
+ parseNumericAddress("192.0.2.23"),
+ parseNumericAddress("2001:db8::1"))
+ }
+ doReturn("MyTestHost")
+ .`when`(repository).getHostnameForServiceId(TEST_SERVICE_ID_1)
+ doReturn("MyTestHost")
+ .`when`(repository).getHostnameForServiceId(TEST_SERVICE_ID_2)
+ val announcementInfo1 =
+ addServiceAndFinishProbing(TEST_SERVICE_ID_1, TEST_SERVICE_1_CUSTOM_HOST)
+
+ val probingInfo2 = addServiceAndStartProbing(TEST_SERVICE_ID_2, customHost)
+ val announcementInfo2 = AnnouncementInfo(TEST_SERVICE_ID_2, emptyList(), emptyList())
+ doReturn(announcementInfo2).`when`(repository).onProbingSucceeded(probingInfo2)
+ doReturn(listOf(announcementInfo1, announcementInfo2))
+ .`when`(repository).restartAnnouncingForHostname("MyTestHost")
+ probeCb.onFinished(probingInfo2)
+
+ val inOrder = inOrder(prober, announcer)
+
+ inOrder.verify(announcer)
+ .startSending(TEST_SERVICE_ID_2, announcementInfo2, 0L /* initialDelayMs */)
+ inOrder.verify(announcer).stop(TEST_SERVICE_ID_1)
+ inOrder.verify(announcer)
+ .startSending(TEST_SERVICE_ID_1, announcementInfo1, 0L /* initialDelayMs */)
}
@Test
@@ -489,8 +517,8 @@
verify(prober, never()).startProbing(any())
}
- private fun addServiceAndFinishProbing(serviceId: Int, serviceInfo: NsdServiceInfo):
- AnnouncementInfo {
+ private fun addServiceAndStartProbing(serviceId: Int, serviceInfo: NsdServiceInfo):
+ ProbingInfo {
val testProbingInfo = mock(ProbingInfo::class.java)
doReturn(serviceId).`when`(testProbingInfo).serviceId
doReturn(testProbingInfo).`when`(repository).setServiceProbing(serviceId)
@@ -499,8 +527,15 @@
verify(repository).addService(serviceId, serviceInfo, null /* ttl */)
verify(prober).startProbing(testProbingInfo)
+ return testProbingInfo
+ }
+
+ private fun addServiceAndFinishProbing(serviceId: Int, serviceInfo: NsdServiceInfo):
+ AnnouncementInfo {
+ val testProbingInfo = addServiceAndStartProbing(serviceId, serviceInfo)
+
// Simulate probing success: continues to announcing
- val testAnnouncementInfo = mock(AnnouncementInfo::class.java)
+ val testAnnouncementInfo = AnnouncementInfo(serviceId, emptyList(), emptyList())
doReturn(testAnnouncementInfo).`when`(repository).onProbingSucceeded(testProbingInfo)
probeCb.onFinished(testProbingInfo)
return testAnnouncementInfo
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/util/MdnsUtilsTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/util/MdnsUtilsTest.kt
index f705bcb..b1a7233 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/util/MdnsUtilsTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/util/MdnsUtilsTest.kt
@@ -17,6 +17,13 @@
package com.android.server.connectivity.mdns.util
import android.os.Build
+import com.android.server.connectivity.mdns.MdnsConstants
+import com.android.server.connectivity.mdns.MdnsConstants.FLAG_TRUNCATED
+import com.android.server.connectivity.mdns.MdnsPacket
+import com.android.server.connectivity.mdns.MdnsPacketReader
+import com.android.server.connectivity.mdns.MdnsPointerRecord
+import com.android.server.connectivity.mdns.MdnsRecord
+import com.android.server.connectivity.mdns.util.MdnsUtils.createQueryDatagramPackets
import com.android.server.connectivity.mdns.util.MdnsUtils.equalsDnsLabelIgnoreDnsCase
import com.android.server.connectivity.mdns.util.MdnsUtils.equalsIgnoreDnsCase
import com.android.server.connectivity.mdns.util.MdnsUtils.toDnsLabelsLowerCase
@@ -24,6 +31,8 @@
import com.android.server.connectivity.mdns.util.MdnsUtils.truncateServiceName
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
+import java.net.DatagramPacket
+import kotlin.test.assertContentEquals
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
@@ -102,4 +111,67 @@
arrayOf("a", "_other", "_type", "_tcp", "local"),
arrayOf("a", "_SUB", "_type", "_TCP", "local")))
}
+
+ @Test
+ fun testCreateQueryDatagramPackets() {
+ // Question data bytes:
+ // Name label(17)(duplicated labels) + PTR type(2) + cacheFlush(2) = 21
+ //
+ // Known answers data bytes:
+ // Name label(17)(duplicated labels) + PTR type(2) + cacheFlush(2) + receiptTimeMillis(4)
+ // + Data length(2) + Pointer data(18)(duplicated labels) = 45
+ val questions = mutableListOf<MdnsRecord>()
+ val knownAnswers = mutableListOf<MdnsRecord>()
+ for (i in 1..100) {
+ questions.add(MdnsPointerRecord(arrayOf("_testservice$i", "_tcp", "local"), false))
+ knownAnswers.add(MdnsPointerRecord(
+ arrayOf("_testservice$i", "_tcp", "local"),
+ 0L,
+ false,
+ 4_500_000L,
+ arrayOf("MyTestService$i", "_testservice$i", "_tcp", "local")
+ ))
+ }
+ // MdnsPacket data bytes:
+ // Questions(21 * 100) + Answers(45 * 100) = 6600 -> at least 5 packets
+ val query = MdnsPacket(
+ MdnsConstants.FLAGS_QUERY,
+ questions as List<MdnsRecord>,
+ knownAnswers as List<MdnsRecord>,
+ emptyList(),
+ emptyList()
+ )
+ // Expect the oversize MdnsPacket to be separated into 5 DatagramPackets.
+ val bufferSize = 1500
+ val packets = createQueryDatagramPackets(
+ ByteArray(bufferSize),
+ query,
+ MdnsConstants.IPV4_SOCKET_ADDR
+ )
+ assertEquals(5, packets.size)
+ assertTrue(packets.all { packet -> packet.length < bufferSize })
+
+ val mdnsPacket = createMdnsPacketFromMultipleDatagramPackets(packets)
+ assertEquals(query.flags, mdnsPacket.flags)
+ assertContentEquals(query.questions, mdnsPacket.questions)
+ assertContentEquals(query.answers, mdnsPacket.answers)
+ }
+
+ private fun createMdnsPacketFromMultipleDatagramPackets(
+ packets: List<DatagramPacket>
+ ): MdnsPacket {
+ var flags = 0
+ val questions = mutableListOf<MdnsRecord>()
+ val answers = mutableListOf<MdnsRecord>()
+ for ((index, packet) in packets.withIndex()) {
+ val mdnsPacket = MdnsPacket.parse(MdnsPacketReader(packet))
+ if (index != packets.size - 1) {
+ assertTrue((mdnsPacket.flags and FLAG_TRUNCATED) == FLAG_TRUNCATED)
+ }
+ flags = mdnsPacket.flags
+ questions.addAll(mdnsPacket.questions)
+ answers.addAll(mdnsPacket.answers)
+ }
+ return MdnsPacket(flags, questions, answers, emptyList(), emptyList())
+ }
}
diff --git a/thread/framework/java/android/net/thread/ActiveOperationalDataset.java b/thread/framework/java/android/net/thread/ActiveOperationalDataset.java
index 4a7d3a7..22457f5 100644
--- a/thread/framework/java/android/net/thread/ActiveOperationalDataset.java
+++ b/thread/framework/java/android/net/thread/ActiveOperationalDataset.java
@@ -18,7 +18,7 @@
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkState;
-import static com.android.net.module.util.HexDump.dumpHexString;
+import static com.android.net.module.util.HexDump.toHexString;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
@@ -610,7 +610,7 @@
sb.append("{networkName=")
.append(getNetworkName())
.append(", extendedPanId=")
- .append(dumpHexString(getExtendedPanId()))
+ .append(toHexString(getExtendedPanId()))
.append(", panId=")
.append(getPanId())
.append(", channel=")
@@ -1109,7 +1109,7 @@
sb.append("{rotation=")
.append(mRotationTimeHours)
.append(", flags=")
- .append(dumpHexString(mFlags))
+ .append(toHexString(mFlags))
.append("}");
return sb.toString();
}
diff --git a/thread/service/Android.bp b/thread/service/Android.bp
index 6e2fac1..a82a499 100644
--- a/thread/service/Android.bp
+++ b/thread/service/Android.bp
@@ -45,6 +45,9 @@
"modules-utils-shell-command-handler",
"net-utils-device-common",
"net-utils-device-common-netlink",
+ // The required dependency net-utils-device-common-struct-base is in the classpath via
+ // framework-connectivity
+ "net-utils-device-common-struct",
"ot-daemon-aidl-java",
],
apex_available: ["com.android.tethering"],
diff --git a/thread/tests/integration/Android.bp b/thread/tests/integration/Android.bp
index 9677ec5..94985b1 100644
--- a/thread/tests/integration/Android.bp
+++ b/thread/tests/integration/Android.bp
@@ -30,6 +30,7 @@
"net-tests-utils",
"net-utils-device-common",
"net-utils-device-common-bpf",
+ "net-utils-device-common-struct-base",
"testables",
"ThreadNetworkTestUtils",
"truth",
diff --git a/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java b/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
index 5fe4325..9b1c338 100644
--- a/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
+++ b/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
@@ -126,11 +126,12 @@
mFtds = new ArrayList<>();
setUpInfraNetwork();
+ mController.setEnabledAndWait(true);
mController.joinAndWait(DEFAULT_DATASET);
// Creates a infra network device.
mInfraNetworkReader = newPacketReader(mInfraNetworkTracker.getTestIface(), mHandler);
- startInfraDevice();
+ startInfraDeviceAndWaitForOnLinkAddr();
// Create Ftds
for (int i = 0; i < NUM_FTD; ++i) {
@@ -185,7 +186,7 @@
* </pre>
*/
- startInfraDevice();
+ startInfraDeviceAndWaitForOnLinkAddr();
FullThreadDevice ftd = mFtds.get(0);
startFtdChild(ftd);
@@ -195,6 +196,36 @@
}
@Test
+ public void unicastRouting_afterInfraNetworkSwitchInfraDevicePingThreadDeviceOmr_replyReceived()
+ throws Exception {
+ /*
+ * <pre>
+ * Topology:
+ * infra network Thread
+ * infra device -------------------- Border Router -------------- Full Thread device
+ * (Cuttlefish)
+ * </pre>
+ */
+
+ FullThreadDevice ftd = mFtds.get(0);
+ startFtdChild(ftd);
+ Inet6Address ftdOmr = ftd.getOmrAddress();
+ // Create a new infra network and let Thread prefer it
+ TestNetworkTracker oldInfraNetworkTracker = mInfraNetworkTracker;
+ try {
+ setUpInfraNetwork();
+ mInfraNetworkReader = newPacketReader(mInfraNetworkTracker.getTestIface(), mHandler);
+ startInfraDeviceAndWaitForOnLinkAddr();
+
+ mInfraDevice.sendEchoRequest(ftd.getOmrAddress());
+
+ assertNotNull(pollForPacketOnInfraNetwork(ICMPV6_ECHO_REPLY_TYPE, ftdOmr));
+ } finally {
+ runAsShell(MANAGE_TEST_NETWORKS, () -> oldInfraNetworkTracker.teardown());
+ }
+ }
+
+ @Test
public void unicastRouting_borderRouterSendsUdpToThreadDevice_datagramReceived()
throws Exception {
/*
@@ -528,7 +559,7 @@
tearDownInfraNetwork();
setUpInfraNetwork();
mInfraNetworkReader = newPacketReader(mInfraNetworkTracker.getTestIface(), mHandler);
- startInfraDevice();
+ startInfraDeviceAndWaitForOnLinkAddr();
mInfraDevice.sendEchoRequest(GROUP_ADDR_SCOPE_5);
@@ -555,7 +586,7 @@
tearDownInfraNetwork();
setUpInfraNetwork();
mInfraNetworkReader = newPacketReader(mInfraNetworkTracker.getTestIface(), mHandler);
- startInfraDevice();
+ startInfraDeviceAndWaitForOnLinkAddr();
ftd.ping(GROUP_ADDR_SCOPE_4);
@@ -587,7 +618,7 @@
assertNotNull(ftdOmr);
}
- private void startInfraDevice() throws Exception {
+ private void startInfraDeviceAndWaitForOnLinkAddr() throws Exception {
mInfraDevice =
new InfraNetworkDevice(MacAddress.fromString("1:2:3:4:5:6"), mInfraNetworkReader);
mInfraDevice.runSlaac(Duration.ofSeconds(60));
diff --git a/thread/tests/utils/Android.bp b/thread/tests/utils/Android.bp
index 24e9bb9..726ec9d 100644
--- a/thread/tests/utils/Android.bp
+++ b/thread/tests/utils/Android.bp
@@ -27,6 +27,7 @@
"net-tests-utils",
"net-utils-device-common",
"net-utils-device-common-bpf",
+ "net-utils-device-common-struct-base",
],
srcs: [
"src/**/*.java",