Merge changes Ie2c35190,I6bd9ceeb,I823c8e94,I1b4868ba into main
* changes:
Clean up TetheringRequest#toString()
Send TetheringRequest to restart Tethering from IpServer
Add hostside CTS tests for SoftApConfig redaction
Include SoftAP config in TetheringEventCallback
diff --git a/bpf/headers/include/bpf/BpfClassic.h b/bpf/headers/include/bpf/BpfClassic.h
index 81be37d..924f7a3 100644
--- a/bpf/headers/include/bpf/BpfClassic.h
+++ b/bpf/headers/include/bpf/BpfClassic.h
@@ -160,6 +160,9 @@
#define BPF_LOAD_NETX_RELATIVE_ICMP_TYPE BPF_LOAD_NETX_RELATIVE_L4_U8(0)
#define BPF_LOAD_NETX_RELATIVE_ICMP_CODE BPF_LOAD_NETX_RELATIVE_L4_U8(1)
+// IGMP start with u8 type
+#define BPF_LOAD_NETX_RELATIVE_IGMP_TYPE BPF_LOAD_NETX_RELATIVE_L4_U8(0)
+
// IPv6 extension headers (HOPOPTS, DSTOPS, FRAG) begin with a u8 nexthdr
#define BPF_LOAD_NETX_RELATIVE_V6EXTHDR_NEXTHDR BPF_LOAD_NETX_RELATIVE_L4_U8(0)
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsQueryScheduler.java b/service-t/src/com/android/server/connectivity/mdns/MdnsQueryScheduler.java
index e52dd2f..356b738 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsQueryScheduler.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsQueryScheduler.java
@@ -107,7 +107,7 @@
final QueryTaskConfig nextRunConfig = currentConfig.getConfigForNextRun(queryMode);
long timeToRun;
if (mLastScheduledQueryTaskArgs == null && !forceEnableBackoff) {
- timeToRun = now + nextRunConfig.delayBeforeTaskWithoutBackoffMs;
+ timeToRun = now + nextRunConfig.getDelayBeforeTaskWithoutBackoff();
} else {
timeToRun = calculateTimeToRun(mLastScheduledQueryTaskArgs,
nextRunConfig, now, minRemainingTtl, lastSentTime, numOfQueriesBeforeBackoff,
@@ -133,7 +133,7 @@
private static long calculateTimeToRun(@Nullable ScheduledQueryTaskArgs taskArgs,
QueryTaskConfig queryTaskConfig, long now, long minRemainingTtl, long lastSentTime,
int numOfQueriesBeforeBackoff, boolean forceEnableBackoff) {
- final long baseDelayInMs = queryTaskConfig.delayBeforeTaskWithoutBackoffMs;
+ final long baseDelayInMs = queryTaskConfig.getDelayBeforeTaskWithoutBackoff();
if (!(forceEnableBackoff
|| queryTaskConfig.shouldUseQueryBackoff(numOfQueriesBeforeBackoff))) {
return lastSentTime + baseDelayInMs;
diff --git a/service-t/src/com/android/server/connectivity/mdns/QueryTaskConfig.java b/service-t/src/com/android/server/connectivity/mdns/QueryTaskConfig.java
index 4e74159..dd4073f 100644
--- a/service-t/src/com/android/server/connectivity/mdns/QueryTaskConfig.java
+++ b/service-t/src/com/android/server/connectivity/mdns/QueryTaskConfig.java
@@ -52,118 +52,124 @@
final int transactionId;
@VisibleForTesting
final boolean expectUnicastResponse;
- private final int queriesPerBurst;
- private final int timeBetweenBurstsInMs;
- private final int burstCounter;
- final long delayBeforeTaskWithoutBackoffMs;
- private final boolean isFirstBurst;
- private final long queryIndex;
+ private final int queryIndex;
+ private final int queryMode;
- QueryTaskConfig(long queryIndex, int transactionId,
- boolean expectUnicastResponse, boolean isFirstBurst, int burstCounter,
- int queriesPerBurst, int timeBetweenBurstsInMs,
- long delayBeforeTaskWithoutBackoffMs) {
+ QueryTaskConfig(int queryMode, int queryIndex, int transactionId,
+ boolean expectUnicastResponse) {
+ this.queryMode = queryMode;
this.transactionId = transactionId;
- this.expectUnicastResponse = expectUnicastResponse;
- this.queriesPerBurst = queriesPerBurst;
- this.timeBetweenBurstsInMs = timeBetweenBurstsInMs;
- this.burstCounter = burstCounter;
- this.delayBeforeTaskWithoutBackoffMs = delayBeforeTaskWithoutBackoffMs;
- this.isFirstBurst = isFirstBurst;
this.queryIndex = queryIndex;
+ this.expectUnicastResponse = expectUnicastResponse;
}
QueryTaskConfig(int queryMode) {
- this.queriesPerBurst = QUERIES_PER_BURST;
- this.burstCounter = 0;
- this.transactionId = 1;
- this.expectUnicastResponse = true;
- this.isFirstBurst = true;
- // Config the scan frequency based on the scan mode.
- if (queryMode == AGGRESSIVE_QUERY_MODE) {
- this.timeBetweenBurstsInMs = INITIAL_AGGRESSIVE_TIME_BETWEEN_BURSTS_MS;
- this.delayBeforeTaskWithoutBackoffMs =
- TIME_BETWEEN_RETRANSMISSION_QUERIES_IN_BURST_MS;
- } else if (queryMode == PASSIVE_QUERY_MODE) {
- // In passive scan mode, sends a single burst of QUERIES_PER_BURST queries, and then
- // in each TIME_BETWEEN_BURSTS interval, sends QUERIES_PER_BURST_PASSIVE_MODE
- // queries.
- this.timeBetweenBurstsInMs = MAX_TIME_BETWEEN_ACTIVE_PASSIVE_BURSTS_MS;
- this.delayBeforeTaskWithoutBackoffMs = TIME_BETWEEN_QUERIES_IN_BURST_MS;
- } else {
- // In active scan mode, sends a burst of QUERIES_PER_BURST queries,
- // TIME_BETWEEN_QUERIES_IN_BURST_MS apart, then waits for the scan interval, and
- // then repeats. The scan interval starts as INITIAL_TIME_BETWEEN_BURSTS_MS and
- // doubles until it maxes out at TIME_BETWEEN_BURSTS_MS.
- this.timeBetweenBurstsInMs = INITIAL_TIME_BETWEEN_BURSTS_MS;
- this.delayBeforeTaskWithoutBackoffMs = TIME_BETWEEN_QUERIES_IN_BURST_MS;
- }
- this.queryIndex = 0;
+ this(queryMode, 0, 1, true);
}
- long getDelayBeforeNextTaskWithoutBackoff(boolean isFirstQueryInBurst,
- boolean isLastQueryInBurst, int queryMode) {
- if (isFirstQueryInBurst && queryMode == AGGRESSIVE_QUERY_MODE) {
- return 0;
+ private static int getBurstIndex(int queryIndex, int queryMode) {
+ if (queryMode == PASSIVE_QUERY_MODE && queryIndex >= QUERIES_PER_BURST) {
+ // In passive mode, after the first burst of QUERIES_PER_BURST queries, subsequent
+ // bursts have QUERIES_PER_BURST_PASSIVE_MODE queries.
+ final int queryIndexAfterFirstBurst = queryIndex - QUERIES_PER_BURST;
+ return 1 + (queryIndexAfterFirstBurst / QUERIES_PER_BURST_PASSIVE_MODE);
+ } else {
+ return queryIndex / QUERIES_PER_BURST;
}
- if (isLastQueryInBurst) {
- return timeBetweenBurstsInMs;
+ }
+
+ private static int getQueryIndexInBurst(int queryIndex, int queryMode) {
+ if (queryMode == PASSIVE_QUERY_MODE && queryIndex >= QUERIES_PER_BURST) {
+ final int queryIndexAfterFirstBurst = queryIndex - QUERIES_PER_BURST;
+ return queryIndexAfterFirstBurst % QUERIES_PER_BURST_PASSIVE_MODE;
+ } else {
+ return queryIndex % QUERIES_PER_BURST;
+ }
+ }
+
+ private static boolean isFirstBurst(int queryIndex, int queryMode) {
+ return getBurstIndex(queryIndex, queryMode) == 0;
+ }
+
+ private static boolean isFirstQueryInBurst(int queryIndex, int queryMode) {
+ return getQueryIndexInBurst(queryIndex, queryMode) == 0;
+ }
+
+ // TODO: move delay calculations to MdnsQueryScheduler
+ long getDelayBeforeTaskWithoutBackoff() {
+ return getDelayBeforeTaskWithoutBackoff(queryIndex, queryMode);
+ }
+
+ private static long getDelayBeforeTaskWithoutBackoff(int queryIndex, int queryMode) {
+ final int burstIndex = getBurstIndex(queryIndex, queryMode);
+ final int queryIndexInBurst = getQueryIndexInBurst(queryIndex, queryMode);
+ if (queryIndexInBurst == 0) {
+ return getTimeToBurstMs(burstIndex, queryMode);
+ } else if (queryIndexInBurst == 1 && queryMode == AGGRESSIVE_QUERY_MODE) {
+ // In aggressive mode, the first 2 queries are sent without delay.
+ return 0;
}
return queryMode == AGGRESSIVE_QUERY_MODE
? TIME_BETWEEN_RETRANSMISSION_QUERIES_IN_BURST_MS
: TIME_BETWEEN_QUERIES_IN_BURST_MS;
}
- boolean getNextExpectUnicastResponse(boolean isLastQueryInBurst, int queryMode) {
- if (!isLastQueryInBurst) {
- return false;
- }
+ private boolean getExpectUnicastResponse(int queryIndex, int queryMode) {
if (queryMode == AGGRESSIVE_QUERY_MODE) {
- return true;
+ if (isFirstQueryInBurst(queryIndex, queryMode)) {
+ return true;
+ }
}
return alwaysAskForUnicastResponse;
}
- int getNextTimeBetweenBurstsMs(boolean isLastQueryInBurst, int queryMode) {
- if (!isLastQueryInBurst) {
- return timeBetweenBurstsInMs;
+ /**
+ * Shifts a value left by the specified number of bits, coercing to at most maxValue.
+ *
+ * <p>This allows calculating min(value*2^shift, maxValue) without overflow.
+ */
+ private static int boundedLeftShift(int value, int shift, int maxValue) {
+ // There must be at least one leading zero for positive values, so the maximum left shift
+ // without overflow is the number of leading zeros minus one.
+ final int maxShift = Integer.numberOfLeadingZeros(value) - 1;
+ if (shift > maxShift) {
+ // The shift would overflow positive integers, so is greater than maxValue.
+ return maxValue;
}
- final int maxTimeBetweenBursts = queryMode == AGGRESSIVE_QUERY_MODE
- ? MAX_TIME_BETWEEN_AGGRESSIVE_BURSTS_MS : MAX_TIME_BETWEEN_ACTIVE_PASSIVE_BURSTS_MS;
- return Math.min(timeBetweenBurstsInMs * 2, maxTimeBetweenBursts);
+ return Math.min(value << shift, maxValue);
+ }
+
+ private static int getTimeToBurstMs(int burstIndex, int queryMode) {
+ if (burstIndex == 0) {
+ // No delay before the first burst
+ return 0;
+ }
+ switch (queryMode) {
+ case PASSIVE_QUERY_MODE:
+ return MAX_TIME_BETWEEN_ACTIVE_PASSIVE_BURSTS_MS;
+ case AGGRESSIVE_QUERY_MODE:
+ return boundedLeftShift(INITIAL_AGGRESSIVE_TIME_BETWEEN_BURSTS_MS,
+ burstIndex - 1,
+ MAX_TIME_BETWEEN_AGGRESSIVE_BURSTS_MS);
+ default: // ACTIVE_QUERY_MODE
+ return boundedLeftShift(INITIAL_TIME_BETWEEN_BURSTS_MS,
+ burstIndex - 1,
+ MAX_TIME_BETWEEN_ACTIVE_PASSIVE_BURSTS_MS);
+ }
}
/**
* Get new QueryTaskConfig for next run.
*/
public QueryTaskConfig getConfigForNextRun(int queryMode) {
- long newQueryCount = queryIndex + 1;
+ final int newQueryIndex = queryIndex + 1;
int newTransactionId = transactionId + 1;
if (newTransactionId > UNSIGNED_SHORT_MAX_VALUE) {
newTransactionId = 1;
}
- int newQueriesPerBurst = queriesPerBurst;
- int newBurstCounter = burstCounter + 1;
- final boolean isFirstQueryInBurst = newBurstCounter == 1;
- final boolean isLastQueryInBurst = newBurstCounter == queriesPerBurst;
- boolean newIsFirstBurst = isFirstBurst && !isLastQueryInBurst;
- if (isLastQueryInBurst) {
- newBurstCounter = 0;
- // In passive scan mode, sends a single burst of QUERIES_PER_BURST queries, and
- // then in each TIME_BETWEEN_BURSTS interval, sends QUERIES_PER_BURST_PASSIVE_MODE
- // queries.
- if (isFirstBurst && queryMode == PASSIVE_QUERY_MODE) {
- newQueriesPerBurst = QUERIES_PER_BURST_PASSIVE_MODE;
- }
- }
-
- return new QueryTaskConfig(newQueryCount, newTransactionId,
- getNextExpectUnicastResponse(isLastQueryInBurst, queryMode), newIsFirstBurst,
- newBurstCounter, newQueriesPerBurst,
- getNextTimeBetweenBurstsMs(isLastQueryInBurst, queryMode),
- getDelayBeforeNextTaskWithoutBackoff(
- isFirstQueryInBurst, isLastQueryInBurst, queryMode));
+ return new QueryTaskConfig(queryMode, newQueryIndex, newTransactionId,
+ getExpectUnicastResponse(newQueryIndex, queryMode));
}
/**
@@ -171,7 +177,7 @@
*/
public boolean shouldUseQueryBackoff(int numOfQueriesBeforeBackoff) {
// Don't enable backoff mode during the burst or in the first burst
- if (burstCounter != 0 || isFirstBurst) {
+ if (!isFirstQueryInBurst(queryIndex, queryMode) || isFirstBurst(queryIndex, queryMode)) {
return false;
}
return queryIndex > numOfQueriesBeforeBackoff;
diff --git a/service/libconnectivity/Android.bp b/service/libconnectivity/Android.bp
index 3a72134..9bfe3a9 100644
--- a/service/libconnectivity/Android.bp
+++ b/service/libconnectivity/Android.bp
@@ -42,6 +42,7 @@
],
llndk: {
symbol_file: "libconnectivity_native.map.txt",
+ moved_to_apex: true,
},
stubs: {
symbol_file: "libconnectivity_native.map.txt",
diff --git a/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java b/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
index f1ff2e4..5d588cc 100644
--- a/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
+++ b/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
@@ -100,6 +100,15 @@
public static final int IPV4_ADDR_LEN = 4;
public static final int IPV4_FLAG_MF = 0x2000;
public static final int IPV4_FLAG_DF = 0x4000;
+ public static final int IPV4_PROTOCOL_IGMP = 2;
+ public static final int IPV4_IGMP_MIN_SIZE = 8;
+ public static final int IPV4_IGMP_GROUP_RECORD_SIZE = 8;
+ public static final int IPV4_IGMP_TYPE_V1_REPORT = 0x12;
+ public static final int IPV4_IGMP_TYPE_V2_JOIN_REPORT = 0x16;
+ public static final int IPV4_IGMP_TYPE_V2_LEAVE_REPORT = 0x17;
+ public static final int IPV4_IGMP_TYPE_V3_REPORT = 0x22;
+ public static final int IPV4_OPTION_TYPE_ROUTER_ALERT = 0x94;
+ public static final int IPV4_OPTION_LEN_ROUTER_ALERT = 4;
// getSockOpt() for v4 MTU
public static final int IP_MTU = 14;
public static final Inet4Address IPV4_ADDR_ALL = makeInet4Address(
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index 9e57f69..a9ac29c 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -96,14 +96,8 @@
],
test_suites: [
"cts",
- "mts-dnsresolver",
- "mts-networking",
"mts-tethering",
- "mts-wifi",
- "mcts-dnsresolver",
- "mcts-networking",
"mcts-tethering",
- "mcts-wifi",
"general-tests",
],
}
diff --git a/tests/cts/net/AndroidTestTemplate.xml b/tests/cts/net/AndroidTestTemplate.xml
index 965d1f6..55b6494 100644
--- a/tests/cts/net/AndroidTestTemplate.xml
+++ b/tests/cts/net/AndroidTestTemplate.xml
@@ -61,8 +61,7 @@
</test>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<!-- Pattern matching the fileKey used by ConnectivityDiagnosticsCollector when calling addFileMetric -->
- <option name="pull-pattern-keys" value="com.android.testutils.ConnectivityDiagnosticsCollector.*" />
- <option name="log-data-type" value="CONNDIAG" />
+ <option name="pull-pattern-keys" value="com.android.testutils.ConnectivityDiagnosticsCollector.*"/>
<option name="collect-on-run-ended-only" value="true" />
</metrics_collector>
<!-- When this test is run in a Mainline context (e.g. with `mts-tradefed`), only enable it if
diff --git a/tests/cts/net/native/dns/Android.bp b/tests/cts/net/native/dns/Android.bp
index de4a3bf..8138598 100644
--- a/tests/cts/net/native/dns/Android.bp
+++ b/tests/cts/net/native/dns/Android.bp
@@ -62,8 +62,6 @@
"cts",
"general-tests",
"mts-dnsresolver",
- "mts-networking",
"mcts-dnsresolver",
- "mcts-networking",
],
}
diff --git a/thread/service/java/com/android/server/thread/FeatureFlags.java b/thread/service/java/com/android/server/thread/FeatureFlags.java
new file mode 100644
index 0000000..29bcedd
--- /dev/null
+++ b/thread/service/java/com/android/server/thread/FeatureFlags.java
@@ -0,0 +1,37 @@
+/*
+ * 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 com.android.server.thread;
+
+import com.android.net.module.util.DeviceConfigUtils;
+
+public class FeatureFlags {
+ // The namespace for Thread Network feature flags
+ private static final String NAMESPACE_THREAD_NETWORK = "thread_network";
+
+ // The prefix for TREL feature flags
+ private static final String TREL_FEATURE_PREFIX = "TrelFeature__";
+
+ // The feature flag for TREL enabled state
+ private static final String TREL_ENABLED_FLAG = TREL_FEATURE_PREFIX + "enabled";
+
+ private static boolean isFeatureEnabled(String flag, boolean defaultValue) {
+ return DeviceConfigUtils.getDeviceConfigPropertyBoolean(
+ NAMESPACE_THREAD_NETWORK, flag, defaultValue);
+ }
+
+ public static boolean isTrelEnabled() {
+ return isFeatureEnabled(TREL_ENABLED_FLAG, false);
+ }
+}
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
index 8747b44..30d5a02 100644
--- a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
@@ -357,7 +357,8 @@
mNsdPublisher,
getMeshcopTxtAttributes(mResources.get()),
mOtDaemonCallbackProxy,
- mCountryCodeSupplier.get());
+ mCountryCodeSupplier.get(),
+ FeatureFlags.isTrelEnabled());
otDaemon.asBinder().linkToDeath(() -> mHandler.post(this::onOtDaemonDied), 0);
mOtDaemon = otDaemon;
mHandler.post(mNat64CidrController::maybeUpdateNat64Cidr);
@@ -1406,7 +1407,7 @@
private void setInfraLinkState(InfraLinkState newInfraLinkState) {
if (Objects.equals(mInfraLinkState, newInfraLinkState)) {
- return ;
+ return;
}
LOG.i("Infra link state changed: " + mInfraLinkState + " -> " + newInfraLinkState);
setInfraLinkInterfaceName(newInfraLinkState.interfaceName);
@@ -1417,7 +1418,7 @@
private void setInfraLinkInterfaceName(String newInfraLinkInterfaceName) {
if (Objects.equals(mInfraLinkState.interfaceName, newInfraLinkInterfaceName)) {
- return ;
+ return;
}
ParcelFileDescriptor infraIcmp6Socket = null;
if (newInfraLinkInterfaceName != null) {
@@ -1440,7 +1441,7 @@
private void setInfraLinkNat64Prefix(@Nullable String newNat64Prefix) {
if (Objects.equals(newNat64Prefix, mInfraLinkState.nat64Prefix)) {
- return ;
+ return;
}
try {
getOtDaemon()
@@ -1453,7 +1454,7 @@
private void setInfraLinkDnsServers(List<String> newDnsServers) {
if (Objects.equals(newDnsServers, mInfraLinkState.dnsServers)) {
- return ;
+ return;
}
try {
getOtDaemon()
diff --git a/thread/tests/integration/AndroidTest.xml b/thread/tests/integration/AndroidTest.xml
index 8f98941..08409b4 100644
--- a/thread/tests/integration/AndroidTest.xml
+++ b/thread/tests/integration/AndroidTest.xml
@@ -48,4 +48,10 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.thread.tests.integration" />
</test>
+
+ <!-- Enable TREL for integration tests -->
+ <target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer">
+ <option name="flag-value"
+ value="thread_network/TrelFeature__enabled=true"/>
+ </target_preparer>
</configuration>
diff --git a/thread/tests/integration/src/android/net/thread/InternetAccessTest.kt b/thread/tests/integration/src/android/net/thread/InternetAccessTest.kt
index cb00611..3c9aa07 100644
--- a/thread/tests/integration/src/android/net/thread/InternetAccessTest.kt
+++ b/thread/tests/integration/src/android/net/thread/InternetAccessTest.kt
@@ -200,6 +200,24 @@
assertThat(reply).isEqualTo("Hello,Thread")
}
+ @Test
+ fun nat64Enabled_afterInfraNetworkSwitch_threadDeviceSendsUdpToEchoServer_replyIsReceived() {
+ controller.setNat64EnabledAndWait(true)
+ waitFor({ otCtl.hasNat64PrefixInNetdata() }, Duration.ofSeconds(10))
+ infraNetworkTracker = TestTunNetworkUtils.setUpInfraNetwork(context, controller)
+ infraNetworkReader = newPacketReader(infraNetworkTracker.testIface, handler)
+ udpEchoServer = TestUdpEchoServer(infraNetworkReader, UDP_ECHO_SERVER_ADDRESS)
+ val ftd = ftds[0]
+ joinNetworkAndWaitForOmr(ftd, DEFAULT_DATASET)
+ waitFor({ otCtl.hasNat64PrefixInNetdata() }, Duration.ofSeconds(10))
+ udpEchoServer.start()
+
+ ftd.udpOpen()
+ ftd.udpSend("Hello,Thread", UDP_ECHO_SERVER_ADDRESS.address, UDP_ECHO_SERVER_ADDRESS.port)
+ val reply = ftd.udpReceive()
+ assertThat(reply).isEqualTo("Hello,Thread")
+ }
+
private fun extractIpv4AddressFromMappedAddress(address: InetAddress): Inet4Address {
return InetAddress.getByAddress(address.address.slice(12 until 16).toByteArray())
as Inet4Address
diff --git a/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java b/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
index 2afca5f..15259c8 100644
--- a/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
+++ b/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
@@ -50,6 +50,8 @@
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.net.module.util.HexDump;
+
import com.google.common.truth.Correspondence;
import org.junit.After;
@@ -64,6 +66,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
@@ -441,6 +444,25 @@
.containsExactly("key1", bytes(0x01, 0x02), "key2", bytes(3));
}
+ @Test
+ // TODO: move this case out to BorderRoutingTest when the service discovery utilities
+ // are decoupled from this test.
+ public void trelFeatureFlagEnabled_trelServicePublished() throws Exception {
+ NsdServiceInfo discoveredService = discoverService(mNsdManager, "_trel._udp");
+ assertThat(discoveredService).isNotNull();
+ // Resolve service with the current TREL port, otherwise it may return stale service from
+ // a previous infra link setup.
+ NsdServiceInfo trelService =
+ resolveServiceUntil(
+ mNsdManager, discoveredService, s -> s.getPort() == mOtCtl.getTrelPort());
+
+ Map<String, byte[]> txtMap = trelService.getAttributes();
+ assertThat(HexDump.toHexString(txtMap.get("xa")).toLowerCase(Locale.ROOT))
+ .isEqualTo(mOtCtl.getExtendedAddr().toLowerCase(Locale.ROOT));
+ assertThat(HexDump.toHexString(txtMap.get("xp")).toLowerCase(Locale.ROOT))
+ .isEqualTo(mOtCtl.getExtendedPanId().toLowerCase(Locale.ROOT));
+ }
+
private void registerService(NsdServiceInfo serviceInfo, RegistrationListener listener)
throws InterruptedException, ExecutionException, TimeoutException {
mNsdManager.registerService(serviceInfo, PROTOCOL_DNS_SD, listener);
diff --git a/thread/tests/integration/src/android/net/thread/utils/OtDaemonController.java b/thread/tests/integration/src/android/net/thread/utils/OtDaemonController.java
index afb0fc7..9fbfa45 100644
--- a/thread/tests/integration/src/android/net/thread/utils/OtDaemonController.java
+++ b/thread/tests/integration/src/android/net/thread/utils/OtDaemonController.java
@@ -144,6 +144,18 @@
executeCommand("netdata register");
}
+ public int getTrelPort() {
+ return Integer.parseInt(executeCommandAndParse("trel port").get(0));
+ }
+
+ public String getExtendedAddr() {
+ return executeCommandAndParse("extaddr").get(0);
+ }
+
+ public String getExtendedPanId() {
+ return executeCommandAndParse("extpanid").get(0);
+ }
+
public String executeCommand(String cmd) {
return SystemUtil.runShellCommand(OT_CTL + " " + cmd);
}
diff --git a/thread/tests/unit/Android.bp b/thread/tests/unit/Android.bp
index c6a24ea..53b1eca 100644
--- a/thread/tests/unit/Android.bp
+++ b/thread/tests/unit/Android.bp
@@ -35,6 +35,7 @@
static_libs: [
"androidx.test.rules",
"frameworks-base-testutils",
+ "framework-configinfrastructure.stubs.module_lib",
"framework-connectivity-pre-jarjar",
"framework-connectivity-t-pre-jarjar",
"framework-location.stubs.module_lib",
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 e188491..9af0b53 100644
--- a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
+++ b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
@@ -17,6 +17,7 @@
package com.android.server.thread;
import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
@@ -34,6 +35,7 @@
import static com.android.server.thread.ThreadNetworkCountryCode.DEFAULT_COUNTRY_CODE;
import static com.android.server.thread.openthread.IOtDaemon.ErrorCode.OT_ERROR_INVALID_STATE;
+import static com.android.testutils.TestPermissionUtil.runAsShell;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat;
@@ -85,6 +87,7 @@
import android.os.SystemClock;
import android.os.UserManager;
import android.os.test.TestLooper;
+import android.provider.DeviceConfig;
import android.util.AtomicFile;
import androidx.test.annotation.UiThreadTest;
@@ -102,6 +105,7 @@
import com.android.server.thread.openthread.OtDaemonConfiguration;
import com.android.server.thread.openthread.testing.FakeOtDaemon;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -258,6 +262,13 @@
mService.setTestNetworkAgent(mMockNetworkAgent);
}
+ @After
+ public void tearDown() throws Exception {
+ runAsShell(
+ WRITE_DEVICE_CONFIG,
+ () -> DeviceConfig.deleteProperty("thread_network", "TrelFeature__enabled"));
+ }
+
@Test
public void initialize_tunInterfaceAndNsdPublisherSetToOtDaemon() throws Exception {
when(mMockTunIfController.getTunFd()).thenReturn(mMockTunFd);
@@ -324,6 +335,33 @@
}
@Test
+ public void initialize_trelFeatureDisabled_trelDisabledAtOtDaemon() throws Exception {
+ runAsShell(
+ WRITE_DEVICE_CONFIG,
+ () ->
+ DeviceConfig.setProperty(
+ "thread_network", "TrelFeature__enabled", "false", false));
+
+ mService.initialize();
+ mTestLooper.dispatchAll();
+
+ assertThat(mFakeOtDaemon.isTrelEnabled()).isFalse();
+ }
+
+ @Test
+ public void initialize_trelFeatureEnabled_setTrelEnabledAtOtDamon() throws Exception {
+ runAsShell(
+ WRITE_DEVICE_CONFIG,
+ () ->
+ DeviceConfig.setProperty(
+ "thread_network", "TrelFeature__enabled", "true", false));
+ mService.initialize();
+ mTestLooper.dispatchAll();
+
+ assertThat(mFakeOtDaemon.isTrelEnabled()).isTrue();
+ }
+
+ @Test
public void getMeshcopTxtAttributes_emptyVendorName_accepted() {
when(mResources.getString(eq(R.string.config_thread_vendor_name))).thenReturn("");