/*
 *    Copyright (c) 2023, 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 <linux/in.h>
#define OTBR_LOG_TAG "BINDER"

#include "android/otdaemon_server.hpp"

#include <algorithm>
#include <net/if.h>
#include <random>
#include <string.h>

#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <openthread/border_agent.h>
#include <openthread/border_router.h>
#include <openthread/cli.h>
#include <openthread/dnssd_server.h>
#include <openthread/icmp6.h>
#include <openthread/ip6.h>
#include <openthread/link.h>
#include <openthread/nat64.h>
#include <openthread/openthread-system.h>
#include <openthread/srp_server.h>
#include <openthread/thread_ftd.h>
#include <openthread/platform/infra_if.h>
#include <openthread/platform/radio.h>

#include "agent/vendor.hpp"
#include "android/android_rcp_host.hpp"
#include "android/common_utils.hpp"
#include "android/otdaemon_telemetry.hpp"
#include "common/code_utils.hpp"
#include "host/thread_host.hpp"

#define BYTE_ARR_END(arr) ((arr) + sizeof(arr))

namespace otbr {

namespace vendor {

std::shared_ptr<VendorServer> VendorServer::newInstance(Application &aApplication)
{
    return ndk::SharedRefBase::make<Android::OtDaemonServer>(
        static_cast<otbr::Host::RcpHost &>(aApplication.GetHost()),
        static_cast<otbr::Android::MdnsPublisher &>(aApplication.GetPublisher()), aApplication.GetBorderAgent(),
        aApplication.GetAdvertisingProxy(), [&aApplication]() {
            aApplication.Deinit();
            aApplication.Init();
        });
}

} // namespace vendor

} // namespace otbr

namespace otbr {
namespace Android {

static const char       OTBR_SERVICE_NAME[] = "ot_daemon";
static constexpr size_t kMaxIp6Size         = 1280;

static const char *ThreadEnabledStateToString(int enabledState)
{
    switch (enabledState)
    {
    case IOtDaemon::OT_STATE_ENABLED:
        return "ENABLED";
    case IOtDaemon::OT_STATE_DISABLED:
        return "DISABLED";
    case IOtDaemon::OT_STATE_DISABLING:
        return "DISABLING";
    default:
        assert(false);
        return "UNKNOWN";
    }
}

OtDaemonServer *OtDaemonServer::sOtDaemonServer = nullptr;

OtDaemonServer::OtDaemonServer(otbr::Host::RcpHost    &aRcpHost,
                               otbr::Mdns::Publisher  &aMdnsPublisher,
                               otbr::BorderAgent      &aBorderAgent,
                               otbr::AdvertisingProxy &aAdvProxy,
                               ResetThreadHandler      aResetThreadHandler)
    : mHost(aRcpHost)
    , mAndroidHost(CreateAndroidHost())
    , mMdnsPublisher(static_cast<MdnsPublisher &>(aMdnsPublisher))
    , mBorderAgent(aBorderAgent)
    , mAdvProxy(aAdvProxy)
    , mResetThreadHandler(aResetThreadHandler)
    , mServiceRegistered(false)
{
    mClientDeathRecipient =
        ::ndk::ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(&OtDaemonServer::BinderDeathCallback));
    sOtDaemonServer = this;
}

void OtDaemonServer::Init(void)
{
    if (!mServiceRegistered)
    {
        binder_exception_t exp = AServiceManager_registerLazyService(asBinder().get(), OTBR_SERVICE_NAME);
        SuccessOrDie(exp, "Failed to register OT daemon binder service");
        mServiceRegistered = true;
    }

    assert(GetOtInstance() != nullptr);

    mHost.AddThreadStateChangedCallback([this](otChangedFlags aFlags) { StateCallback(aFlags); });
    otIp6SetAddressCallback(GetOtInstance(), OtDaemonServer::AddressCallback, this);
    otIp6SetReceiveCallback(GetOtInstance(), OtDaemonServer::ReceiveCallback, this);
    otBackboneRouterSetMulticastListenerCallback(GetOtInstance(), OtDaemonServer::HandleBackboneMulticastListenerEvent,
                                                 this);
    otIp6SetReceiveFilterEnabled(GetOtInstance(), true);
    otNat64SetReceiveIp4Callback(GetOtInstance(), &OtDaemonServer::ReceiveCallback, this);
    mHost.AddEphemeralKeyStateChangedCallback([this](otBorderAgentEphemeralKeyState aEpskcState, uint16_t aPort) {
        HandleEpskcStateChanged(aEpskcState, aPort);
    });

    otBorderAgentEphemeralKeySetEnabled(GetOtInstance(), true);
    otSysUpstreamDnsServerSetResolvConfEnabled(false);

    mTaskRunner.Post(kTelemetryCheckInterval, [this]() { PushTelemetryIfConditionMatch(); });
}

void OtDaemonServer::BinderDeathCallback(void *aBinderServer)
{
    OtDaemonServer *thisServer = static_cast<OtDaemonServer *>(aBinderServer);

    otbrLogCrit("system_server is dead, removing configs and callbacks...");

    thisServer->mMeshcopTxts   = {};
    thisServer->mINsdPublisher = nullptr;

    // Note that the INsdPublisher reference is held in MdnsPublisher
    thisServer->mMdnsPublisher.SetINsdPublisher(nullptr);

    thisServer->mCallback = nullptr;
    thisServer->mTunFd.set(-1); // the original FD will be closed automatically
}

void OtDaemonServer::StateCallback(otChangedFlags aFlags)
{
    std::vector<OnMeshPrefixConfig> onMeshPrefixes;

    assert(GetOtInstance() != nullptr);

    if (RefreshOtDaemonState(aFlags))
    {
        if (mCallback == nullptr)
        {
            otbrLogWarning("Ignoring OT state changes: callback is not set");
        }
        else
        {
            NotifyStateChanged(/* aListenerId*/ -1);
        }
    }

    if (aFlags & OT_CHANGED_THREAD_BACKBONE_ROUTER_STATE)
    {
        if (mCallback == nullptr)
        {
            otbrLogWarning("Ignoring OT backbone router state changes: callback is not set");
        }
        else
        {
            mCallback->onBackboneRouterStateChanged(GetBackboneRouterState());
        }
    }

    if ((aFlags & OT_CHANGED_THREAD_NETDATA) && RefreshOnMeshPrefixes())
    {
        if (mCallback == nullptr)
        {
            otbrLogWarning("Ignoring OT netdata changes: callback is not set");
        }
        else
        {
            onMeshPrefixes.assign(mOnMeshPrefixes.begin(), mOnMeshPrefixes.end());
            mCallback->onPrefixChanged(onMeshPrefixes);
        }
    }
}

bool OtDaemonServer::RefreshOnMeshPrefixes()
{
    std::set<OnMeshPrefixConfig> onMeshPrefixConfigs;
    otNetworkDataIterator        iterator = OT_NETWORK_DATA_ITERATOR_INIT;
    otBorderRouterConfig         config;
    bool                         rv = false;

    VerifyOrExit(GetOtInstance() != nullptr, otbrLogWarning("Can't get on mesh prefixes: OT is not initialized"));

    while (otNetDataGetNextOnMeshPrefix(GetOtInstance(), &iterator, &config) == OT_ERROR_NONE)
    {
        OnMeshPrefixConfig onMeshPrefixConfig;

        onMeshPrefixConfig.prefix.assign(std::begin(config.mPrefix.mPrefix.mFields.m8),
                                         std::end(config.mPrefix.mPrefix.mFields.m8));
        onMeshPrefixConfig.prefixLength = config.mPrefix.mLength;
        onMeshPrefixConfigs.insert(onMeshPrefixConfig);
    }

    if (mOnMeshPrefixes != onMeshPrefixConfigs)
    {
        mOnMeshPrefixes = std::move(onMeshPrefixConfigs);
        rv              = true;
    }
exit:
    return rv;
}

Ipv6AddressInfo OtDaemonServer::ConvertToAddressInfo(const otNetifAddress &aAddress)
{
    Ipv6AddressInfo addrInfo;
    otIp6Prefix     addressPrefix{aAddress.mAddress, aAddress.mPrefixLength};

    addrInfo.address.assign(std::begin(aAddress.mAddress.mFields.m8), std::end(aAddress.mAddress.mFields.m8));
    addrInfo.prefixLength = aAddress.mPrefixLength;
    addrInfo.isPreferred  = aAddress.mPreferred;
    addrInfo.isMeshLocal  = aAddress.mMeshLocal;
    addrInfo.isMeshLocalEid =
        (memcmp(&aAddress.mAddress, otThreadGetMeshLocalEid(GetOtInstance()), sizeof(aAddress.mAddress)) == 0);
    addrInfo.isActiveOmr = otNetDataContainsOmrPrefix(GetOtInstance(), &addressPrefix);
    return addrInfo;
}

Ipv6AddressInfo OtDaemonServer::ConvertToAddressInfo(const otNetifMulticastAddress &aAddress)
{
    Ipv6AddressInfo addrInfo;

    addrInfo.address.assign(std::begin(aAddress.mAddress.mFields.m8), std::end(aAddress.mAddress.mFields.m8));
    return addrInfo;
}

void OtDaemonServer::AddressCallback(const otIp6AddressInfo *aAddressInfo, bool aIsAdded, void *aBinderServer)
{
    OT_UNUSED_VARIABLE(aAddressInfo);
    OT_UNUSED_VARIABLE(aIsAdded);
    OtDaemonServer                *thisServer = static_cast<OtDaemonServer *>(aBinderServer);
    std::vector<Ipv6AddressInfo>   addrInfoList;
    const otNetifAddress          *unicastAddrs   = otIp6GetUnicastAddresses(thisServer->GetOtInstance());
    const otNetifMulticastAddress *multicastAddrs = otIp6GetMulticastAddresses(thisServer->GetOtInstance());

    for (const otNetifAddress *addr = unicastAddrs; addr != nullptr; addr = addr->mNext)
    {
        addrInfoList.push_back(thisServer->ConvertToAddressInfo(*addr));
    }
    for (const otNetifMulticastAddress *maddr = multicastAddrs; maddr != nullptr; maddr = maddr->mNext)
    {
        addrInfoList.push_back(thisServer->ConvertToAddressInfo(*maddr));
    }
    if (thisServer->mCallback != nullptr)
    {
        thisServer->mCallback->onAddressChanged(addrInfoList);
    }
    else
    {
        otbrLogWarning("OT daemon callback is not set");
    }
}

void OtDaemonServer::ReceiveCallback(otMessage *aMessage, void *aBinderServer)
{
    static_cast<OtDaemonServer *>(aBinderServer)->ReceiveCallback(aMessage);
}

// TODO: b/291053118 - We should reuse the same code in openthread/src/posix/platform/netif.cpp
void OtDaemonServer::ReceiveCallback(otMessage *aMessage)
{
    char     packet[kMaxIp6Size];
    uint16_t length = otMessageGetLength(aMessage);
    int      fd     = mTunFd.get();

    VerifyOrExit(fd != -1, otbrLogWarning("Ignoring egress packet: invalid tunnel FD"));

    if (otMessageRead(aMessage, 0, packet, sizeof(packet)) != length)
    {
        otbrLogWarning("Failed to read packet from otMessage");
        ExitNow();
    }

    if (write(fd, packet, length) != length)
    {
        otbrLogWarning("Failed to send packet over tunnel interface: %s", strerror(errno));
    }

exit:
    otMessageFree(aMessage);
}

static constexpr uint8_t kIpVersion4 = 4;
static constexpr uint8_t kIpVersion6 = 6;

// TODO: b/291053118 - We should reuse the same code in openthread/src/posix/platform/netif.cpp
static uint8_t getIpVersion(const uint8_t *data)
{
    assert(data != nullptr);

    // Mute compiler warnings.
    OT_UNUSED_VARIABLE(kIpVersion4);
    OT_UNUSED_VARIABLE(kIpVersion6);

    return (static_cast<uint8_t>(data[0]) >> 4) & 0x0F;
}

// TODO: b/291053118 - we should use a shared library with ot-posix to handle packet translations
// between the tunnel interface and Thread.
void OtDaemonServer::TransmitCallback(void)
{
    char              packet[kMaxIp6Size];
    ssize_t           length;
    otMessage        *message = nullptr;
    otError           error   = OT_ERROR_NONE;
    otMessageSettings settings;
    int               fd = mTunFd.get();
    bool              isIp4;

    assert(GetOtInstance() != nullptr);

    VerifyOrExit(fd != -1);

    length = read(fd, packet, sizeof(packet));

    if (length == -1)
    {
        otbrLogWarning("Failed to read packet from tunnel interface: %s", strerror(errno));
        ExitNow();
    }
    else if (length == 0)
    {
        otbrLogWarning("Unexpected EOF on the tunnel FD");
        ExitNow();
    }

    VerifyOrExit(GetOtInstance() != nullptr, otbrLogWarning("Ignoring tunnel packet: OT is not initialized"));

    settings.mLinkSecurityEnabled = (otThreadGetDeviceRole(GetOtInstance()) != OT_DEVICE_ROLE_DISABLED);
    settings.mPriority            = OT_MESSAGE_PRIORITY_LOW;

    isIp4   = (getIpVersion(reinterpret_cast<uint8_t *>(packet)) == kIpVersion4);
    message = isIp4 ? otIp4NewMessage(GetOtInstance(), &settings) : otIp6NewMessage(GetOtInstance(), &settings);
    VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
    otMessageSetOrigin(message, OT_MESSAGE_ORIGIN_HOST_UNTRUSTED);

    SuccessOrExit(error = otMessageAppend(message, packet, static_cast<uint16_t>(length)));

    error   = isIp4 ? otNat64Send(GetOtInstance(), message) : otIp6Send(GetOtInstance(), message);
    message = nullptr;

exit:
    if (message != nullptr)
    {
        otMessageFree(message);
    }

    if (error != OT_ERROR_NONE)
    {
        if (error == OT_ERROR_DROP)
        {
            otbrLogInfo("Dropped tunnel packet (length=%d)", length);
        }
        else
        {
            otbrLogWarning("Failed to transmit tunnel packet: %s", otThreadErrorToString(error));
        }
    }
}

void OtDaemonServer::HandleEpskcStateChanged(otBorderAgentEphemeralKeyState aEpskcState, uint16_t aPort)
{
    mState.ephemeralKeyState = GetEphemeralKeyState(aEpskcState);
    if (mState.ephemeralKeyState == OT_EPHEMERAL_KEY_DISABLED)
    {
        mState.ephemeralKeyLifetimeMillis = 0;
    }
    else
    {
        mState.ephemeralKeyLifetimeMillis =
            mEphemeralKeyExpiryMillis -
            std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch())
                .count();
    }

    NotifyStateChanged(/* aListenerId*/ -1);
}

void OtDaemonServer::NotifyStateChanged(int64_t aListenerId)
{
    if (mCallback != nullptr)
    {
        mCallback->onStateChanged(mState, aListenerId);
    }
}

int OtDaemonServer::GetEphemeralKeyState(otBorderAgentEphemeralKeyState aEpskcState)
{
    int ephemeralKeyState;

    switch (aEpskcState)
    {
    case OT_BORDER_AGENT_STATE_STARTED:
        ephemeralKeyState = OT_EPHEMERAL_KEY_ENABLED;
        break;
    case OT_BORDER_AGENT_STATE_CONNECTED:
    case OT_BORDER_AGENT_STATE_ACCEPTED:
        ephemeralKeyState = OT_EPHEMERAL_KEY_IN_USE;
        break;
    case OT_BORDER_AGENT_STATE_DISABLED:
    case OT_BORDER_AGENT_STATE_STOPPED:
        ephemeralKeyState = OT_EPHEMERAL_KEY_DISABLED;
        break;
    }

    return ephemeralKeyState;
}

BackboneRouterState OtDaemonServer::GetBackboneRouterState()
{
    BackboneRouterState                       state;
    otBackboneRouterState                     bbrState;
    otBackboneRouterMulticastListenerInfo     info;
    otBackboneRouterMulticastListenerIterator iter = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT;
    state.listeningAddresses                       = std::vector<std::string>();

    VerifyOrExit(GetOtInstance() != nullptr, otbrLogWarning("Can't get bbr state: OT is not initialized"));

    bbrState = otBackboneRouterGetState(GetOtInstance());
    switch (bbrState)
    {
    case OT_BACKBONE_ROUTER_STATE_DISABLED:
    case OT_BACKBONE_ROUTER_STATE_SECONDARY:
        state.multicastForwardingEnabled = false;
        break;
    case OT_BACKBONE_ROUTER_STATE_PRIMARY:
        state.multicastForwardingEnabled = true;
        break;
    }

    while (otBackboneRouterMulticastListenerGetNext(GetOtInstance(), &iter, &info) == OT_ERROR_NONE)
    {
        char string[OT_IP6_ADDRESS_STRING_SIZE];

        otIp6AddressToString(&info.mAddress, string, sizeof(string));
        state.listeningAddresses.push_back(string);
    }

exit:
    return state;
}

void OtDaemonServer::HandleBackboneMulticastListenerEvent(void                                  *aBinderServer,
                                                          otBackboneRouterMulticastListenerEvent aEvent,
                                                          const otIp6Address                    *aAddress)
{
    OtDaemonServer *thisServer = static_cast<OtDaemonServer *>(aBinderServer);
    char            addressString[OT_IP6_ADDRESS_STRING_SIZE];

    otIp6AddressToString(aAddress, addressString, sizeof(addressString));

    otbrLogInfo("Multicast forwarding address changed, %s is %s", addressString,
                (aEvent == OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ADDED) ? "added" : "removed");

    if (thisServer->mCallback == nullptr)
    {
        otbrLogWarning("Ignoring OT multicast listener event: callback is not set");
        ExitNow();
    }
    thisServer->mCallback->onBackboneRouterStateChanged(thisServer->GetBackboneRouterState());

exit:
    return;
}

otInstance *OtDaemonServer::GetOtInstance()
{
    return mHost.GetInstance();
}

void OtDaemonServer::Update(MainloopContext &aMainloop)
{
    int fd = mTunFd.get();

    if (fd != -1)
    {
        FD_SET(fd, &aMainloop.mReadFdSet);
        aMainloop.mMaxFd = std::max(aMainloop.mMaxFd, fd);
    }
}

void OtDaemonServer::Process(const MainloopContext &aMainloop)
{
    int fd = mTunFd.get();

    if (fd != -1 && FD_ISSET(fd, &aMainloop.mReadFdSet))
    {
        TransmitCallback();
    }
}

std::unique_ptr<AndroidThreadHost> OtDaemonServer::CreateAndroidHost(void)
{
    std::unique_ptr<AndroidThreadHost> host;

    switch (mHost.GetCoprocessorType())
    {
    case OT_COPROCESSOR_RCP:
        host = std::make_unique<AndroidRcpHost>(static_cast<otbr::Host::RcpHost &>(mHost));
        break;

    case OT_COPROCESSOR_NCP:
    default:
        DieNow("Unknown coprocessor type!");
        break;
    }

    return host;
}

Status OtDaemonServer::initialize(const bool                                aEnabled,
                                  const OtDaemonConfiguration              &aConfiguration,
                                  const ScopedFileDescriptor               &aTunFd,
                                  const std::shared_ptr<INsdPublisher>     &aINsdPublisher,
                                  const MeshcopTxtAttributes               &aMeshcopTxts,
                                  const std::string                        &aCountryCode,
                                  const bool                                aTrelEnabled,
                                  const std::shared_ptr<IOtDaemonCallback> &aCallback)
{
    VerifyOrExit(mHost.IsInitialized());

    otbrLogInfo("OT daemon is initialized by system server (enabled=%s, tunFd=%d)", (aEnabled ? "true" : "false"),
                aTunFd.get());

    // The copy constructor of `ScopedFileDescriptor` is deleted. It is unable to pass the `aTunFd`
    // to the lambda function. The processing method of `aTunFd` doesn't call OpenThread functions,
    // we can process `aTunFd` directly in front of the task.
    mTunFd = aTunFd.dup();

    mINsdPublisher = aINsdPublisher;
    mMeshcopTxts   = aMeshcopTxts;

    mTaskRunner.Post(
        [aEnabled, aConfiguration, aINsdPublisher, aMeshcopTxts, aCallback, aCountryCode, aTrelEnabled, this]() {
            initializeInternal(aEnabled, aConfiguration, mINsdPublisher, mMeshcopTxts, aCountryCode, aTrelEnabled,
                               aCallback);
        });

exit:
    return Status::ok();
}

void OtDaemonServer::initializeInternal(const bool                                aEnabled,
                                        const OtDaemonConfiguration              &aConfiguration,
                                        const std::shared_ptr<INsdPublisher>     &aINsdPublisher,
                                        const MeshcopTxtAttributes               &aMeshcopTxts,
                                        const std::string                        &aCountryCode,
                                        const bool                                aTrelEnabled,
                                        const std::shared_ptr<IOtDaemonCallback> &aCallback)
{
    std::string              instanceName = aMeshcopTxts.vendorName + " " + aMeshcopTxts.modelName;
    Mdns::Publisher::TxtList nonStandardTxts;
    otbrError                error;

    mAndroidHost->SetConfiguration(aConfiguration, nullptr /* aReceiver */);

    if (aConfiguration.countryCodeEnabled)
    {
        setCountryCodeInternal(aCountryCode, nullptr /* aReceiver */);
    }
    registerStateCallbackInternal(aCallback, -1 /* listenerId */);

    mMdnsPublisher.SetINsdPublisher(aINsdPublisher);
    mAdvProxy.SetAllowMlEid(!aConfiguration.borderRouterEnabled);

    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(aEnabled && aConfiguration.borderRouterEnabled);
    mAndroidHost->SetTrelEnabled(aTrelEnabled);
    mTrelEnabled = aTrelEnabled;

    if (aEnabled)
    {
        EnableThread(nullptr /* aReceiver */);
    }
    else
    {
        UpdateThreadEnabledState(OT_STATE_DISABLED, nullptr /* aReceiver */);
    }
}

Status OtDaemonServer::terminate(void)
{
    mTaskRunner.Post([]() {
        otbrLogWarning("Terminating ot-daemon process...");
        exit(0);
    });
    return Status::ok();
}

void OtDaemonServer::UpdateThreadEnabledState(const int enabled, const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    VerifyOrExit(enabled != mState.threadEnabled);

    otbrLogInfo("Thread enabled state changed: %s -> %s", ThreadEnabledStateToString(mState.threadEnabled),
                ThreadEnabledStateToString(enabled));
    mState.threadEnabled = enabled;

    if (aReceiver != nullptr)
    {
        aReceiver->onSuccess();
    }

    // Enables the BorderAgent module only when Thread is enabled and configured a Border Router,
    // so that it won't publish the MeshCoP mDNS service when unnecessary
    // TODO: b/376217403 - enables / disables OT Border Agent at runtime
    mBorderAgent.SetEnabled(enabled == OT_STATE_ENABLED && mAndroidHost->GetConfiguration().borderRouterEnabled);

    NotifyStateChanged(/* aListenerId*/ -1);

exit:
    return;
}

void OtDaemonServer::EnableThread(const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    otOperationalDatasetTlvs datasetTlvs;

    if ((mAndroidHost->GetConfiguration().borderRouterEnabled &&
         mAndroidHost->GetConfiguration().borderRouterAutoJoinEnabled) &&
        (otDatasetGetActiveTlvs(GetOtInstance(), &datasetTlvs) != OT_ERROR_NOT_FOUND && datasetTlvs.mLength > 0) &&
        !isAttached())
    {
        (void)otIp6SetEnabled(GetOtInstance(), true);
        (void)otThreadSetEnabled(GetOtInstance(), true);
    }
    UpdateThreadEnabledState(OT_STATE_ENABLED, aReceiver);
}

Status OtDaemonServer::setThreadEnabled(const bool aEnabled, const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    VerifyOrExit(mHost.IsInitialized(), PropagateResult(OT_ERROR_INVALID_STATE, "OT is not initialized", aReceiver));

    mTaskRunner.Post([aEnabled, aReceiver, this]() { setThreadEnabledInternal(aEnabled, aReceiver); });

exit:
    return Status::ok();
}

void OtDaemonServer::setThreadEnabledInternal(const bool aEnabled, const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    int         error = OT_ERROR_NONE;
    std::string message;

    VerifyOrExit(mState.threadEnabled != OT_STATE_DISABLING, error = OT_ERROR_BUSY, message = "Thread is disabling");

    if ((mState.threadEnabled == OT_STATE_ENABLED) == aEnabled)
    {
        aReceiver->onSuccess();
        ExitNow();
    }

    if (aEnabled)
    {
        EnableThread(aReceiver);
    }
    else
    {
        // `aReceiver` should not be set here because the operation isn't finished yet
        UpdateThreadEnabledState(OT_STATE_DISABLING, nullptr /* aReceiver */);

        LeaveGracefully(false /* aEraseDataset */, "disableThread", [aReceiver, this]() {
            // Ignore errors as those operations should always succeed
            (void)otThreadSetEnabled(GetOtInstance(), false);
            (void)otIp6SetEnabled(GetOtInstance(), false);
            UpdateThreadEnabledState(OT_STATE_DISABLED, aReceiver);
        });
    }

exit:
    if (error != OT_ERROR_NONE)
    {
        PropagateResult(error, message, aReceiver);
    }
}

Status OtDaemonServer::activateEphemeralKeyMode(const int64_t                             aLifetimeMillis,
                                                const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    VerifyOrExit(mHost.IsInitialized(), PropagateResult(OT_ERROR_INVALID_STATE, "OT is not initialized", aReceiver));

    mTaskRunner.Post(
        [aLifetimeMillis, aReceiver, this]() { activateEphemeralKeyModeInternal(aLifetimeMillis, aReceiver); });

exit:
    return Status::ok();
}

void OtDaemonServer::activateEphemeralKeyModeInternal(const int64_t                             aLifetimeMillis,
                                                      const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    int         error = OT_ERROR_NONE;
    std::string message;
    std::string passcode;

    VerifyOrExit(isAttached(), error = static_cast<int>(IOtDaemon::ErrorCode::OT_ERROR_FAILED_PRECONDITION),
                 message = "Cannot activate ephemeral key mode when this device is not attached to Thread network");
    VerifyOrExit(otBorderAgentEphemeralKeyGetState(GetOtInstance()) != OT_BORDER_AGENT_STATE_DISABLED,
                 error = OT_ERROR_INVALID_STATE, message = "ephemeral key manager is disabled");
    VerifyOrExit(otBorderAgentEphemeralKeyGetState(GetOtInstance()) == OT_BORDER_AGENT_STATE_STOPPED,
                 error = OT_ERROR_BUSY, message = "ephemeral key mode is already activated");

    otbrLogInfo("Activating ephemeral key mode with %lldms lifetime.", aLifetimeMillis);

    SuccessOrExit(error = mBorderAgent.CreateEphemeralKey(passcode), message = "Failed to create ephemeral key");
    SuccessOrExit(error   = otBorderAgentEphemeralKeyStart(GetOtInstance(), passcode.c_str(),
                                                           static_cast<uint32_t>(aLifetimeMillis), 0 /* aUdpPort */),
                  message = "Failed to set ephemeral key");

exit:
    if (aReceiver != nullptr)
    {
        if (error == OT_ERROR_NONE)
        {
            mState.ephemeralKeyState          = GetEphemeralKeyState(otBorderAgentEphemeralKeyGetState(GetOtInstance()));
            mState.ephemeralKeyPasscode       = passcode;
            mState.ephemeralKeyLifetimeMillis = aLifetimeMillis;
            mEphemeralKeyExpiryMillis         = std::chrono::duration_cast<std::chrono::milliseconds>(
                                            std::chrono::steady_clock::now().time_since_epoch())
                                            .count() +
                                        aLifetimeMillis;
            aReceiver->onSuccess();
        }
        else
        {
            aReceiver->onError(error, message);
        }
    }
}

Status OtDaemonServer::deactivateEphemeralKeyMode(const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    VerifyOrExit(mHost.IsInitialized(), PropagateResult(OT_ERROR_INVALID_STATE, "OT is not initialized", aReceiver));

    mTaskRunner.Post([aReceiver, this]() { deactivateEphemeralKeyModeInternal(aReceiver); });

exit:
    return Status::ok();
}

void OtDaemonServer::deactivateEphemeralKeyModeInternal(const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    int         error = OT_ERROR_NONE;
    std::string message;

    otbrLogInfo("Deactivating ephemeral key mode.");

    VerifyOrExit(otBorderAgentEphemeralKeyGetState(GetOtInstance()) != OT_BORDER_AGENT_STATE_DISABLED &&
                     otBorderAgentEphemeralKeyGetState(GetOtInstance()) != OT_BORDER_AGENT_STATE_STOPPED,
                 error = OT_ERROR_NONE);

    otBorderAgentEphemeralKeyStop(GetOtInstance());

exit:
    PropagateResult(error, message, aReceiver);
}

Status OtDaemonServer::registerStateCallback(const std::shared_ptr<IOtDaemonCallback> &aCallback, int64_t aListenerId)
{
    VerifyOrExit(mHost.IsInitialized());

    mTaskRunner.Post([aCallback, aListenerId, this]() { registerStateCallbackInternal(aCallback, aListenerId); });

exit:
    return Status::ok();
}

void OtDaemonServer::registerStateCallbackInternal(const std::shared_ptr<IOtDaemonCallback> &aCallback,
                                                   int64_t                                   aListenerId)
{
    mCallback = aCallback;
    if (mCallback != nullptr)
    {
        AIBinder_linkToDeath(mCallback->asBinder().get(), mClientDeathRecipient.get(), this);
    }

    // To ensure that a client app can get the latest correct state immediately when registering a
    // state callback, here needs to invoke the callback
    RefreshOtDaemonState(/* aFlags */ 0xffffffff);
    NotifyStateChanged(aListenerId);
    if (mCallback != nullptr)
    {
        mCallback->onBackboneRouterStateChanged(GetBackboneRouterState());
    }

exit:
    return;
}

bool OtDaemonServer::RefreshOtDaemonState(otChangedFlags aFlags)
{
    bool haveUpdates = false;

    if (aFlags & OT_CHANGED_THREAD_NETIF_STATE)
    {
        mState.isInterfaceUp = mHost.Ip6IsEnabled();
        haveUpdates          = true;
    }

    if (aFlags & OT_CHANGED_THREAD_ROLE)
    {
        mState.deviceRole = mHost.GetDeviceRole();
        haveUpdates       = true;
    }

    if (aFlags & OT_CHANGED_THREAD_PARTITION_ID)
    {
        mState.partitionId = mHost.GetPartitionId();
        haveUpdates        = true;
    }

    if (aFlags & OT_CHANGED_ACTIVE_DATASET)
    {
        otOperationalDatasetTlvs datasetTlvs;
        mHost.GetDatasetActiveTlvs(datasetTlvs);
        mState.activeDatasetTlvs.assign(datasetTlvs.mTlvs, datasetTlvs.mTlvs + datasetTlvs.mLength);

        haveUpdates = true;
    }

    if (aFlags & OT_CHANGED_PENDING_DATASET)
    {
        otOperationalDatasetTlvs datasetTlvs;
        mHost.GetDatasetPendingTlvs(datasetTlvs);
        mState.pendingDatasetTlvs.assign(datasetTlvs.mTlvs, datasetTlvs.mTlvs + datasetTlvs.mLength);

        haveUpdates = true;
    }

    if (isAttached() && !mState.activeDatasetTlvs.empty() && mJoinReceiver != nullptr)
    {
        otbrLogInfo("Join succeeded");
        mJoinReceiver->onSuccess();
        mJoinReceiver = nullptr;
    }

    return haveUpdates;
}

/**
 * Returns `true` if the two TLV lists are representing the same Operational Dataset.
 *
 * Note this method works even if TLVs in `aLhs` and `aRhs` are not ordered.
 */
static bool areDatasetsEqual(const otOperationalDatasetTlvs &aLhs, const otOperationalDatasetTlvs &aRhs)
{
    bool result = false;

    otOperationalDataset     lhsDataset;
    otOperationalDataset     rhsDataset;
    otOperationalDatasetTlvs lhsNormalizedTlvs;
    otOperationalDatasetTlvs rhsNormalizedTlvs;

    // Sort the TLVs in the TLV byte arrays by leveraging the deterministic nature of the two OT APIs
    SuccessOrExit(otDatasetParseTlvs(&aLhs, &lhsDataset));
    SuccessOrExit(otDatasetParseTlvs(&aRhs, &rhsDataset));
    otDatasetConvertToTlvs(&lhsDataset, &lhsNormalizedTlvs);
    otDatasetConvertToTlvs(&rhsDataset, &rhsNormalizedTlvs);

    result = (lhsNormalizedTlvs.mLength == rhsNormalizedTlvs.mLength) &&
             (memcmp(lhsNormalizedTlvs.mTlvs, rhsNormalizedTlvs.mTlvs, lhsNormalizedTlvs.mLength) == 0);

exit:
    return result;
}

Status OtDaemonServer::join(const std::vector<uint8_t>               &aActiveOpDatasetTlvs,
                            bool                                      aCreatePartitionIfNotFound,
                            const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    otOperationalDatasetTlvs otDatasetTlvs;

    VerifyOrExit(mHost.IsInitialized(), PropagateResult(OT_ERROR_INVALID_STATE, "OT is not initialized", aReceiver));

    std::copy(aActiveOpDatasetTlvs.begin(), aActiveOpDatasetTlvs.end(), otDatasetTlvs.mTlvs);
    otDatasetTlvs.mLength = static_cast<uint8_t>(aActiveOpDatasetTlvs.size());

    mTaskRunner.Post([otDatasetTlvs, aCreatePartitionIfNotFound, aReceiver, this]() {
        joinInternal(otDatasetTlvs, aCreatePartitionIfNotFound, aReceiver);
    });

exit:
    return Status::ok();
}

void OtDaemonServer::joinInternal(const otOperationalDatasetTlvs           &aNewDatasetTlvs,
                                  bool                                      aCreatePartitionIfNotFound,
                                  const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    int                      error = OT_ERROR_NONE;
    std::string              message;
    otOperationalDatasetTlvs curDatasetTlvs;

    VerifyOrExit(mState.threadEnabled != OT_STATE_DISABLING, error = OT_ERROR_BUSY, message = "Thread is disabling");

    VerifyOrExit(mState.threadEnabled == OT_STATE_ENABLED,
                 error   = static_cast<int>(IOtDaemon::ErrorCode::OT_ERROR_THREAD_DISABLED),
                 message = "Thread is disabled");

    otbrLogInfo("Start joining...");

    SuccessOrExit(
        error = otThreadSetLinkMode(GetOtInstance(), AndroidRcpHost::GetLinkModeConfig(aCreatePartitionIfNotFound)));

    error = otDatasetGetActiveTlvs(GetOtInstance(), &curDatasetTlvs);
    if (error == OT_ERROR_NONE && areDatasetsEqual(aNewDatasetTlvs, curDatasetTlvs) && isAttached())
    {
        // Do not leave and re-join if this device has already joined the same network.
        // This can help elimilate unnecessary connectivity and topology disruption and
        // save the time for re-joining. It's more useful for use cases where Thread
        // networks are dynamically brought up and torn down (e.g. Thread on mobile phones).
        aReceiver->onSuccess();
        ExitNow();
    }

    // If this device has ever joined a different network, try to leave from previous
    // network first. Do this even this device role is detached or disabled, this is for
    // clearing any in-memory state of the previous network.
    if (error == OT_ERROR_NONE && !areDatasetsEqual(aNewDatasetTlvs, curDatasetTlvs))
    {
        LeaveGracefully(true /* aEraseDataset */, "join",
                        [aNewDatasetTlvs, aCreatePartitionIfNotFound, aReceiver, this]() {
                            joinInternal(aNewDatasetTlvs, aCreatePartitionIfNotFound, aReceiver);
                        });
        ExitNow();
    }

    SuccessOrExit(error   = otDatasetSetActiveTlvs(GetOtInstance(), &aNewDatasetTlvs),
                  message = "Failed to set Active Operational Dataset");

    // Shouldn't we have an equivalent `otThreadAttach` method vs `otThreadDetachGracefully`?
    SuccessOrExit(error = otIp6SetEnabled(GetOtInstance(), true), message = "Failed to bring up Thread interface");
    SuccessOrExit(error = otThreadSetEnabled(GetOtInstance(), true), message = "Failed to bring up Thread stack");

    // Abort an ongoing join()
    if (mJoinReceiver != nullptr)
    {
        mJoinReceiver->onError(OT_ERROR_ABORT, "Aborted by a new join()");
    }
    mJoinReceiver = aReceiver;

exit:
    if (error != OT_ERROR_NONE)
    {
        PropagateResult(error, message, aReceiver);
    }
}

Status OtDaemonServer::leave(bool aEraseDataset, const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    VerifyOrExit(mHost.IsInitialized(), PropagateResult(OT_ERROR_INVALID_STATE, "OT is not initialized", aReceiver));

    mTaskRunner.Post([aEraseDataset, aReceiver, this]() { leaveInternal(aEraseDataset, aReceiver); });

exit:
    return Status::ok();
}

void OtDaemonServer::leaveInternal(bool aEraseDataset, const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    LeaveGracefully(aEraseDataset, "leave", [aReceiver]() { PropagateResult(OT_ERROR_NONE, "", aReceiver); });
}

void OtDaemonServer::LeaveGracefully(bool aEraseDataset, const std::string &aCallerTag, const LeaveCallback &aCallback)
{
    otOperationalDatasetTlvs curDatasetTlvs;

    if (otThreadGetDeviceRole(GetOtInstance()) != OT_DEVICE_ROLE_DISABLED)
    {
        otbrLogInfo("Start graceful leave...");

        mLeaveCallbacks.push_back([aEraseDataset, aCallerTag, aCallback, this]() {
            assert(otThreadGetDeviceRole(GetOtInstance()) == OT_DEVICE_ROLE_DISABLED);
            LeaveGracefully(aEraseDataset, aCallerTag, aCallback);
        });

        // Ignores the OT_ERROR_BUSY error if a detach has already been requested.
        // `otThreadDetachGracefully()` will invoke the `DetachGracefullyCallback`
        // callabck in 0 seconds if this device role is detached or disabled. So
        // `DetachGracefullyCallback` is guaranteed to be called in all cases
        (void)otThreadDetachGracefully(GetOtInstance(), DetachGracefullyCallback, this);
        ExitNow();
    }

    // Any join() or scheduleMigration() onging requests will be aborted
    if (mJoinReceiver != nullptr)
    {
        mJoinReceiver->onError(OT_ERROR_ABORT, "Aborted by a " + aCallerTag + " operation");
        mJoinReceiver = nullptr;
    }

    if (mMigrationReceiver != nullptr)
    {
        mMigrationReceiver->onError(OT_ERROR_ABORT, "Aborted by a " + aCallerTag + " operation");
        mMigrationReceiver = nullptr;
    }

    // It's not necessary to reset the OpenThread instance if it has no dataset
    if (aEraseDataset && otDatasetGetActiveTlvs(GetOtInstance(), &curDatasetTlvs) == OT_ERROR_NONE)
    {
        SuccessOrDie(otInstanceErasePersistentInfo(GetOtInstance()), "Failed to erase persistent info");
        mResetThreadHandler();

        // The OtDaemonServer runtime states are outdated after
        // the OT instances has been destroyed in `mResetThreadHandler`
        ResetRuntimeStatesAfterLeave();

        initializeInternal(mState.threadEnabled, mAndroidHost->GetConfiguration(), mINsdPublisher, mMeshcopTxts,
                           mCountryCode, mTrelEnabled, mCallback);
    }

    otbrLogInfo("Leave() is done");

    aCallback();

exit:
    return;
}

void OtDaemonServer::ResetRuntimeStatesAfterLeave()
{
    bool threadEnabled = mState.threadEnabled;

    assert(mJoinReceiver == nullptr);
    assert(mMigrationReceiver == nullptr);

    // The Thread Enabled state survives the leave() API call.
    // This indicates that we should move the threadEnabled state
    // out of the OtDaemonState class
    mState               = OtDaemonState();
    mState.threadEnabled = threadEnabled;

    mOnMeshPrefixes.clear();
    mEphemeralKeyExpiryMillis = 0;
}

void OtDaemonServer::DetachGracefullyCallback(void *aBinderServer)
{
    OtDaemonServer *thisServer = static_cast<OtDaemonServer *>(aBinderServer);
    thisServer->DetachGracefullyCallback();
}

void OtDaemonServer::DetachGracefullyCallback(void)
{
    otbrLogInfo("DetachGracefully success...");

    for (auto &callback : mLeaveCallbacks)
    {
        callback();
    }
    mLeaveCallbacks.clear();
}

bool OtDaemonServer::isAttached()
{
    otDeviceRole role = otThreadGetDeviceRole(GetOtInstance());

    return role == OT_DEVICE_ROLE_CHILD || role == OT_DEVICE_ROLE_ROUTER || role == OT_DEVICE_ROLE_LEADER;
}

Status OtDaemonServer::scheduleMigration(const std::vector<uint8_t>               &aPendingOpDatasetTlvs,
                                         const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    VerifyOrExit(mHost.IsInitialized(), PropagateResult(OT_ERROR_INVALID_STATE, "OT is not initialized", aReceiver));

    mTaskRunner.Post(
        [aPendingOpDatasetTlvs, aReceiver, this]() { scheduleMigrationInternal(aPendingOpDatasetTlvs, aReceiver); });

exit:
    return Status::ok();
}

void OtDaemonServer::scheduleMigrationInternal(const std::vector<uint8_t>               &aPendingOpDatasetTlvs,
                                               const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    int                  error = OT_ERROR_NONE;
    std::string          message;
    otOperationalDataset emptyDataset;

    VerifyOrExit(mState.threadEnabled != OT_STATE_DISABLING, error = OT_ERROR_BUSY, message = "Thread is disabling");

    VerifyOrExit(mState.threadEnabled == OT_STATE_ENABLED,
                 error   = static_cast<int>(IOtDaemon::ErrorCode::OT_ERROR_THREAD_DISABLED),
                 message = "Thread is disabled");

    if (GetOtInstance() == nullptr)
    {
        message = "OT is not initialized";
        ExitNow(error = OT_ERROR_INVALID_STATE);
    }
    if (!isAttached())
    {
        message = "Cannot schedule migration when this device is detached";
        ExitNow(error = static_cast<int>(IOtDaemon::ErrorCode::OT_ERROR_FAILED_PRECONDITION));
    }

    // TODO: check supported channel mask

    error = otDatasetSendMgmtPendingSet(GetOtInstance(), &emptyDataset, aPendingOpDatasetTlvs.data(),
                                        static_cast<uint8_t>(aPendingOpDatasetTlvs.size()), SendMgmtPendingSetCallback,
                                        /* aBinderServer= */ this);
    if (error != OT_ERROR_NONE)
    {
        message = "Failed to send MGMT_PENDING_SET.req";
    }

exit:
    if (error != OT_ERROR_NONE)
    {
        PropagateResult(error, message, aReceiver);
    }
    else
    {
        // otDatasetSendMgmtPendingSet() returns OT_ERROR_BUSY if it has already been called before but the
        // callback hasn't been invoked. So we can guarantee that mMigrationReceiver is always nullptr here
        assert(mMigrationReceiver == nullptr);
        mMigrationReceiver = aReceiver;
    }
}

void OtDaemonServer::SendMgmtPendingSetCallback(otError aResult, void *aBinderServer)
{
    OtDaemonServer *thisServer = static_cast<OtDaemonServer *>(aBinderServer);

    if (thisServer->mMigrationReceiver != nullptr)
    {
        PropagateResult(aResult, "Failed to register Pending Dataset to leader", thisServer->mMigrationReceiver);
        thisServer->mMigrationReceiver = nullptr;
    }
}

Status OtDaemonServer::setCountryCode(const std::string                        &aCountryCode,
                                      const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    VerifyOrExit(mHost.IsInitialized(), PropagateResult(OT_ERROR_INVALID_STATE, "OT is not initialized", aReceiver));

    mTaskRunner.Post([aCountryCode, aReceiver, this]() { setCountryCodeInternal(aCountryCode, aReceiver); });

exit:
    return Status::ok();
}

void OtDaemonServer::setCountryCodeInternal(const std::string                        &aCountryCode,
                                            const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    mHost.SetCountryCode(aCountryCode, [aReceiver, aCountryCode, this](otError aError, const std::string &aMessage) {
        if (aError == OT_ERROR_NONE)
        {
            mCountryCode = aCountryCode;
        }

        PropagateResult(aError, aMessage, aReceiver);
    });
}

Status OtDaemonServer::getChannelMasks(const std::shared_ptr<IChannelMasksReceiver> &aReceiver)
{
    VerifyOrExit(mHost.IsInitialized(), aReceiver->onError(OT_ERROR_INVALID_STATE, "OT is not initialized"));

    mTaskRunner.Post([aReceiver, this]() { getChannelMasksInternal(aReceiver); });

exit:
    return Status::ok();
}

void OtDaemonServer::getChannelMasksInternal(const std::shared_ptr<IChannelMasksReceiver> &aReceiver)
{
    auto channelMasksReceiver = [aReceiver](uint32_t aSupportedChannelMask, uint32_t aPreferredChannelMask) {
        aReceiver->onSuccess(aSupportedChannelMask, aPreferredChannelMask);
    };
    auto errorReceiver = [aReceiver](otError aError, const std::string &aMessage) {
        aReceiver->onError(aError, aMessage);
    };
    mHost.GetChannelMasks(channelMasksReceiver, errorReceiver);
}

Status OtDaemonServer::setChannelMaxPowers(const std::vector<ChannelMaxPower>       &aChannelMaxPowers,
                                           const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    VerifyOrExit(mHost.IsInitialized(), PropagateResult(OT_ERROR_INVALID_STATE, "OT is not initialized", aReceiver));

    mTaskRunner.Post(
        [aChannelMaxPowers, aReceiver, this]() { setChannelMaxPowersInternal(aChannelMaxPowers, aReceiver); });

exit:
    return Status::ok();
}

Status OtDaemonServer::setChannelMaxPowersInternal(const std::vector<ChannelMaxPower>       &aChannelMaxPowers,
                                                   const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    // Transform aidl ChannelMaxPower to ThreadHost::ChannelMaxPower
    std::vector<Host::ThreadHost::ChannelMaxPower> channelMaxPowers(aChannelMaxPowers.size());
    std::transform(aChannelMaxPowers.begin(), aChannelMaxPowers.end(), channelMaxPowers.begin(),
                   [](const ChannelMaxPower &aChannelMaxPower) {
                       // INT_MIN indicates that the corresponding channel is disabled in Thread Android API
                       // `setChannelMaxPowers()` INT16_MAX indicates that the corresponding channel is disabled in
                       // OpenThread API `otPlatRadioSetChannelTargetPower()`.
                       return Host::ThreadHost::ChannelMaxPower(
                           aChannelMaxPower.channel,
                           aChannelMaxPower.maxPower == INT_MIN
                               ? INT16_MAX
                               : std::clamp(aChannelMaxPower.maxPower, INT16_MIN, INT16_MAX - 1));
                   });

    mHost.SetChannelMaxPowers(channelMaxPowers, [aReceiver](otError aError, const std::string &aMessage) {
        PropagateResult(aError, aMessage, aReceiver);
    });

    return Status::ok();
}

Status OtDaemonServer::setConfiguration(const OtDaemonConfiguration              &aConfiguration,
                                        const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    VerifyOrExit(mHost.IsInitialized(), PropagateResult(OT_ERROR_INVALID_STATE, "OT is not initialized", aReceiver));

    mTaskRunner.Post([aConfiguration, aReceiver, this]() {
        if (aConfiguration != mAndroidHost->GetConfiguration())
        {
            mAdvProxy.SetAllowMlEid(!aConfiguration.borderRouterEnabled);
            mBorderAgent.SetEnabled(mState.threadEnabled && aConfiguration.borderRouterEnabled);
            mAndroidHost->SetConfiguration(aConfiguration, aReceiver);
        }
    });

exit:
    return Status::ok();
}

Status OtDaemonServer::setInfraLinkInterfaceName(const std::optional<std::string>         &aInterfaceName,
                                                 const ScopedFileDescriptor               &aIcmp6Socket,
                                                 const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    int icmp6Socket = aIcmp6Socket.dup().release();

    VerifyOrExit(mHost.IsInitialized(), PropagateResult(OT_ERROR_INVALID_STATE, "OT is not initialized", aReceiver));

    mTaskRunner.Post([interfaceName = aInterfaceName.value_or(""), icmp6Socket, aReceiver, this]() {
        mAndroidHost->SetInfraLinkInterfaceName(interfaceName, icmp6Socket, aReceiver);
    });

exit:
    return Status::ok();
}

Status OtDaemonServer::runOtCtlCommand(const std::string                        &aCommand,
                                       const bool                                aIsInteractive,
                                       const std::shared_ptr<IOtOutputReceiver> &aReceiver)
{
    VerifyOrExit(mHost.IsInitialized(), aReceiver->onError(OT_ERROR_INVALID_STATE, "OT is not initialized"));

    mTaskRunner.Post([aCommand, aIsInteractive, aReceiver, this]() {
        runOtCtlCommandInternal(aCommand, aIsInteractive, aReceiver);
    });

exit:
    return Status::ok();
}

Status OtDaemonServer::setInfraLinkNat64Prefix(const std::optional<std::string>         &aNat64Prefix,
                                               const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    VerifyOrExit(mHost.IsInitialized(), PropagateResult(OT_ERROR_INVALID_STATE, "OT is not initialized", aReceiver));

    mTaskRunner.Post([nat64Prefix = aNat64Prefix.value_or(""), aReceiver, this]() {
        mAndroidHost->SetInfraLinkNat64Prefix(nat64Prefix, aReceiver);
    });

exit:
    return Status::ok();
}

void OtDaemonServer::runOtCtlCommandInternal(const std::string                        &aCommand,
                                             const bool                                aIsInteractive,
                                             const std::shared_ptr<IOtOutputReceiver> &aReceiver)
{
    mAndroidHost->RunOtCtlCommand(aCommand, aIsInteractive, aReceiver);
}

Status OtDaemonServer::setInfraLinkDnsServers(const std::vector<std::string>           &aDnsServers,
                                              const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    VerifyOrExit(mHost.IsInitialized(), PropagateResult(OT_ERROR_INVALID_STATE, "OT is not initialized", aReceiver));

    mTaskRunner.Post(
        [aDnsServers, aReceiver, this]() { mAndroidHost->SetInfraLinkDnsServers(aDnsServers, aReceiver); });

exit:
    return Status::ok();
}

binder_status_t OtDaemonServer::dump(int aFd, const char **aArgs, uint32_t aNumArgs)
{
    binder_status_t status = STATUS_OK;

    VerifyOrExit(mHost.IsInitialized());
    status = mAndroidHost->Dump(aFd, aArgs, aNumArgs);

exit:
    return status;
}

void OtDaemonServer::PushTelemetryIfConditionMatch()
{
    VerifyOrExit(GetOtInstance() != nullptr);

    // TODO: Push telemetry per kTelemetryUploadIntervalThreshold instead of on startup.
    // TODO: Save unpushed telemetries in local cache to avoid data loss.
    RetrieveAndPushAtoms(GetOtInstance());
    mTaskRunner.Post(kTelemetryUploadIntervalThreshold, [this]() { PushTelemetryIfConditionMatch(); });

exit:
    return;
}

Status OtDaemonServer::setNat64Cidr(const std::optional<std::string>         &aCidr,
                                    const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    VerifyOrExit(mHost.IsInitialized(), PropagateResult(OT_ERROR_INVALID_STATE, "OT is not initialized", aReceiver));

    mTaskRunner.Post([aCidr, aReceiver, this]() { setNat64CidrInternal(aCidr, aReceiver); });

exit:
    return Status::ok();
}

void OtDaemonServer::setNat64CidrInternal(const std::optional<std::string>         &aCidr,
                                          const std::shared_ptr<IOtStatusReceiver> &aReceiver)
{
    otError     error = OT_ERROR_NONE;
    std::string message;

    if (aCidr.has_value())
    {
        otIp4Cidr nat64Cidr{};

        otbrLogInfo("Setting NAT64 CIDR: %s", aCidr->c_str());
        SuccessOrExit(error = otIp4CidrFromString(aCidr->c_str(), &nat64Cidr), message = "Failed to parse NAT64 CIDR");
        SuccessOrExit(error = otNat64SetIp4Cidr(GetOtInstance(), &nat64Cidr), message = "Failed to set NAT64 CIDR");
    }
    else
    {
        otbrLogInfo("Clearing NAT64 CIDR");
        otNat64ClearIp4Cidr(GetOtInstance());
    }

exit:
    PropagateResult(error, message, aReceiver);
}

} // namespace Android
} // namespace otbr
