aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHanda Wang <handaw@google.com>2023-12-21 13:56:05 +0800
committerHanda Wang <handaw@google.com>2024-02-02 06:27:02 +0000
commit803e06be599c1bde0fb6d18c2c1a2bea7bfe8cf2 (patch)
treeb6e62fb49abec8646deba4d5504e990acb81dd9c
parent9602b96ba439d53f0c15b7940c9e957d84220f61 (diff)
downloadot-br-posix-803e06be599c1bde0fb6d18c2c1a2bea7bfe8cf2.tar.gz
[mdns] mDNS publisher implementation for ot-daemon
This CL contains: - The AIDL definitions related to mDNS publisher. - The `otbr::mdns::Publisher` implementation for Android platform. Bug: 320211657 Bug: 318323473 Change-Id: I40450ea0001d029e61d6db6fa92e99607e64ecea
-rw-r--r--Android.bp30
-rw-r--r--src/android/aidl/com/android/server/thread/openthread/DnsTxtAttribute.aidl37
-rw-r--r--src/android/aidl/com/android/server/thread/openthread/INsdPublisher.aidl75
-rw-r--r--src/android/aidl/com/android/server/thread/openthread/INsdStatusReceiver.aidl38
-rw-r--r--src/android/aidl/com/android/server/thread/openthread/IOtDaemon.aidl8
-rw-r--r--src/android/java/com/android/server/thread/openthread/testing/FakeOtDaemon.java16
-rw-r--r--src/android/mdns_publisher.cpp265
-rw-r--r--src/android/mdns_publisher.hpp162
-rw-r--r--src/android/otdaemon_fuzzer.cpp4
-rw-r--r--src/android/otdaemon_server.cpp26
-rw-r--r--src/android/otdaemon_server.hpp12
-rw-r--r--tests/android/java/com/android/server/thread/openthread/testing/FakeOtDaemonTest.java13
12 files changed, 659 insertions, 27 deletions
diff --git a/Android.bp b/Android.bp
index 2b818cbd..4aa21386 100644
--- a/Android.bp
+++ b/Android.bp
@@ -90,16 +90,16 @@ cc_defaults {
cflags: [
// Optimize for debugging
"-Og",
- ]
- }
+ ],
+ },
},
// Enable security checks
sanitize: {
- misc_undefined: ["bounds"],
- cfi: true,
- scs: true,
- },
+ misc_undefined: ["bounds"],
+ cfi: true,
+ scs: true,
+ },
}
cc_defaults {
@@ -134,14 +134,19 @@ cc_defaults {
"-DOTBR_CONFIG_FILE=\"src/android/otbr-config-android.h\"",
"-DOTBR_ENABLE_VENDOR_SERVER=1", // for OtDaemonServer
"-DOTBR_ENABLE_BORDER_ROUTING=1",
+ "-DOTBR_ENABLE_BORDER_AGENT=1",
+ "-DOTBR_ENABLE_PUBLISH_MESHCOP_BA_ID=1",
+ // Used for bypassing the macro check. In fact mdnssd is not used because we don't compile
+ // the related source files.
"-DOTBR_ENABLE_MDNS_MDNSSD=1",
- "-DOTBR_ENABLE_SRP_ADVERTISING_PROXY=1",
- "-DOTBR_ENABLE_DNSSD_DISCOVERY_PROXY=1",
+ "-DOTBR_ENABLE_SRP_ADVERTISING_PROXY=0",
+ "-DOTBR_ENABLE_DNSSD_DISCOVERY_PROXY=0",
"-DOTBR_PACKAGE_NAME=\"OTBR_AGENT\"",
],
srcs: [
"src/agent/application.cpp",
+ "src/android/mdns_publisher.cpp",
"src/android/otdaemon_server.cpp",
"src/android/otdaemon_telemetry.cpp",
"src/border_agent/border_agent.cpp",
@@ -156,7 +161,6 @@ cc_defaults {
"src/common/task_runner.cpp",
"src/common/types.cpp",
"src/mdns/mdns.cpp",
- "src/mdns/mdns_mdnssd.cpp",
"src/utils/crc16.cpp",
"src/utils/dns_utils.cpp",
"src/utils/hex.cpp",
@@ -190,7 +194,7 @@ cc_defaults {
host_ldlibs: ["-lutil"],
min_sdk_version: "30",
- apex_available: [ "com.android.tethering" ],
+ apex_available: ["com.android.tethering"],
}
cc_binary {
@@ -206,9 +210,9 @@ cc_binary {
cc_fuzz {
name: "ot_daemon_service_fuzzer",
- defaults:[
+ defaults: [
"ot-daemon-defaults",
- "service_fuzzer_defaults"
+ "service_fuzzer_defaults",
],
srcs: [
"src/android/otdaemon_fuzzer.cpp",
@@ -239,7 +243,7 @@ cc_library_static {
"libutils",
],
min_sdk_version: "30",
- apex_available: [ "com.android.tethering" ],
+ apex_available: ["com.android.tethering"],
}
genrule {
diff --git a/src/android/aidl/com/android/server/thread/openthread/DnsTxtAttribute.aidl b/src/android/aidl/com/android/server/thread/openthread/DnsTxtAttribute.aidl
new file mode 100644
index 00000000..8fa9ed88
--- /dev/null
+++ b/src/android/aidl/com/android/server/thread/openthread/DnsTxtAttribute.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2024, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.server.thread.openthread;
+
+/**
+ * An attribute in DNS TXT Resource Record.
+ */
+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/INsdPublisher.aidl b/src/android/aidl/com/android/server/thread/openthread/INsdPublisher.aidl
new file mode 100644
index 00000000..11ba51c9
--- /dev/null
+++ b/src/android/aidl/com/android/server/thread/openthread/INsdPublisher.aidl
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2024, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.server.thread.openthread;
+
+import com.android.server.thread.openthread.DnsTxtAttribute;
+import com.android.server.thread.openthread.INsdStatusReceiver;
+
+/**
+ * The service which supports mDNS advertising and discovery by {@link NsdManager}.
+ */
+oneway interface INsdPublisher {
+ /**
+ * Registers an mDNS service.
+ *
+ * <p>When the hostname is set to null, register the service with the default host. Otherwise,
+ * register the servcie with the specified hostname.
+ *
+ * <p>The listenerId is an integer ID generated by the caller. It's used by the caller and
+ * the NsdPublisher service to uniquely identify a registration.
+ *
+ * @param hostname the hostname
+ * @param name the service instance name
+ * @param type the service type
+ * @param subtypeList the list of subtypes
+ * @param port the port number of the service
+ * @param txt the entries of the TXT record
+ * @param receiver the receiver of the register callback
+ * @param listenerId the listener ID of the 'unregister' opreation
+ */
+ void registerService(in @nullable String hostname,
+ in String name,
+ in String type,
+ in List<String> subtypeList,
+ int port,
+ in List<DnsTxtAttribute> txt,
+ in INsdStatusReceiver receiver,
+ int listenerId);
+
+ /**
+ * Unregisters an mDNS service.
+ *
+ * <p>To unregister a previously registered service/host/key, the caller must pass in the same
+ * listener which was used when registering the service/host/key.
+ *
+ * @param receiver the receiver of the unregister callback
+ * @param listenerId the listenerId of the 'unregister' operation
+ */
+ void unregister(in INsdStatusReceiver receiver, int listenerId);
+}
diff --git a/src/android/aidl/com/android/server/thread/openthread/INsdStatusReceiver.aidl b/src/android/aidl/com/android/server/thread/openthread/INsdStatusReceiver.aidl
new file mode 100644
index 00000000..f7891c5c
--- /dev/null
+++ b/src/android/aidl/com/android/server/thread/openthread/INsdStatusReceiver.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2024, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.server.thread.openthread;
+
+/**
+ * Receives the status of an mDNS operation which may fail with an error code from
+ * {@link NsdManager}.
+ */
+oneway interface INsdStatusReceiver {
+ void onSuccess();
+ void onError(int errorCode);
+}
diff --git a/src/android/aidl/com/android/server/thread/openthread/IOtDaemon.aidl b/src/android/aidl/com/android/server/thread/openthread/IOtDaemon.aidl
index 3c51df39..e73df904 100644
--- a/src/android/aidl/com/android/server/thread/openthread/IOtDaemon.aidl
+++ b/src/android/aidl/com/android/server/thread/openthread/IOtDaemon.aidl
@@ -34,6 +34,7 @@ import com.android.server.thread.openthread.BorderRouterConfigurationParcel;
import com.android.server.thread.openthread.Ipv6AddressInfo;
import com.android.server.thread.openthread.IOtStatusReceiver;
import com.android.server.thread.openthread.IOtDaemonCallback;
+import com.android.server.thread.openthread.INsdPublisher;
/**
* The OpenThread daemon service which provides access to the core Thread stack for
@@ -78,8 +79,11 @@ oneway interface IOtDaemon {
* @param tunFd the Thread tunnel interface FD which can be used to transmit/receive
* packets to/from Thread PAN
* @param enabled the Thead enabled state from Persistent Settings
+ * @param nsdPublisher the INsdPublisher which can be used for mDNS advertisement/discovery
+ * on AIL by {@link NsdManager}
*/
- void initialize(in ParcelFileDescriptor tunFd, in boolean enabled);
+ void initialize(in ParcelFileDescriptor tunFd, in boolean enabled,
+ in INsdPublisher nsdPublisher);
/**
* Enables/disables Thread.
@@ -148,6 +152,4 @@ oneway interface IOtDaemon {
*/
oneway void configureBorderRouter(
in BorderRouterConfigurationParcel brConfig, in IOtStatusReceiver receiver);
-
- // TODO: add Border Router APIs
}
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 e69201b2..56508e83 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
@@ -40,6 +40,7 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import com.android.server.thread.openthread.BorderRouterConfigurationParcel;
+import com.android.server.thread.openthread.INsdPublisher;
import com.android.server.thread.openthread.IOtDaemon;
import com.android.server.thread.openthread.IOtDaemonCallback;
import com.android.server.thread.openthread.IOtStatusReceiver;
@@ -69,6 +70,8 @@ public final class FakeOtDaemon extends IOtDaemon.Stub {
@Nullable private ParcelFileDescriptor mTunFd;
+ @NonNull private INsdPublisher mNsdPublisher;
+
@Nullable private IOtDaemonCallback mCallback;
@Nullable private Long mCallbackListenerId;
@@ -110,9 +113,11 @@ public final class FakeOtDaemon extends IOtDaemon.Stub {
}
@Override
- public void initialize(ParcelFileDescriptor tunFd, boolean enabled) throws RemoteException {
+ public void initialize(ParcelFileDescriptor tunFd, boolean enabled, INsdPublisher nsdPublisher)
+ throws RemoteException {
mTunFd = tunFd;
mThreadEnabled = enabled ? OT_STATE_ENABLED : OT_STATE_DISABLED;
+ mNsdPublisher = nsdPublisher;
}
@Override
@@ -141,6 +146,15 @@ public final class FakeOtDaemon extends IOtDaemon.Stub {
return mTunFd;
}
+ /**
+ * Returns the INsdPublisher sent to OT daemon or {@code null} if {@link #initialize} is never
+ * called.
+ */
+ @Nullable
+ public INsdPublisher getNsdPublisher() {
+ return mNsdPublisher;
+ }
+
@Override
public void registerStateCallback(IOtDaemonCallback callback, long listenerId)
throws RemoteException {
diff --git a/src/android/mdns_publisher.cpp b/src/android/mdns_publisher.cpp
new file mode 100644
index 00000000..ddb0437d
--- /dev/null
+++ b/src/android/mdns_publisher.cpp
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2024, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "android/mdns_publisher.hpp"
+
+namespace otbr {
+
+Mdns::Publisher *Mdns::Publisher::Create(Mdns::Publisher::StateCallback aCallback)
+{
+ return new Android::MdnsPublisher(std::move(aCallback));
+}
+
+namespace Android {
+
+using Status = ::ndk::ScopedAStatus;
+
+otbrError DnsErrorToOtbrErrorImpl(int32_t aError)
+{
+ return aError == 0 ? OTBR_ERROR_NONE : OTBR_ERROR_MDNS;
+}
+
+otbrError MdnsPublisher::DnsErrorToOtbrError(int32_t aError)
+{
+ return DnsErrorToOtbrErrorImpl(aError);
+}
+
+Status MdnsPublisher::NsdStatusReceiver::onSuccess()
+{
+ if (!mCallback.IsNull())
+ {
+ std::move(mCallback)(OTBR_ERROR_NONE);
+ }
+
+ return Status::ok();
+}
+
+void MdnsPublisher::SetINsdPublisher(std::shared_ptr<INsdPublisher> aINsdPublisher)
+{
+ otbrLogInfo("Set INsdPublisher %p", aINsdPublisher.get());
+
+ if (aINsdPublisher)
+ {
+ mNsdPublisher = std::move(aINsdPublisher);
+ mStateCallback(Mdns::Publisher::State::kReady);
+ }
+ else
+ {
+ Stop();
+ mNsdPublisher = std::move(aINsdPublisher);
+ }
+}
+
+Status MdnsPublisher::NsdStatusReceiver::onError(int aError)
+{
+ if (!mCallback.IsNull())
+ {
+ std::move(mCallback)(DnsErrorToOtbrErrorImpl(aError));
+ }
+
+ return Status::ok();
+}
+
+std::shared_ptr<MdnsPublisher::NsdStatusReceiver> CreateReceiver(Mdns::Publisher::ResultCallback aCallback)
+{
+ return ndk::SharedRefBase::make<MdnsPublisher::NsdStatusReceiver>(std::move(aCallback));
+}
+
+void DieForNotImplemented(const char *aFuncName)
+{
+ VerifyOrDie(false, (std::string(aFuncName) + " is not implemented").c_str());
+}
+
+otbrError MdnsPublisher::PublishServiceImpl(const std::string &aHostName,
+ const std::string &aName,
+ const std::string &aType,
+ const SubTypeList &aSubTypeList,
+ uint16_t aPort,
+ const TxtData &aTxtData,
+ ResultCallback &&aCallback)
+{
+ int32_t listenerId = AllocateListenerId();
+ TxtList txtList;
+ otbrError error = OTBR_ERROR_NONE;
+
+ std::vector<DnsTxtAttribute> txtAttributes;
+
+ VerifyOrExit(IsStarted(), error = OTBR_ERROR_MDNS);
+ if (mNsdPublisher == nullptr)
+ {
+ otbrLogWarning("No platform mDNS implementation registered!");
+ ExitNow(error = OTBR_ERROR_MDNS);
+ }
+
+ aCallback = HandleDuplicateServiceRegistration(aHostName, aName, aType, aSubTypeList, aPort, aTxtData,
+ std::move(aCallback));
+ VerifyOrExit(!aCallback.IsNull(), error = OTBR_ERROR_INVALID_STATE);
+
+ SuccessOrExit(error = DecodeTxtData(txtList, aTxtData.data(), aTxtData.size()));
+
+ for (const auto &txtEntry : txtList)
+ {
+ DnsTxtAttribute txtAttribute;
+
+ txtAttribute.name = txtEntry.mKey;
+ txtAttribute.value = txtEntry.mValue;
+ txtAttributes.push_back(std::move(txtAttribute));
+ }
+ AddServiceRegistration(MakeUnique<NsdServiceRegistration>(aHostName, aName, aType, aSubTypeList, aPort, aTxtData,
+ /* aCallback= */ nullptr, this, listenerId,
+ mNsdPublisher));
+
+ otbrLogInfo("Publishing service %s.%s listener ID = %d", aName.c_str(), aType.c_str(), listenerId);
+
+ mNsdPublisher->registerService(aHostName, aName, aType, aSubTypeList, aPort, txtAttributes,
+ CreateReceiver(std::move(aCallback)), listenerId);
+
+exit:
+ return error;
+}
+
+void MdnsPublisher::UnpublishService(const std::string &aName, const std::string &aType, ResultCallback &&aCallback)
+{
+ NsdServiceRegistration *serviceRegistration =
+ static_cast<NsdServiceRegistration *>(FindServiceRegistration(aName, aType));
+
+ VerifyOrExit(IsStarted(), std::move(aCallback)(OTBR_ERROR_MDNS));
+ if (mNsdPublisher != nullptr)
+ {
+ otbrLogWarning("No platform mDNS implementation registered!");
+ ExitNow(std::move(aCallback)(OTBR_ERROR_MDNS));
+ }
+ VerifyOrExit(serviceRegistration != nullptr, std::move(aCallback)(OTBR_ERROR_NONE));
+
+ serviceRegistration->mUnregisterReceiver = CreateReceiver(std::move(aCallback));
+ RemoveServiceRegistration(aName, aType, OTBR_ERROR_NONE);
+
+exit:
+ return;
+}
+
+otbrError MdnsPublisher::PublishHostImpl(const std::string &aName,
+ const AddressList &aAddresses,
+ ResultCallback &&aCallback)
+{
+ OTBR_UNUSED_VARIABLE(aName);
+ OTBR_UNUSED_VARIABLE(aAddresses);
+ OTBR_UNUSED_VARIABLE(aCallback);
+
+ DieForNotImplemented(__func__);
+
+ return OTBR_ERROR_MDNS;
+}
+
+void MdnsPublisher::UnpublishHost(const std::string &aName, ResultCallback &&aCallback)
+{
+ OTBR_UNUSED_VARIABLE(aName);
+ OTBR_UNUSED_VARIABLE(aCallback);
+
+ DieForNotImplemented(__func__);
+
+ return;
+}
+
+void MdnsPublisher::SubscribeService(const std::string &aType, const std::string &aInstanceName)
+{
+ OTBR_UNUSED_VARIABLE(aType);
+ OTBR_UNUSED_VARIABLE(aInstanceName);
+
+ DieForNotImplemented(__func__);
+}
+
+void MdnsPublisher::UnsubscribeService(const std::string &aType, const std::string &aInstanceName)
+{
+ OTBR_UNUSED_VARIABLE(aType);
+ OTBR_UNUSED_VARIABLE(aInstanceName);
+
+ DieForNotImplemented(__func__);
+}
+
+void MdnsPublisher::SubscribeHost(const std::string &aHostName)
+{
+ OTBR_UNUSED_VARIABLE(aHostName);
+
+ DieForNotImplemented(__func__);
+}
+
+void MdnsPublisher::UnsubscribeHost(const std::string &aHostName)
+{
+ OTBR_UNUSED_VARIABLE(aHostName);
+
+ DieForNotImplemented(__func__);
+}
+
+void MdnsPublisher::OnServiceResolveFailedImpl(const std::string &aType,
+ const std::string &aInstanceName,
+ int32_t aErrorCode)
+{
+ OTBR_UNUSED_VARIABLE(aType);
+ OTBR_UNUSED_VARIABLE(aInstanceName);
+ OTBR_UNUSED_VARIABLE(aErrorCode);
+
+ DieForNotImplemented(__func__);
+}
+
+void MdnsPublisher::OnHostResolveFailedImpl(const std::string &aHostName, int32_t aErrorCode)
+{
+ OTBR_UNUSED_VARIABLE(aHostName);
+ OTBR_UNUSED_VARIABLE(aErrorCode);
+
+ DieForNotImplemented(__func__);
+}
+
+int32_t MdnsPublisher::AllocateListenerId(void)
+{
+ if (mNextListenerId == std::numeric_limits<int32_t>::max())
+ {
+ mNextListenerId = 0;
+ }
+ return mNextListenerId++;
+}
+
+MdnsPublisher::NsdServiceRegistration::~NsdServiceRegistration(void)
+{
+ VerifyOrExit(mPublisher->IsStarted() && mNsdPublisher != nullptr);
+
+ otbrLogInfo("Unpublishing service %s.%s listener ID = %d", mName.c_str(), mType.c_str(), mListenerId);
+
+ if (!mUnregisterReceiver)
+ {
+ mUnregisterReceiver = CreateReceiver([](int) {});
+ }
+
+ mNsdPublisher->unregister(mUnregisterReceiver, mListenerId);
+
+exit:
+ return;
+}
+
+} // namespace Android
+} // namespace otbr
diff --git a/src/android/mdns_publisher.hpp b/src/android/mdns_publisher.hpp
new file mode 100644
index 00000000..97840fb7
--- /dev/null
+++ b/src/android/mdns_publisher.hpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2024, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef OTBR_ANDROID_MDNS_PUBLISHER_HPP_
+#define OTBR_ANDROID_MDNS_PUBLISHER_HPP_
+
+#include "mdns/mdns.hpp"
+
+#include <aidl/com/android/server/thread/openthread/BnNsdStatusReceiver.h>
+#include <aidl/com/android/server/thread/openthread/DnsTxtAttribute.h>
+#include <aidl/com/android/server/thread/openthread/INsdPublisher.h>
+
+namespace otbr {
+namespace Android {
+using aidl::com::android::server::thread::openthread::BnNsdStatusReceiver;
+using aidl::com::android::server::thread::openthread::DnsTxtAttribute;
+using aidl::com::android::server::thread::openthread::INsdPublisher;
+
+class MdnsPublisher : public Mdns::Publisher
+{
+public:
+ explicit MdnsPublisher(Publisher::StateCallback aCallback)
+ {
+ mNextListenerId = 0;
+ mStateCallback = std::move(aCallback);
+ }
+
+ ~MdnsPublisher(void) { Stop(); }
+
+ // In this Publisher implementation, SetINsdPublisher() does the job to start/stop the Publisher. That's because we
+ // want to ensure ot-daemon won't do any mDNS operations when Thread is disabled.
+ void SetINsdPublisher(std::shared_ptr<INsdPublisher> aINsdPublisher);
+
+ otbrError Start(void) override { return OTBR_ERROR_MDNS; }
+
+ void Stop(void) override
+ {
+ mServiceRegistrations.clear();
+ mHostRegistrations.clear();
+ mStateCallback(Mdns::Publisher::State::kIdle);
+ }
+
+ bool IsStarted(void) const override { return mNsdPublisher != nullptr; }
+
+ void UnpublishService(const std::string &aName, const std::string &aType, ResultCallback &&aCallback) override;
+
+ void UnpublishHost(const std::string &aName, ResultCallback &&aCallback) override;
+
+ void SubscribeService(const std::string &aType, const std::string &aInstanceName) override;
+
+ void UnsubscribeService(const std::string &aType, const std::string &aInstanceName) override;
+
+ void SubscribeHost(const std::string &aHostName) override;
+
+ void UnsubscribeHost(const std::string &aHostName) override;
+
+ class NsdStatusReceiver : public BnNsdStatusReceiver
+ {
+ public:
+ explicit NsdStatusReceiver(Mdns::Publisher::ResultCallback aCallback)
+ : mCallback(std::move(aCallback))
+ {
+ }
+
+ ::ndk::ScopedAStatus onSuccess(void) override;
+
+ ::ndk::ScopedAStatus onError(int aError) override;
+
+ private:
+ Mdns::Publisher::ResultCallback mCallback;
+ };
+
+protected:
+ otbrError PublishServiceImpl(const std::string &aHostName,
+ const std::string &aName,
+ const std::string &aType,
+ const SubTypeList &aSubTypeList,
+ uint16_t aPort,
+ const TxtData &aTxtData,
+ ResultCallback &&aCallback) override;
+
+ otbrError PublishHostImpl(const std::string &aName, const AddressList &aAddresses, ResultCallback &&aCallback);
+
+ void OnServiceResolveFailedImpl(const std::string &aType, const std::string &aInstanceName, int32_t aErrorCode);
+
+ void OnHostResolveFailedImpl(const std::string &aHostName, int32_t aErrorCode);
+
+ otbrError DnsErrorToOtbrError(int32_t aError);
+
+private:
+ class NsdServiceRegistration : public ServiceRegistration
+ {
+ public:
+ NsdServiceRegistration(const std::string &aHostName,
+ const std::string &aName,
+ const std::string &aType,
+ const SubTypeList &aSubTypeList,
+ uint16_t aPort,
+ const TxtData &aTxtData,
+ ResultCallback &&aCallback,
+ MdnsPublisher *aPublisher,
+ int32_t aListenerId,
+ std::shared_ptr<INsdPublisher> aINsdPublisher)
+ : ServiceRegistration(aHostName,
+ aName,
+ aType,
+ aSubTypeList,
+ aPort,
+ aTxtData,
+ std::move(aCallback),
+ aPublisher)
+ , mListenerId(aListenerId)
+ , mNsdPublisher(std::move(aINsdPublisher))
+
+ {
+ }
+
+ ~NsdServiceRegistration(void) override;
+
+ const int32_t mListenerId;
+ std::shared_ptr<NsdStatusReceiver> mUnregisterReceiver;
+
+ private:
+ std::shared_ptr<INsdPublisher> mNsdPublisher;
+ };
+
+ int32_t AllocateListenerId(void);
+
+ StateCallback mStateCallback;
+ int32_t mNextListenerId;
+ std::shared_ptr<INsdPublisher> mNsdPublisher = nullptr;
+};
+
+} // namespace Android
+} // namespace otbr
+
+#endif // OTBR_ANDROID_MDNS_PUBLISHER_HPP_
diff --git a/src/android/otdaemon_fuzzer.cpp b/src/android/otdaemon_fuzzer.cpp
index 9dd84ab8..be0bc347 100644
--- a/src/android/otdaemon_fuzzer.cpp
+++ b/src/android/otdaemon_fuzzer.cpp
@@ -36,8 +36,8 @@ using otbr::Android::OtDaemonServer;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
- otbr::Ncp::ControllerOpenThread ncp("", {}, "", true, false);
- auto service = ndk::SharedRefBase::make<OtDaemonServer>(ncp);
+ otbr::Application app("", {}, {}, false, "", 0);
+ auto service = ndk::SharedRefBase::make<OtDaemonServer>(app);
fuzzService(service->asBinder().get(), FuzzedDataProvider(data, size));
return 0;
}
diff --git a/src/android/otdaemon_server.cpp b/src/android/otdaemon_server.cpp
index 4e695be9..254cc6fd 100644
--- a/src/android/otdaemon_server.cpp
+++ b/src/android/otdaemon_server.cpp
@@ -54,7 +54,7 @@ namespace vendor {
std::shared_ptr<VendorServer> VendorServer::newInstance(Application &aApplication)
{
- return ndk::SharedRefBase::make<Android::OtDaemonServer>(aApplication.GetNcp());
+ return ndk::SharedRefBase::make<Android::OtDaemonServer>(aApplication);
}
} // namespace vendor
@@ -96,8 +96,10 @@ static Ipv6AddressInfo ConvertToAddressInfo(const otIp6AddressInfo &aAddressInfo
return addrInfo;
}
-OtDaemonServer::OtDaemonServer(otbr::Ncp::ControllerOpenThread &aNcp)
- : mNcp(aNcp)
+OtDaemonServer::OtDaemonServer(Application &aApplication)
+ : mNcp(aApplication.GetNcp())
+ , mBorderAgent(aApplication.GetBorderAgent())
+ , mMdnsPublisher(static_cast<MdnsPublisher &>(aApplication.GetBorderAgent().GetPublisher()))
, mBorderRouterConfiguration()
{
mClientDeathRecipient =
@@ -316,11 +318,14 @@ void OtDaemonServer::Process(const MainloopContext &aMainloop)
}
}
-Status OtDaemonServer::initialize(const ScopedFileDescriptor &aTunFd, const bool enabled)
+Status OtDaemonServer::initialize(const ScopedFileDescriptor &aTunFd,
+ const bool enabled,
+ const std::shared_ptr<INsdPublisher> &aINsdPublisher)
{
otbrLogInfo("OT daemon is initialized by system server (tunFd=%d, enabled=%s)",
aTunFd.get(), enabled ? "true" : "false");
mTunFd = aTunFd.dup();
+ mINsdPublisher = aINsdPublisher;
if (enabled)
{
@@ -342,6 +347,18 @@ void OtDaemonServer::updateThreadEnabledState(const int enabled, const std::shar
aReceiver->onSuccess();
}
+ switch (enabled)
+ {
+ case OT_STATE_ENABLED:
+ mMdnsPublisher.SetINsdPublisher(mINsdPublisher);
+ break;
+ case OT_STATE_DISABLED:
+ mMdnsPublisher.SetINsdPublisher(nullptr);
+ break;
+ default:
+ break;
+ }
+
if (mCallback != nullptr)
{
mCallback->onThreadEnabledChanged(mThreadEnabled);
@@ -777,5 +794,6 @@ void OtDaemonServer::PushTelemetryIfConditionMatch()
exit:
return;
}
+
} // namespace Android
} // namespace otbr
diff --git a/src/android/otdaemon_server.hpp b/src/android/otdaemon_server.hpp
index 9a069ab6..60a35151 100644
--- a/src/android/otdaemon_server.hpp
+++ b/src/android/otdaemon_server.hpp
@@ -34,11 +34,13 @@
#include <vector>
#include <aidl/com/android/server/thread/openthread/BnOtDaemon.h>
+#include <aidl/com/android/server/thread/openthread/INsdPublisher.h>
#include <aidl/com/android/server/thread/openthread/IOtDaemon.h>
#include <openthread/instance.h>
#include <openthread/ip6.h>
#include "agent/vendor.hpp"
+#include "android/mdns_publisher.hpp"
#include "common/mainloop.hpp"
#include "common/time.hpp"
#include "ncp/ncp_openthread.hpp"
@@ -51,6 +53,7 @@ using ScopedFileDescriptor = ::ndk::ScopedFileDescriptor;
using Status = ::ndk::ScopedAStatus;
using aidl::com::android::server::thread::openthread::BnOtDaemon;
using aidl::com::android::server::thread::openthread::BorderRouterConfigurationParcel;
+using aidl::com::android::server::thread::openthread::INsdPublisher;
using aidl::com::android::server::thread::openthread::IOtDaemon;
using aidl::com::android::server::thread::openthread::IOtDaemonCallback;
using aidl::com::android::server::thread::openthread::IOtStatusReceiver;
@@ -60,7 +63,7 @@ using aidl::com::android::server::thread::openthread::OtDaemonState;
class OtDaemonServer : public BnOtDaemon, public MainloopProcessor, public vendor::VendorServer
{
public:
- explicit OtDaemonServer(otbr::Ncp::ControllerOpenThread &aNcp);
+ explicit OtDaemonServer(Application &aApplication);
virtual ~OtDaemonServer(void) = default;
// Disallow copy and assign.
@@ -86,7 +89,9 @@ private:
// Implements IOtDaemon.aidl
- Status initialize(const ScopedFileDescriptor &aTunFd, const bool enabled) override;
+ Status initialize(const ScopedFileDescriptor &aTunFd,
+ const bool enabled,
+ const std::shared_ptr<INsdPublisher> &aNsdPublisher) override;
Status setThreadEnabled(const bool enabled, const std::shared_ptr<IOtStatusReceiver> &aReceiver) override;
Status registerStateCallback(const std::shared_ptr<IOtDaemonCallback> &aCallback, int64_t listenerId) override;
bool isAttached(void);
@@ -120,6 +125,9 @@ private:
int mThreadEnabled = IOtDaemon::OT_STATE_DISABLED;
otbr::Ncp::ControllerOpenThread &mNcp;
+ otbr::BorderAgent &mBorderAgent;
+ MdnsPublisher &mMdnsPublisher;
+ std::shared_ptr<INsdPublisher> mINsdPublisher;
TaskRunner mTaskRunner;
ScopedFileDescriptor mTunFd;
OtDaemonState mState;
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 18dd4009..d97af20b 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
@@ -44,6 +44,7 @@ import android.os.test.TestLooper;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.server.thread.openthread.INsdPublisher;
import com.android.server.thread.openthread.IOtDaemonCallback;
import com.android.server.thread.openthread.IOtStatusReceiver;
import com.android.server.thread.openthread.OtDaemonState;
@@ -84,6 +85,7 @@ public final class FakeOtDaemonTest {
private FakeOtDaemon mFakeOtDaemon;
private TestLooper mTestLooper;
@Mock private ParcelFileDescriptor mMockTunFd;
+ @Mock private INsdPublisher mMockNsdPublisher;
@Before
public void setUp() {
@@ -95,14 +97,21 @@ public final class FakeOtDaemonTest {
@Test
public void initialize_succeed_tunFdIsSet() throws Exception {
- mFakeOtDaemon.initialize(mMockTunFd, true);
+ mFakeOtDaemon.initialize(mMockTunFd, true, mMockNsdPublisher);
assertThat(mFakeOtDaemon.getTunFd()).isEqualTo(mMockTunFd);
}
@Test
+ public void initialize_succeed_NsdPublisherIsSet() throws Exception {
+ mFakeOtDaemon.initialize(mMockTunFd, true, mMockNsdPublisher);
+
+ assertThat(mFakeOtDaemon.getNsdPublisher()).isEqualTo(mMockNsdPublisher);
+ }
+
+ @Test
public void registerStateCallback_noStateChange_callbackIsInvoked() throws Exception {
- mFakeOtDaemon.initialize(mMockTunFd, true);
+ mFakeOtDaemon.initialize(mMockTunFd, true, mMockNsdPublisher);
final AtomicReference<OtDaemonState> stateRef = new AtomicReference<>();
final AtomicLong listenerIdRef = new AtomicLong();