Add Thread network HAL

Bug: b/203492431
Test: Build and run the VTS test and run otbr-agent on Android emulator.
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:b82b0478059dd6203f43d5917558e34fd4ed44d5)
Merged-In: If58b5a8c75e40376ae6c6e93554afe750496308b
Change-Id: If58b5a8c75e40376ae6c6e93554afe750496308b
diff --git a/threadnetwork/aidl/default/Android.bp b/threadnetwork/aidl/default/Android.bp
new file mode 100644
index 0000000..201306d
--- /dev/null
+++ b/threadnetwork/aidl/default/Android.bp
@@ -0,0 +1,56 @@
+//
+//  Copyright (c) 2022 Google LLC.
+//  All rights reserved.
+//
+//  This document is the property of Google LLC, Inc. It is
+//  considered proprietary and confidential information.
+//
+//  This document may not be reproduced or transmitted in any form,
+//  in whole or in part, without the express written permission of
+//  Google LLC.
+
+cc_defaults {
+    name: "threadnetwork_service_default",
+    vintf_fragments: ["threadnetwork-default.xml"],
+    vendor: true,
+    relative_install_path: "hw",
+
+    shared_libs: [
+        "android.hardware.threadnetwork-V1-ndk",
+        "libbase",
+        "libbinder_ndk",
+        "libcutils",
+        "liblog",
+        "libutils",
+    ],
+
+    cppflags: [
+        "-Wno-non-virtual-dtor",
+    ],
+
+    static_libs: [
+        "openthread-common",
+        "openthread-hdlc",
+        "openthread-platform",
+        "openthread-posix",
+        "openthread-url",
+    ],
+
+    srcs: [
+        "main.cpp",
+        "service.cpp",
+        "thread_chip.cpp",
+        "utils.cpp",
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.threadnetwork-service.sim",
+    defaults: ["threadnetwork_service_default"],
+    init_rc: ["android.hardware.threadnetwork-service.sim.rc"],
+}
+
+cc_binary {
+    name: "android.hardware.threadnetwork-service",
+    defaults: ["threadnetwork_service_default"],
+}
diff --git a/threadnetwork/aidl/default/android.hardware.threadnetwork-service.sim.rc b/threadnetwork/aidl/default/android.hardware.threadnetwork-service.sim.rc
new file mode 100644
index 0000000..2fb409c
--- /dev/null
+++ b/threadnetwork/aidl/default/android.hardware.threadnetwork-service.sim.rc
@@ -0,0 +1,3 @@
+service vendor.threadnetwork_hal /vendor/bin/hw/android.hardware.threadnetwork-service.sim spinel+hdlc+forkpty:///vendor/bin/ot-rcp?forkpty-arg=1
+    class hal
+    user thread_network
diff --git a/threadnetwork/aidl/default/main.cpp b/threadnetwork/aidl/default/main.cpp
new file mode 100644
index 0000000..9f2a789
--- /dev/null
+++ b/threadnetwork/aidl/default/main.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+
+#include "service.hpp"
+#include "utils.hpp"
+
+int main(int argc, char* argv[]) {
+    CHECK_GT(argc, 1);
+    aidl::android::hardware::threadnetwork::Service service(&argv[1], argc - 1);
+
+    ALOGI("Thread Network HAL is running");
+
+    service.startLoop();
+    return EXIT_FAILURE;  // should not reach
+}
diff --git a/threadnetwork/aidl/default/service.cpp b/threadnetwork/aidl/default/service.cpp
new file mode 100644
index 0000000..44bb99a
--- /dev/null
+++ b/threadnetwork/aidl/default/service.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "service.hpp"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "thread_chip.hpp"
+#include "utils.hpp"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace threadnetwork {
+
+Service::Service(char* urls[], int numUrls) : mBinderFd(-1) {
+    CHECK_NE(urls, nullptr);
+    CHECK_GT(numUrls, 0);
+
+    for (int i = 0; i < numUrls; i++) {
+        auto threadChip = ndk::SharedRefBase::make<ThreadChip>(i, urls[i]);
+        CHECK_NE(threadChip, nullptr);
+        mThreadChips.push_back(std::move(threadChip));
+    }
+
+    binder_status_t status = ABinderProcess_setupPolling(&mBinderFd);
+    CHECK_EQ(status, ::STATUS_OK);
+    CHECK_GE(mBinderFd, 0);
+}
+
+void Service::Update(otSysMainloopContext& context) {
+    FD_SET(mBinderFd, &context.mReadFdSet);
+    context.mMaxFd = std::max(context.mMaxFd, mBinderFd);
+}
+
+void Service::Process(const otSysMainloopContext& context) {
+    if (FD_ISSET(mBinderFd, &context.mReadFdSet)) {
+        ABinderProcess_handlePolledCommands();
+    }
+}
+
+void Service::startLoop(void) {
+    const struct timeval kPollTimeout = {1, 0};
+    otSysMainloopContext context;
+    int rval;
+
+    ot::Posix::Mainloop::Manager::Get().Add(*this);
+
+    while (true) {
+        context.mMaxFd = -1;
+        context.mTimeout = kPollTimeout;
+
+        FD_ZERO(&context.mReadFdSet);
+        FD_ZERO(&context.mWriteFdSet);
+        FD_ZERO(&context.mErrorFdSet);
+
+        ot::Posix::Mainloop::Manager::Get().Update(context);
+
+        rval = select(context.mMaxFd + 1, &context.mReadFdSet, &context.mWriteFdSet,
+                      &context.mErrorFdSet, &context.mTimeout);
+
+        if (rval >= 0) {
+            ot::Posix::Mainloop::Manager::Get().Process(context);
+        } else if (errno != EINTR) {
+            ALOGE("select() failed: %s", strerror(errno));
+            break;
+        }
+    }
+}
+}  // namespace threadnetwork
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/threadnetwork/aidl/default/service.hpp b/threadnetwork/aidl/default/service.hpp
new file mode 100644
index 0000000..4137049
--- /dev/null
+++ b/threadnetwork/aidl/default/service.hpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mainloop.hpp"
+#include "thread_chip.hpp"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace threadnetwork {
+
+class Service : public ot::Posix::Mainloop::Source {
+  public:
+    Service(char* urls[], int numUrls);
+
+    void Update(otSysMainloopContext& context) override;
+    void Process(const otSysMainloopContext& context) override;
+    void startLoop(void);
+
+  private:
+    int mBinderFd;
+    std::vector<std::shared_ptr<ThreadChip>> mThreadChips;
+};
+}  // namespace threadnetwork
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/threadnetwork/aidl/default/thread_chip.cpp b/threadnetwork/aidl/default/thread_chip.cpp
new file mode 100644
index 0000000..b5cc7eb
--- /dev/null
+++ b/threadnetwork/aidl/default/thread_chip.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "thread_chip.hpp"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "utils.hpp"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace threadnetwork {
+
+ThreadChip::ThreadChip(uint8_t id, char* url)
+    : mUrl(),
+      mInterface(handleReceivedFrame, this, mRxFrameBuffer),
+      mRxFrameBuffer(),
+      mCallback(nullptr) {
+    const std::string name(std::string() + IThreadChip::descriptor + "/chip" + std::to_string(id));
+    binder_status_t status;
+
+    ALOGI("ServiceName: %s, Url: %s", name.c_str(), url);
+    CHECK_EQ(mUrl.Init(url), 0);
+    status = AServiceManager_addService(asBinder().get(), name.c_str());
+    CHECK_EQ(status, STATUS_OK);
+}
+
+void ThreadChip::clientDeathCallback(void* context) {
+    reinterpret_cast<ThreadChip*>(context)->clientDeathCallback();
+}
+
+void ThreadChip::clientDeathCallback(void) {
+    ALOGW("Thread Network HAL client is dead.");
+    close();
+}
+
+void ThreadChip::handleReceivedFrame(void* context) {
+    static_cast<ThreadChip*>(context)->handleReceivedFrame();
+}
+
+void ThreadChip::handleReceivedFrame(void) {
+    if (mCallback != nullptr) {
+        mCallback->onReceiveSpinelFrame(std::vector<uint8_t>(
+                mRxFrameBuffer.GetFrame(), mRxFrameBuffer.GetFrame() + mRxFrameBuffer.GetLength()));
+    }
+
+    mRxFrameBuffer.DiscardFrame();
+}
+
+ndk::ScopedAStatus ThreadChip::open(const std::shared_ptr<IThreadChipCallback>& in_callback) {
+    ndk::ScopedAStatus status;
+    AIBinder* binder;
+
+    VerifyOrExit(mCallback == nullptr,
+                 status = errorStatus(ERROR_BUSY, "Interface is already opened"));
+    VerifyOrExit(in_callback != nullptr,
+                 status = errorStatus(ERROR_INVALID_ARGS, "The callback is NULL"));
+    binder = in_callback->asBinder().get();
+    VerifyOrExit(binder != nullptr,
+                 status = errorStatus(ERROR_FAILED, "Failed to get the callback binder"));
+    mBinderDeathRecipient = AIBinder_DeathRecipient_new(clientDeathCallback);
+    VerifyOrExit(AIBinder_linkToDeath(binder, mBinderDeathRecipient, this) == STATUS_OK,
+                 status = errorStatus(ERROR_FAILED, "Failed to link the binder to death"));
+    VerifyOrExit(mInterface.Init(mUrl) == OT_ERROR_NONE,
+                 status = errorStatus(ERROR_FAILED, "Failed to initialize the interface"));
+
+    mCallback = in_callback;
+    ot::Posix::Mainloop::Manager::Get().Add(*this);
+    status = ndk::ScopedAStatus::ok();
+
+exit:
+    if (!status.isOk())
+    {
+        if (mBinderDeathRecipient != nullptr)
+        {
+           AIBinder_DeathRecipient_delete(mBinderDeathRecipient);
+           mBinderDeathRecipient = nullptr;
+        }
+        ALOGW("Open failed, error: %s", status.getDescription().c_str());
+    }
+    else
+    {
+        ALOGI("open()");
+    }
+
+    return status;
+}
+
+ndk::ScopedAStatus ThreadChip::close() {
+    VerifyOrExit(mCallback != nullptr);
+    mCallback = nullptr;
+    mInterface.Deinit();
+
+    ot::Posix::Mainloop::Manager::Get().Remove(*this);
+
+    AIBinder_DeathRecipient_delete(mBinderDeathRecipient);
+    mBinderDeathRecipient = nullptr;
+
+exit:
+    ALOGI("close()");
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus ThreadChip::sendSpinelFrame(const std::vector<uint8_t>& in_frame) {
+    ndk::ScopedAStatus status;
+    otError error;
+
+    VerifyOrExit(mCallback != nullptr,
+                 status = errorStatus(ERROR_FAILED, "The interface is not open"));
+
+    error = mInterface.SendFrame(reinterpret_cast<const uint8_t*>(in_frame.data()),
+                                 in_frame.size());
+    if (error == OT_ERROR_NONE) {
+        status = ndk::ScopedAStatus::ok();
+    } else if (error == OT_ERROR_NO_BUFS) {
+        status = errorStatus(ERROR_NO_BUFS, "Insufficient buffer space to send");
+    } else if (error == OT_ERROR_BUSY) {
+        status = errorStatus(ERROR_BUSY, "The interface is busy");
+    } else {
+        status = errorStatus(ERROR_FAILED, "Failed to send the spinel frame");
+    }
+
+exit:
+    if (!status.isOk())
+    {
+        ALOGW("Send spinel frame failed, error: %s", status.getDescription().c_str());
+    }
+
+    return status;
+}
+
+ndk::ScopedAStatus ThreadChip::reset() {
+    mInterface.OnRcpReset();
+    ALOGI("reset()");
+    return ndk::ScopedAStatus::ok();
+}
+
+void ThreadChip::Update(otSysMainloopContext& context) {
+    if (mCallback != nullptr) {
+        mInterface.UpdateFdSet(context.mReadFdSet, context.mWriteFdSet, context.mMaxFd,
+                               context.mTimeout);
+    }
+}
+
+void ThreadChip::Process(const otSysMainloopContext& context) {
+    struct RadioProcessContext radioContext;
+
+    if (mCallback != nullptr) {
+        radioContext.mReadFdSet = &context.mReadFdSet;
+        radioContext.mWriteFdSet = &context.mWriteFdSet;
+        mInterface.Process(radioContext);
+    }
+}
+
+ndk::ScopedAStatus ThreadChip::errorStatus(int32_t error, const char* message) {
+    return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(error, message));
+}
+}  // namespace threadnetwork
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/threadnetwork/aidl/default/thread_chip.hpp b/threadnetwork/aidl/default/thread_chip.hpp
new file mode 100644
index 0000000..d444374
--- /dev/null
+++ b/threadnetwork/aidl/default/thread_chip.hpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/threadnetwork/BnThreadChip.h>
+#include <aidl/android/hardware/threadnetwork/IThreadChipCallback.h>
+
+#include "hdlc_interface.hpp"
+#include "lib/spinel/spinel_interface.hpp"
+#include "mainloop.hpp"
+
+#include <android/binder_ibinder.h>
+#include <utils/Mutex.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace threadnetwork {
+
+class ThreadChip : public BnThreadChip, ot::Posix::Mainloop::Source {
+  public:
+    ThreadChip(uint8_t id, char* url);
+
+    ndk::ScopedAStatus open(const std::shared_ptr<IThreadChipCallback>& in_callback) override;
+    ndk::ScopedAStatus close() override;
+    ndk::ScopedAStatus sendSpinelFrame(const std::vector<uint8_t>& in_frame) override;
+    ndk::ScopedAStatus reset() override;
+    void Update(otSysMainloopContext& context) override;
+    void Process(const otSysMainloopContext& context) override;
+
+  private:
+    static void clientDeathCallback(void* context);
+    void clientDeathCallback(void);
+    static void handleReceivedFrame(void* context);
+    void handleReceivedFrame(void);
+    ndk::ScopedAStatus errorStatus(int32_t error, const char* message);
+
+    ot::Url::Url mUrl;
+    ot::Posix::HdlcInterface mInterface;
+    ot::Spinel::SpinelInterface::RxFrameBuffer mRxFrameBuffer;
+    std::shared_ptr<IThreadChipCallback> mCallback;
+    AIBinder_DeathRecipient* mBinderDeathRecipient;
+};
+
+}  // namespace threadnetwork
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/threadnetwork/aidl/default/threadnetwork-default.xml b/threadnetwork/aidl/default/threadnetwork-default.xml
new file mode 100644
index 0000000..d7dee1e
--- /dev/null
+++ b/threadnetwork/aidl/default/threadnetwork-default.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.threadnetwork</name>
+        <fqname>IThreadChip/chip0</fqname>
+    </hal>
+</manifest>
diff --git a/threadnetwork/aidl/default/utils.cpp b/threadnetwork/aidl/default/utils.cpp
new file mode 100644
index 0000000..a8f3464
--- /dev/null
+++ b/threadnetwork/aidl/default/utils.cpp
@@ -0,0 +1,36 @@
+
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.hpp"
+
+#include <openthread/logging.h>
+
+void otLogCritPlat(const char* format, ...) {
+    va_list args;
+
+    va_start(args, format);
+    __android_log_vprint(ANDROID_LOG_FATAL, LOG_TAG, format, args);
+    va_end(args);
+}
+
+void otLogWarnPlat(const char* format, ...) {
+    va_list args;
+
+    va_start(args, format);
+    __android_log_vprint(ANDROID_LOG_WARN, LOG_TAG, format, args);
+    va_end(args);
+}
diff --git a/threadnetwork/aidl/default/utils.hpp b/threadnetwork/aidl/default/utils.hpp
new file mode 100644
index 0000000..279c0ba
--- /dev/null
+++ b/threadnetwork/aidl/default/utils.hpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#define LOG_TAG "threadnetwork_hal"
+#include <utils/Log.h>