Snap for 11967491 from 2db9f0ea9952d7704e5a21009c1d9ff47570fbc4 to 24Q3-release

Change-Id: Icf1f3513625eae92eb2957fc86fabec29748a4c0
diff --git a/src/android/aidl/com/android/server/thread/openthread/DnsTxtAttribute.aidl b/src/android/aidl/com/android/server/thread/openthread/DnsTxtAttribute.aidl
index 8fa9ed8..d2db1f4 100644
--- a/src/android/aidl/com/android/server/thread/openthread/DnsTxtAttribute.aidl
+++ b/src/android/aidl/com/android/server/thread/openthread/DnsTxtAttribute.aidl
@@ -31,6 +31,8 @@
 /**
  *  An attribute in DNS TXT Resource Record.
  */
+@JavaOnlyImmutable
+@JavaDerive(equals=true, toString=true)
 parcelable DnsTxtAttribute {
     String name; // The name of the attribute.
     byte[] value; // The value of the attribute.
diff --git a/src/android/aidl/com/android/server/thread/openthread/MeshcopTxtAttributes.aidl b/src/android/aidl/com/android/server/thread/openthread/MeshcopTxtAttributes.aidl
index 3ac1db8..f97965d 100644
--- a/src/android/aidl/com/android/server/thread/openthread/MeshcopTxtAttributes.aidl
+++ b/src/android/aidl/com/android/server/thread/openthread/MeshcopTxtAttributes.aidl
@@ -28,6 +28,8 @@
 
 package com.android.server.thread.openthread;
 
+import com.android.server.thread.openthread.DnsTxtAttribute;
+
 /**
  *  A collection of MeshCoP TXT entries that are supplied by Android platform.
  */
@@ -53,5 +55,11 @@
      */
     byte[] vendorOui;
 
-    // More vendor-specific (v*) TXT entries can be added here
+    /**
+     * Non-standard (vendor-specific) _meshcop._udp TXT entries.
+     *
+     * All TXT keys MUST start with "v".
+     *
+     */
+    List<DnsTxtAttribute> nonStandardTxtEntries;
 }
diff --git a/src/android/java/com/android/server/thread/openthread/testing/FakeOtDaemon.java b/src/android/java/com/android/server/thread/openthread/testing/FakeOtDaemon.java
index 931e373..993b9fc 100644
--- a/src/android/java/com/android/server/thread/openthread/testing/FakeOtDaemon.java
+++ b/src/android/java/com/android/server/thread/openthread/testing/FakeOtDaemon.java
@@ -52,6 +52,7 @@
 
 import java.time.Duration;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.NoSuchElementException;
 
 /** A fake implementation of the {@link IOtDaemon} AIDL API for testing. */
@@ -158,6 +159,8 @@
         mOverriddenMeshcopTxts.vendorOui = overriddenMeshcopTxts.vendorOui.clone();
         mOverriddenMeshcopTxts.vendorName = overriddenMeshcopTxts.vendorName;
         mOverriddenMeshcopTxts.modelName = overriddenMeshcopTxts.modelName;
+        mOverriddenMeshcopTxts.nonStandardTxtEntries =
+                List.copyOf(overriddenMeshcopTxts.nonStandardTxtEntries);
 
         registerStateCallback(callback, PROACTIVE_LISTENER_ID);
     }
diff --git a/src/android/otdaemon_server.cpp b/src/android/otdaemon_server.cpp
index 030556e..5f5ea3c 100644
--- a/src/android/otdaemon_server.cpp
+++ b/src/android/otdaemon_server.cpp
@@ -477,14 +477,26 @@
                                         const std::shared_ptr<IOtDaemonCallback> &aCallback,
                                         const std::string                        &aCountryCode)
 {
-    std::string instanceName = aMeshcopTxts.vendorName + " " + aMeshcopTxts.modelName;
+    std::string              instanceName = aMeshcopTxts.vendorName + " " + aMeshcopTxts.modelName;
+    Mdns::Publisher::TxtList nonStandardTxts;
+    otbrError                error;
 
     setCountryCodeInternal(aCountryCode, nullptr /* aReceiver */);
     registerStateCallbackInternal(aCallback, -1 /* listenerId */);
 
     mMdnsPublisher.SetINsdPublisher(aINsdPublisher);
-    mBorderAgent.SetMeshCopServiceValues(instanceName, aMeshcopTxts.modelName, aMeshcopTxts.vendorName,
-                                         aMeshcopTxts.vendorOui);
+
+    for (const auto &txtAttr : aMeshcopTxts.nonStandardTxtEntries)
+    {
+        nonStandardTxts.emplace_back(txtAttr.name.c_str(), txtAttr.value.data(), txtAttr.value.size());
+    }
+    error = mBorderAgent.SetMeshCopServiceValues(instanceName, aMeshcopTxts.modelName, aMeshcopTxts.vendorName,
+                                                 aMeshcopTxts.vendorOui, nonStandardTxts);
+    if (error != OTBR_ERROR_NONE)
+    {
+        otbrLogCrit("Failed to set MeshCoP values: %d", static_cast<int>(error));
+    }
+
     mBorderAgent.SetEnabled(enabled);
 
     if (enabled)
diff --git a/src/border_agent/border_agent.cpp b/src/border_agent/border_agent.cpp
index b6acf18..96700d5 100644
--- a/src/border_agent/border_agent.cpp
+++ b/src/border_agent/border_agent.cpp
@@ -146,20 +146,30 @@
     mNcp.AddThreadStateChangedCallback([this](otChangedFlags aFlags) { HandleThreadStateChanged(aFlags); });
 }
 
-otbrError BorderAgent::SetMeshCopServiceValues(const std::string          &aServiceInstanceName,
-                                               const std::string          &aProductName,
-                                               const std::string          &aVendorName,
-                                               const std::vector<uint8_t> &aVendorOui)
+otbrError BorderAgent::SetMeshCopServiceValues(const std::string              &aServiceInstanceName,
+                                               const std::string              &aProductName,
+                                               const std::string              &aVendorName,
+                                               const std::vector<uint8_t>     &aVendorOui,
+                                               const Mdns::Publisher::TxtList &aNonStandardTxtEntries)
 {
     otbrError error = OTBR_ERROR_NONE;
 
     VerifyOrExit(aProductName.size() <= kMaxProductNameLength, error = OTBR_ERROR_INVALID_ARGS);
     VerifyOrExit(aVendorName.size() <= kMaxVendorNameLength, error = OTBR_ERROR_INVALID_ARGS);
     VerifyOrExit(aVendorOui.empty() || aVendorOui.size() == kVendorOuiLength, error = OTBR_ERROR_INVALID_ARGS);
+    for (const auto &txtEntry : aNonStandardTxtEntries)
+    {
+        VerifyOrExit(!txtEntry.mKey.empty() && txtEntry.mKey.front() == 'v', error = OTBR_ERROR_INVALID_ARGS);
+    }
 
     mProductName = aProductName;
     mVendorName  = aVendorName;
     mVendorOui   = aVendorOui;
+    mMeshCopTxtUpdate.clear();
+    for (const auto &txtEntry : aNonStandardTxtEntries)
+    {
+        mMeshCopTxtUpdate[txtEntry.mKey] = txtEntry.mValue;
+    }
 
     mBaseServiceInstanceName = aServiceInstanceName;
 
@@ -317,7 +327,6 @@
     }
 }
 
-#if OTBR_ENABLE_DBUS_SERVER
 void AppendVendorTxtEntries(const std::map<std::string, std::vector<uint8_t>> &aVendorEntries,
                             Mdns::Publisher::TxtList                          &aTxtList)
 {
@@ -343,7 +352,6 @@
         }
     }
 }
-#endif
 
 void BorderAgent::PublishMeshCopService(void)
 {
@@ -417,9 +425,8 @@
 #if OTBR_ENABLE_BORDER_ROUTING
     AppendOmrTxtEntry(*instance, txtList);
 #endif
-#if OTBR_ENABLE_DBUS_SERVER
+
     AppendVendorTxtEntries(mMeshCopTxtUpdate, txtList);
-#endif
 
     if (otBorderAgentGetState(instance) != OT_BORDER_AGENT_STATE_STOPPED)
     {
diff --git a/src/border_agent/border_agent.hpp b/src/border_agent/border_agent.hpp
index 39a0d2c..b5b71f5 100644
--- a/src/border_agent/border_agent.hpp
+++ b/src/border_agent/border_agent.hpp
@@ -99,22 +99,24 @@
      *
      * This method must be called before this BorderAgent is enabled by SetEnabled.
      *
-     * @param[in] aServiceInstanceName  The service instance name; suffix may be appended to this value to avoid
-     *                                  name conflicts.
-     * @param[in] aProductName          The product name; must not exceed length of kMaxProductNameLength
-     *                                  and an empty string will be ignored.
-     * @param[in] aVendorName           The vendor name; must not exceed length of kMaxVendorNameLength
-     *                                  and an empty string will be ignored.
-     * @param[in] aVendorOui            The vendor OUI; must have length of 3 bytes or be empty and ignored.
+     * @param[in] aServiceInstanceName    The service instance name; suffix may be appended to this value to avoid
+     *                                    name conflicts.
+     * @param[in] aProductName            The product name; must not exceed length of kMaxProductNameLength
+     *                                    and an empty string will be ignored.
+     * @param[in] aVendorName             The vendor name; must not exceed length of kMaxVendorNameLength
+     *                                    and an empty string will be ignored.
+     * @param[in] aVendorOui              The vendor OUI; must have length of 3 bytes or be empty and ignored.
+     * @param[in] aNonStandardTxtEntries  Non-standard (vendor-specific) TXT entries whose key MUST start with "v"
      *
      * @returns OTBR_ERROR_INVALID_ARGS  If aVendorName, aProductName or aVendorOui exceeds the
-     *                                   allowed ranges.
+     *                                   allowed ranges or invalid keys are found in aNonStandardTxtEntries
      * @returns OTBR_ERROR_NONE          If successfully set the meshcop service values.
      */
-    otbrError SetMeshCopServiceValues(const std::string          &aServiceInstanceName,
-                                      const std::string          &aProductName,
-                                      const std::string          &aVendorName,
-                                      const std::vector<uint8_t> &aVendorOui = {});
+    otbrError SetMeshCopServiceValues(const std::string              &aServiceInstanceName,
+                                      const std::string              &aProductName,
+                                      const std::string              &aVendorName,
+                                      const std::vector<uint8_t>     &aVendorOui             = {},
+                                      const Mdns::Publisher::TxtList &aNonStandardTxtEntries = {});
 
     /**
      * This method enables/disables the Border Agent.
@@ -153,9 +155,7 @@
     Mdns::Publisher                 &mPublisher;
     bool                             mIsEnabled;
 
-#if OTBR_ENABLE_DBUS_SERVER
     std::map<std::string, std::vector<uint8_t>> mMeshCopTxtUpdate;
-#endif
 
     std::vector<uint8_t> mVendorOui;
 
diff --git a/tests/android/java/com/android/server/thread/openthread/testing/FakeOtDaemonTest.java b/tests/android/java/com/android/server/thread/openthread/testing/FakeOtDaemonTest.java
index 2ee0553..8e214f4 100644
--- a/tests/android/java/com/android/server/thread/openthread/testing/FakeOtDaemonTest.java
+++ b/tests/android/java/com/android/server/thread/openthread/testing/FakeOtDaemonTest.java
@@ -53,6 +53,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.thread.openthread.BackboneRouterState;
+import com.android.server.thread.openthread.DnsTxtAttribute;
 import com.android.server.thread.openthread.IChannelMasksReceiver;
 import com.android.server.thread.openthread.INsdPublisher;
 import com.android.server.thread.openthread.IOtDaemonCallback;
@@ -67,6 +68,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
@@ -119,6 +121,7 @@
         mOverriddenMeshcopTxts.vendorName = TEST_VENDOR_NAME;
         mOverriddenMeshcopTxts.vendorOui = TEST_VENDOR_OUI;
         mOverriddenMeshcopTxts.modelName = TEST_MODEL_NAME;
+        mOverriddenMeshcopTxts.nonStandardTxtEntries = List.of();
     }
 
     @Test
@@ -126,6 +129,8 @@
         mOverriddenMeshcopTxts.vendorName = TEST_VENDOR_NAME;
         mOverriddenMeshcopTxts.vendorOui = TEST_VENDOR_OUI;
         mOverriddenMeshcopTxts.modelName = TEST_MODEL_NAME;
+        mOverriddenMeshcopTxts.nonStandardTxtEntries =
+                List.of(new DnsTxtAttribute("v2", new byte[] {0x02}));
 
         mFakeOtDaemon.initialize(
                 mMockTunFd,
@@ -141,6 +146,8 @@
         assertThat(meshcopTxts.vendorName).isEqualTo(TEST_VENDOR_NAME);
         assertThat(meshcopTxts.vendorOui).isEqualTo(TEST_VENDOR_OUI);
         assertThat(meshcopTxts.modelName).isEqualTo(TEST_MODEL_NAME);
+        assertThat(meshcopTxts.nonStandardTxtEntries)
+                .containsExactly(new DnsTxtAttribute("v2", new byte[] {0x02}));
         assertThat(mFakeOtDaemon.getTunFd()).isEqualTo(mMockTunFd);
         assertThat(mFakeOtDaemon.getEnabledState()).isEqualTo(OT_STATE_ENABLED);
         assertThat(mFakeOtDaemon.getNsdPublisher()).isEqualTo(mMockNsdPublisher);