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/compatibility_matrices/compatibility_matrix.8.xml b/compatibility_matrices/compatibility_matrix.8.xml
index 93beb92..9fbf93e 100644
--- a/compatibility_matrices/compatibility_matrix.8.xml
+++ b/compatibility_matrices/compatibility_matrix.8.xml
@@ -834,4 +834,12 @@
             <regex-instance>.*</regex-instance>
         </interface>
     </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.threadnetwork</name>
+        <version>1</version>
+        <interface>
+            <name>IThreadChip</name>
+            <regex-instance>chip[0-9]+</regex-instance>
+        </interface>
+    </hal>
 </compatibility-matrix>
diff --git a/threadnetwork/aidl/Android.bp b/threadnetwork/aidl/Android.bp
new file mode 100644
index 0000000..480ac0f
--- /dev/null
+++ b/threadnetwork/aidl/Android.bp
@@ -0,0 +1,22 @@
+aidl_interface {
+    name: "android.hardware.threadnetwork",
+    vendor_available: true,
+    srcs: [
+        "android/hardware/threadnetwork/*.aidl",
+    ],
+
+    stability: "vintf",
+
+    backend: {
+        java: {
+            platform_apis: true,
+        },
+        ndk: {
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.threadnetwork",
+            ],
+            min_sdk_version: "33",
+        },
+    },
+}
diff --git a/threadnetwork/aidl/OWNERS b/threadnetwork/aidl/OWNERS
new file mode 100644
index 0000000..e3111c8
--- /dev/null
+++ b/threadnetwork/aidl/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 1288834
+zhanglongxia@google.com
+xyk@google.com
diff --git a/threadnetwork/aidl/aidl_api/android.hardware.threadnetwork/current/android/hardware/threadnetwork/IThreadChip.aidl b/threadnetwork/aidl/aidl_api/android.hardware.threadnetwork/current/android/hardware/threadnetwork/IThreadChip.aidl
new file mode 100644
index 0000000..e4d4cbe
--- /dev/null
+++ b/threadnetwork/aidl/aidl_api/android.hardware.threadnetwork/current/android/hardware/threadnetwork/IThreadChip.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.threadnetwork;
+@VintfStability
+interface IThreadChip {
+  void open(in android.hardware.threadnetwork.IThreadChipCallback callback);
+  void close();
+  void reset();
+  void sendSpinelFrame(in byte[] frame);
+  const int ERROR_FAILED = 1;
+  const int ERROR_INVALID_ARGS = 2;
+  const int ERROR_NO_BUFS = 3;
+  const int ERROR_BUSY = 4;
+}
diff --git a/threadnetwork/aidl/aidl_api/android.hardware.threadnetwork/current/android/hardware/threadnetwork/IThreadChipCallback.aidl b/threadnetwork/aidl/aidl_api/android.hardware.threadnetwork/current/android/hardware/threadnetwork/IThreadChipCallback.aidl
new file mode 100644
index 0000000..e86b3ec
--- /dev/null
+++ b/threadnetwork/aidl/aidl_api/android.hardware.threadnetwork/current/android/hardware/threadnetwork/IThreadChipCallback.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.threadnetwork;
+@VintfStability
+interface IThreadChipCallback {
+  oneway void onReceiveSpinelFrame(in byte[] frame);
+}
diff --git a/threadnetwork/aidl/android/hardware/threadnetwork/IThreadChip.aidl b/threadnetwork/aidl/android/hardware/threadnetwork/IThreadChip.aidl
new file mode 100644
index 0000000..eebaa46
--- /dev/null
+++ b/threadnetwork/aidl/android/hardware/threadnetwork/IThreadChip.aidl
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+package android.hardware.threadnetwork;
+
+import android.hardware.threadnetwork.IThreadChipCallback;
+
+/**
+ * Controls a Thread radio chip on the device.
+ */
+
+@VintfStability
+interface IThreadChip {
+    /**
+     * The operation failed for the internal error.
+     */
+    const int ERROR_FAILED = 1;
+
+    /**
+     * The invalid arguments.
+     */
+    const int ERROR_INVALID_ARGS = 2;
+
+    /**
+     * Insufficient buffers available to send frames.
+     */
+    const int ERROR_NO_BUFS = 3;
+
+    /**
+     * Service is busy and could not service the operation.
+     */
+    const int ERROR_BUSY = 4;
+
+    /**
+     * This method initializes the Thread HAL instance. If open completes
+     * successfully, then the Thread HAL instance is ready to accept spinel
+     * messages through sendSpinelFrame() API.
+     *
+     * @param callback  A IThreadChipCallback callback instance.
+     *
+     * @throws ServiceSpecificException with one of the following values:
+     *     - ERROR_FAILED        The interface cannot be opened due to an internal error.
+     *     - ERROR_INVALID_ARGS  The callback handle is invalid (for example, it is null).
+     *     - ERROR_BUSY          This interface is in use.
+     */
+    void open(in IThreadChipCallback callback);
+
+    /**
+     * Close the Thread HAL instance. Must free all resources.
+     */
+    void close();
+
+    /**
+     * This method resets the Thread HAL internal state. The callback registered by
+     * `open()` won’t be reset and the resource allocated by `open()` won’t be free.
+     *
+     */
+    void reset();
+
+    /**
+     * This method sends a spinel frame to the Thread HAL.
+     *
+     * This method should block until the frame is sent out successfully or
+     * the method throws errors immediately.
+     *
+     * Spinel Protocol:
+     *     https://github.com/openthread/openthread/blob/main/src/lib/spinel/spinel.h
+     *
+     * @param frame  The spinel frame to be sent.
+     *
+     * @throws ServiceSpecificException with one of the following values:
+     *         - ERROR_FAILED The Thread HAL failed to send the frame for an internal reason.
+     *         - ERROR_NO_BUFS Insufficient buffer space to send the frame.
+     *         - ERROR_BUSY The Thread HAL is busy.
+     */
+    void sendSpinelFrame(in byte[] frame);
+}
diff --git a/threadnetwork/aidl/android/hardware/threadnetwork/IThreadChipCallback.aidl b/threadnetwork/aidl/android/hardware/threadnetwork/IThreadChipCallback.aidl
new file mode 100644
index 0000000..046edc3
--- /dev/null
+++ b/threadnetwork/aidl/android/hardware/threadnetwork/IThreadChipCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package android.hardware.threadnetwork;
+
+@VintfStability
+interface IThreadChipCallback {
+    /**
+     * This method is called when a spinel frame is received. Thread network
+     * will process the received spinel frame.
+     *
+     * Spinel Protocol:
+     *     https://github.com/openthread/openthread/blob/main/src/lib/spinel/spinel.h
+     *
+     * @param frame  The received spinel frame.
+     */
+    oneway void onReceiveSpinelFrame(in byte[] frame);
+}
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>
diff --git a/threadnetwork/aidl/vts/Android.bp b/threadnetwork/aidl/vts/Android.bp
new file mode 100644
index 0000000..864e885
--- /dev/null
+++ b/threadnetwork/aidl/vts/Android.bp
@@ -0,0 +1,38 @@
+//
+// 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.
+//
+
+cc_test {
+    name: "VtsHalThreadNetworkTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: [
+        "VtsHalThreadNetworkTargetTest.cpp",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "libbinder_ndk",
+    ],
+    static_libs: [
+        "android.hardware.threadnetwork-V1-ndk",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
diff --git a/threadnetwork/aidl/vts/VtsHalThreadNetworkTargetTest.cpp b/threadnetwork/aidl/vts/VtsHalThreadNetworkTargetTest.cpp
new file mode 100644
index 0000000..04c6dea
--- /dev/null
+++ b/threadnetwork/aidl/vts/VtsHalThreadNetworkTargetTest.cpp
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "ThreadNetworkHalTargetTest"
+
+#include <future>
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_manager.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <log/log.h>
+
+#include <aidl/android/hardware/threadnetwork/BnThreadChipCallback.h>
+#include <aidl/android/hardware/threadnetwork/IThreadChip.h>
+
+using aidl::android::hardware::threadnetwork::BnThreadChipCallback;
+using aidl::android::hardware::threadnetwork::IThreadChip;
+using android::ProcessState;
+using ndk::ScopedAStatus;
+using ndk::SpAIBinder;
+
+namespace {
+constexpr static int kCallbackTimeoutMs = 5000;
+}  // namespace
+
+class ThreadChipCallback : public BnThreadChipCallback {
+  public:
+    ThreadChipCallback(const std::function<void(const std::vector<uint8_t>&)>& on_spinel_message_cb)
+        : on_spinel_message_cb_(on_spinel_message_cb) {}
+
+    ScopedAStatus onReceiveSpinelFrame(const std::vector<uint8_t>& in_aFrame) {
+        on_spinel_message_cb_(in_aFrame);
+        return ScopedAStatus::ok();
+    }
+
+  private:
+    std::function<void(const std::vector<uint8_t>&)> on_spinel_message_cb_;
+};
+
+class ThreadNetworkAidl : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        std::string serviceName = GetParam();
+
+        ALOGI("serviceName: %s", serviceName.c_str());
+
+        thread_chip = IThreadChip::fromBinder(
+                SpAIBinder(AServiceManager_waitForService(serviceName.c_str())));
+        ASSERT_NE(thread_chip, nullptr);
+    }
+
+    virtual void TearDown() override { thread_chip->close(); }
+
+    std::shared_ptr<IThreadChip> thread_chip;
+};
+
+TEST_P(ThreadNetworkAidl, Open) {
+    std::shared_ptr<ThreadChipCallback> callback =
+            ndk::SharedRefBase::make<ThreadChipCallback>([](auto /* data */) {});
+
+    EXPECT_TRUE(thread_chip->open(callback).isOk());
+}
+
+TEST_P(ThreadNetworkAidl, Close) {
+    std::shared_ptr<ThreadChipCallback> callback =
+            ndk::SharedRefBase::make<ThreadChipCallback>([](auto /* data */) {});
+
+    EXPECT_TRUE(thread_chip->open(callback).isOk());
+    EXPECT_TRUE(thread_chip->close().isOk());
+}
+
+TEST_P(ThreadNetworkAidl, Reset) {
+    std::shared_ptr<ThreadChipCallback> callback =
+            ndk::SharedRefBase::make<ThreadChipCallback>([](auto /* data */) {});
+
+    EXPECT_TRUE(thread_chip->open(callback).isOk());
+    EXPECT_TRUE(thread_chip->reset().isOk());
+}
+
+TEST_P(ThreadNetworkAidl, SendSpinelFrame) {
+    const uint8_t kCmdOffset = 2;
+    const uint8_t kMajorVersionOffset = 3;
+    const uint8_t kMinorVersionOffset = 4;
+    const std::vector<uint8_t> kGetSpinelProtocolVersion({0x81, 0x02, 0x01});
+    const std::vector<uint8_t> kGetSpinelProtocolVersionResponse({0x81, 0x06, 0x01, 0x04, 0x03});
+    uint8_t min_major_version = kGetSpinelProtocolVersionResponse[kMajorVersionOffset];
+    uint8_t min_minor_version = kGetSpinelProtocolVersionResponse[kMinorVersionOffset];
+    uint8_t major_version;
+    uint8_t minor_version;
+    std::promise<void> open_cb_promise;
+    std::future<void> open_cb_future{open_cb_promise.get_future()};
+    std::shared_ptr<ThreadChipCallback> callback;
+    std::vector<uint8_t> received_frame;
+    std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+
+    callback = ndk::SharedRefBase::make<ThreadChipCallback>(
+            [&](const std::vector<uint8_t>& in_aFrame) {
+                if (in_aFrame.size() == kGetSpinelProtocolVersionResponse.size() &&
+                    in_aFrame[kCmdOffset] == kGetSpinelProtocolVersionResponse[kCmdOffset]) {
+                    major_version = in_aFrame[kMajorVersionOffset];
+                    minor_version = in_aFrame[kMinorVersionOffset];
+                    open_cb_promise.set_value();
+                }
+            });
+
+    ASSERT_NE(callback, nullptr);
+
+    EXPECT_TRUE(thread_chip->open(callback).isOk());
+
+    EXPECT_TRUE(thread_chip->sendSpinelFrame(kGetSpinelProtocolVersion).isOk());
+    EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+
+    EXPECT_GE(major_version, min_major_version);
+    if (major_version == min_major_version) {
+        EXPECT_GE(minor_version, min_minor_version);
+    }
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ThreadNetworkAidl);
+INSTANTIATE_TEST_SUITE_P(
+        Thread, ThreadNetworkAidl,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IThreadChip::descriptor)),
+        android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ProcessState::self()->setThreadPoolMaxThreadCount(1);
+    ProcessState::self()->startThreadPool();
+    return RUN_ALL_TESTS();
+}