From bb7efba754100db9413fa43b2c0998c60179a602 Mon Sep 17 00:00:00 2001 From: Yang Sun Date: Thu, 7 Nov 2024 17:09:03 +0800 Subject: [PATCH] Configure TREL feature from feature flag. Bug: 354868035 Bug: 359089751 Test: atest ThreadNetworkIntegrationTests:ServiceDiscoveryTest#trel_servicePublished Change-Id: Ib545df6bf5ce20f35914b4695dbd2e3c1e1232e4 --- .../android/server/thread/FeatureFlags.java | 37 ++++++++++++++++++ .../ThreadNetworkControllerService.java | 11 +++--- thread/tests/integration/AndroidTest.xml | 6 +++ .../net/thread/ServiceDiscoveryTest.java | 22 +++++++++++ .../net/thread/utils/OtDaemonController.java | 12 ++++++ thread/tests/unit/Android.bp | 1 + .../ThreadNetworkControllerServiceTest.java | 38 +++++++++++++++++++ 7 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 thread/service/java/com/android/server/thread/FeatureFlags.java 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 0000000000..29bcedd0b3 --- /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 8747b44b46..30d5a02c67 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 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub 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 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub 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 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub private void setInfraLinkInterfaceName(String newInfraLinkInterfaceName) { if (Objects.equals(mInfraLinkState.interfaceName, newInfraLinkInterfaceName)) { - return ; + return; } ParcelFileDescriptor infraIcmp6Socket = null; if (newInfraLinkInterfaceName != null) { @@ -1440,7 +1441,7 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub private void setInfraLinkNat64Prefix(@Nullable String newNat64Prefix) { if (Objects.equals(newNat64Prefix, mInfraLinkState.nat64Prefix)) { - return ; + return; } try { getOtDaemon() @@ -1453,7 +1454,7 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub private void setInfraLinkDnsServers(List 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 8f989415d7..08409b426c 100644 --- a/thread/tests/integration/AndroidTest.xml +++ b/thread/tests/integration/AndroidTest.xml @@ -48,4 +48,10 @@ + + + + diff --git a/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java b/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java index 2afca5fe32..15259c803e 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.core.app.ApplicationProvider; 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.ArrayList; 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 @@ public class ServiceDiscoveryTest { .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 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 afb0fc798d..9fbfa45867 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 @@ public final class OtDaemonController { 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 c6a24ea508..53b1eca63b 100644 --- a/thread/tests/unit/Android.bp +++ b/thread/tests/unit/Android.bp @@ -35,6 +35,7 @@ android_test { 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 e1884915c3..9af0b53967 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 android.net.thread.ThreadNetworkManager.PERMISSION_THREAD_NETWORK_ 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.RemoteException; 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.MeshcopTxtAttributes; 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 @@ public final class ThreadNetworkControllerServiceTest { 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); @@ -323,6 +334,33 @@ public final class ThreadNetworkControllerServiceTest { verify(mFakeOtDaemon, times(1)).setNat64Cidr(eq(TEST_NAT64_CIDR.toString()), any()); } + @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("");