diff options
author | Handa Wang <handaw@google.com> | 2023-12-21 13:56:05 +0800 |
---|---|---|
committer | Handa Wang <handaw@google.com> | 2024-02-02 06:27:02 +0000 |
commit | 803e06be599c1bde0fb6d18c2c1a2bea7bfe8cf2 (patch) | |
tree | b6e62fb49abec8646deba4d5504e990acb81dd9c | |
parent | 9602b96ba439d53f0c15b7940c9e957d84220f61 (diff) | |
download | ot-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.bp | 30 | ||||
-rw-r--r-- | src/android/aidl/com/android/server/thread/openthread/DnsTxtAttribute.aidl | 37 | ||||
-rw-r--r-- | src/android/aidl/com/android/server/thread/openthread/INsdPublisher.aidl | 75 | ||||
-rw-r--r-- | src/android/aidl/com/android/server/thread/openthread/INsdStatusReceiver.aidl | 38 | ||||
-rw-r--r-- | src/android/aidl/com/android/server/thread/openthread/IOtDaemon.aidl | 8 | ||||
-rw-r--r-- | src/android/java/com/android/server/thread/openthread/testing/FakeOtDaemon.java | 16 | ||||
-rw-r--r-- | src/android/mdns_publisher.cpp | 265 | ||||
-rw-r--r-- | src/android/mdns_publisher.hpp | 162 | ||||
-rw-r--r-- | src/android/otdaemon_fuzzer.cpp | 4 | ||||
-rw-r--r-- | src/android/otdaemon_server.cpp | 26 | ||||
-rw-r--r-- | src/android/otdaemon_server.hpp | 12 | ||||
-rw-r--r-- | tests/android/java/com/android/server/thread/openthread/testing/FakeOtDaemonTest.java | 13 |
12 files changed, 659 insertions, 27 deletions
@@ -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(); |