Add a test wakeupclient service implementation.

The test implementation will send out a remote task to remote access
HAL every 5s.

Test: Manually test on gcar_emu.
adb root
adb remount
adb reboot
adb root
adb remount
m -j TestWakeupClientServer
cd out/target/product/emulator_car64_x86_64
adb push ./vendor/bin/TestWakeupClientServer /vendor/bin
adb shell

In the shell:
su
/vendor/bin/TestWakeupClientServer

Check adb logcat, verify tasks are received.
Bug: 246841306

Change-Id: Idaf198662f7004e3a9e77d75caeffc00cda49218
diff --git a/automotive/remoteaccess/hal/default/Android.bp b/automotive/remoteaccess/hal/default/Android.bp
new file mode 100644
index 0000000..25bda3e
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/Android.bp
@@ -0,0 +1,67 @@
+/*
+ * 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_binary {
+    name: "android.hardware.automotive.remoteaccess@V1-default-service",
+    vendor: true,
+    vintf_fragments: ["remoteaccess-default-service.xml"],
+    init_rc: ["remoteaccess-default-service.rc"],
+    relative_install_path: "hw",
+    srcs: ["src/RemoteAccessImpl.cpp"],
+    whole_static_libs: [
+        "RemoteAccessService",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+        "liblog",
+        "libutils",
+        "libgrpc++",
+        "libprotobuf-cpp-full",
+    ],
+    cflags: [
+        "-Wno-unused-parameter",
+        "-DGRPC_SERVICE_ADDRESS=\"localhost:50051\"",
+    ],
+}
+
+cc_library {
+    name: "RemoteAccessService",
+    vendor: true,
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+    srcs: [
+        "src/RemoteAccessService.cpp",
+    ],
+    whole_static_libs: [
+        "android.hardware.automotive.remoteaccess-V1-ndk",
+        "wakeup_client_protos",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "liblog",
+        "libutils",
+        "libgrpc++",
+        "libprotobuf-cpp-full",
+    ],
+    cflags: [
+        "-Wno-unused-parameter",
+    ],
+}
diff --git a/automotive/remoteaccess/hal/default/include/RemoteAccessService.h b/automotive/remoteaccess/hal/default/include/RemoteAccessService.h
new file mode 100644
index 0000000..806440a
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/include/RemoteAccessService.h
@@ -0,0 +1,83 @@
+/*
+ * 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/automotive/remoteaccess/ApState.h>
+#include <aidl/android/hardware/automotive/remoteaccess/BnRemoteAccess.h>
+#include <aidl/android/hardware/automotive/remoteaccess/IRemoteTaskCallback.h>
+#include <android-base/thread_annotations.h>
+#include <wakeup_client.grpc.pb.h>
+
+#include <string>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace remoteaccess {
+
+class RemoteAccessService
+    : public aidl::android::hardware::automotive::remoteaccess::BnRemoteAccess {
+  public:
+    explicit RemoteAccessService(WakeupClient::StubInterface* grpcStub);
+
+    ~RemoteAccessService();
+
+    ndk::ScopedAStatus getDeviceId(std::string* deviceId) override;
+
+    ndk::ScopedAStatus getWakeupServiceName(std::string* wakeupServiceName) override;
+
+    ndk::ScopedAStatus setRemoteTaskCallback(
+            const std::shared_ptr<
+                    aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback>&
+                    callback) override;
+
+    ndk::ScopedAStatus clearRemoteTaskCallback() override;
+
+    ndk::ScopedAStatus notifyApStateChange(
+            const aidl::android::hardware::automotive::remoteaccess::ApState& newState) override;
+
+  private:
+    // For testing.
+    friend class RemoteAccessServiceUnitTest;
+
+    WakeupClient::StubInterface* mGrpcStub;
+    std::thread mThread;
+    std::mutex mLock;
+    std::condition_variable mCv;
+    std::shared_ptr<aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback>
+            mRemoteTaskCallback GUARDED_BY(mLock);
+    std::unique_ptr<grpc::ClientContext> mGetRemoteTasksContext GUARDED_BY(mLock);
+    // Associated with mCv to notify the task loop to stop waiting and exit.
+    bool mTaskWaitStopped GUARDED_BY(mLock);
+    // A mutex to make sure startTaskLoop does not overlap with stopTaskLoop.
+    std::mutex mStartStopTaskLoopLock;
+    bool mTaskLoopRunning GUARDED_BY(mStartStopTaskLoopLock);
+    // Default wait time before retry connecting to remote access client is 10s.
+    size_t mRetryWaitInMs = 10'000;
+
+    void runTaskLoop();
+    void maybeStartTaskLoop();
+    void maybeStopTaskLoop();
+
+    void setRetryWaitInMs(size_t retryWaitInMs) { mRetryWaitInMs = retryWaitInMs; }
+};
+
+}  // namespace remoteaccess
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/remoteaccess/hal/default/proto/Android.bp b/automotive/remoteaccess/hal/default/proto/Android.bp
new file mode 100644
index 0000000..31b9d0e
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/proto/Android.bp
@@ -0,0 +1,81 @@
+// 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+genrule {
+    name: "wakeup_client_pb_h",
+    tools: [
+        "aprotoc",
+        "protoc-gen-grpc-cpp-plugin",
+    ],
+    cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
+    srcs: [
+        "wakeup_client.proto",
+    ],
+    out: [
+        "wakeup_client.pb.h",
+        "wakeup_client.grpc.pb.h",
+    ],
+    vendor: true,
+}
+
+genrule {
+    name: "wakeup_client_pb_cc",
+    tools: [
+        "aprotoc",
+        "protoc-gen-grpc-cpp-plugin",
+    ],
+    cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
+    srcs: [
+        "wakeup_client.proto",
+    ],
+    out: [
+        "wakeup_client.pb.cc",
+        "wakeup_client.grpc.pb.cc",
+    ],
+    vendor: true,
+}
+
+cc_library_static {
+    name: "wakeup_client_protos",
+    vendor: true,
+    host_supported: true,
+    include_dirs: [
+        "external/protobuf/src",
+    ],
+    generated_headers: [
+        "wakeup_client_pb_h",
+    ],
+    export_generated_headers: [
+        "wakeup_client_pb_h",
+    ],
+    generated_sources: [
+        "wakeup_client_pb_cc",
+    ],
+    shared_libs: [
+        "libgrpc++",
+        "libprotobuf-cpp-full",
+    ],
+    cflags: [
+        "-Wno-unused-parameter",
+    ],
+}
diff --git a/automotive/remoteaccess/hal/default/proto/wakeup_client.proto b/automotive/remoteaccess/hal/default/proto/wakeup_client.proto
new file mode 100644
index 0000000..4fe0d01
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/proto/wakeup_client.proto
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+package android.hardware.automotive.remoteaccess;
+
+/**
+ * Service provided by a wakeup client running on TCU.
+ */
+service WakeupClient {
+    /**
+     * Establish a long-live connection to receive remote tasks.
+     *
+     * <p>For the server, whenever a remote task arrives, if the connection is
+     * alive, it will use the return stream to return a task's information.
+     *
+     * <p>If the connection is not alive, the server must stores the remote task
+     * until a new connection is established (which means AP is ready to
+     * receive remote task again) and send the stored tasks.
+     *
+     * <p>If the server closes the connection, the client will try to
+     * reestablish the connection.
+     */
+    rpc GetRemoteTasks(GetRemoteTasksRequest) returns (stream GetRemoteTasksResponse) {}
+
+    /**
+     * Notifies whether AP is required to be waken up when remote task arrives.
+     *
+     * <p>Wakeup client should store and use this state until a new call with a
+     * different state arrives.
+     *
+     * <p>If {@code isWakeupRequired} in the request is true, it must wake up AP
+     * when a remote task arrives.
+     *
+     * <p>If {@code isWakeupRequired} in the request is false, it must not try
+     * to wake up AP.
+     */
+    rpc NotifyWakeupRequired(NotifyWakeupRequiredRequest) returns (NotifyWakeupRequiredResponse) {}
+}
+
+message GetRemoteTasksRequest {}
+
+message GetRemoteTasksResponse {
+    string clientId = 1;
+    bytes data = 2;
+}
+
+message NotifyWakeupRequiredRequest {
+    bool isWakeupRequired = 1;
+}
+
+message NotifyWakeupRequiredResponse {}
diff --git a/automotive/remoteaccess/hal/default/remoteaccess-default-service.rc b/automotive/remoteaccess/hal/default/remoteaccess-default-service.rc
new file mode 100644
index 0000000..b7a9cdc
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/remoteaccess-default-service.rc
@@ -0,0 +1,4 @@
+service vendor.remoteaccess-default /vendor/bin/hw/android.hardware.automotive.remoteaccess@V1-default-service
+    class hal
+    user vehicle_network
+    group system inet
diff --git a/automotive/remoteaccess/hal/default/remoteaccess-default-service.xml b/automotive/remoteaccess/hal/default/remoteaccess-default-service.xml
new file mode 100644
index 0000000..d050a1b
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/remoteaccess-default-service.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.automotive.remoteaccess</name>
+        <version>1</version>
+        <fqname>IRemoteAccess/default</fqname>
+    </hal>
+</manifest>
diff --git a/automotive/remoteaccess/hal/default/src/RemoteAccessImpl.cpp b/automotive/remoteaccess/hal/default/src/RemoteAccessImpl.cpp
new file mode 100644
index 0000000..8720c2f
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/src/RemoteAccessImpl.cpp
@@ -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.
+ */
+
+#define LOG_TAG "RemoteAccessImpl"
+
+#include "RemoteAccessService.h"
+
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <grpcpp/create_channel.h>
+#include <stdlib.h>
+#include <utils/Log.h>
+
+constexpr char SERVICE_NAME[] = "android.hardware.automotive.remoteaccess.IRemoteAccess/default";
+
+int main(int /* argc */, char* /* argv */[]) {
+    ALOGI("Registering RemoteAccessService as service...");
+
+#ifndef GRPC_SERVICE_ADDRESS
+    ALOGE("GRPC_SERVICE_ADDRESS is not defined, exiting");
+    exit(1);
+#endif
+    auto channel = grpc::CreateChannel(GRPC_SERVICE_ADDRESS, grpc::InsecureChannelCredentials());
+    auto clientStub = android::hardware::automotive::remoteaccess::WakeupClient::NewStub(channel);
+    auto service = ndk::SharedRefBase::make<
+            android::hardware::automotive::remoteaccess::RemoteAccessService>(clientStub.get());
+
+    binder_exception_t err = AServiceManager_addService(service->asBinder().get(), SERVICE_NAME);
+    if (err != EX_NONE) {
+        ALOGE("failed to register android.hardware.automotive.remote.IRemoteAccess service, "
+              "exception: %d",
+              err);
+        exit(1);
+    }
+
+    if (!ABinderProcess_setThreadPoolMaxThreadCount(1)) {
+        ALOGE("%s", "failed to set thread pool max thread count");
+        exit(1);
+    }
+    ABinderProcess_startThreadPool();
+
+    ALOGI("RemoteAccess service Ready");
+
+    ABinderProcess_joinThreadPool();
+
+    ALOGW("Should not reach here");
+
+    return 0;
+}
diff --git a/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp b/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
new file mode 100644
index 0000000..6c297e3
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
@@ -0,0 +1,190 @@
+/*
+ * 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 "RemoteAccessService.h"
+
+#include <android/binder_status.h>
+#include <grpc++/grpc++.h>
+#include <utils/Log.h>
+#include <chrono>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace remoteaccess {
+
+namespace {
+
+using ::aidl::android::hardware::automotive::remoteaccess::ApState;
+using ::aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback;
+using ::android::base::ScopedLockAssertion;
+using ::grpc::ClientContext;
+using ::grpc::ClientReaderInterface;
+using ::grpc::Status;
+using ::grpc::StatusCode;
+using ::ndk::ScopedAStatus;
+
+const std::string WAKEUP_SERVICE_NAME = "com.google.vehicle.wakeup";
+
+std::vector<uint8_t> stringToBytes(const std::string& s) {
+    const char* data = s.data();
+    return std::vector<uint8_t>(data, data + s.size());
+}
+
+ScopedAStatus rpcStatusToScopedAStatus(const Status& status, const std::string& errorMsg) {
+    return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+            status.error_code(), (errorMsg + ", error: " + status.error_message()).c_str());
+}
+
+}  // namespace
+
+RemoteAccessService::RemoteAccessService(WakeupClient::StubInterface* grpcStub)
+    : mGrpcStub(grpcStub){};
+
+RemoteAccessService::~RemoteAccessService() {
+    maybeStopTaskLoop();
+}
+
+void RemoteAccessService::maybeStartTaskLoop() {
+    std::lock_guard<std::mutex> lockGuard(mStartStopTaskLoopLock);
+    if (mTaskLoopRunning) {
+        return;
+    }
+
+    mThread = std::thread([this]() { runTaskLoop(); });
+
+    mTaskLoopRunning = true;
+}
+
+void RemoteAccessService::maybeStopTaskLoop() {
+    std::lock_guard<std::mutex> lockGuard(mStartStopTaskLoopLock);
+    if (!mTaskLoopRunning) {
+        return;
+    }
+
+    {
+        std::lock_guard<std::mutex> lockGuard(mLock);
+        // Try to stop the reading stream.
+        if (mGetRemoteTasksContext) {
+            mGetRemoteTasksContext->TryCancel();
+            mGetRemoteTasksContext.reset();
+        }
+        mTaskWaitStopped = true;
+        mCv.notify_all();
+    }
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+
+    mTaskLoopRunning = false;
+}
+
+void RemoteAccessService::runTaskLoop() {
+    GetRemoteTasksRequest request = {};
+    std::unique_ptr<ClientReaderInterface<GetRemoteTasksResponse>> reader;
+    while (true) {
+        {
+            std::lock_guard<std::mutex> lockGuard(mLock);
+            mGetRemoteTasksContext.reset(new ClientContext());
+            reader = mGrpcStub->GetRemoteTasks(mGetRemoteTasksContext.get(), request);
+        }
+        GetRemoteTasksResponse response;
+        while (reader->Read(&response)) {
+            ALOGI("Receiving one task from remote task client");
+
+            std::shared_ptr<IRemoteTaskCallback> callback;
+            {
+                std::lock_guard<std::mutex> lockGuard(mLock);
+                callback = mRemoteTaskCallback;
+            }
+            if (callback == nullptr) {
+                ALOGD("No callback registered, task ignored");
+                continue;
+            }
+            ALOGD("Calling onRemoteTaskRequested callback for client ID: %s",
+                  response.clientid().c_str());
+            ScopedAStatus callbackStatus = callback->onRemoteTaskRequested(
+                    response.clientid(), stringToBytes(response.data()));
+            if (!callbackStatus.isOk()) {
+                ALOGE("Failed to call onRemoteTaskRequested callback, status: %d, message: %s",
+                      callbackStatus.getStatus(), callbackStatus.getMessage());
+            }
+        }
+        Status status = reader->Finish();
+
+        ALOGE("GetRemoteTasks stream breaks, code: %d, message: %s, sleeping for 10s and retry",
+              status.error_code(), status.error_message().c_str());
+        // The long lasting connection should not return. But if the server returns, retry after
+        // 10s.
+        {
+            std::unique_lock lk(mLock);
+            if (mCv.wait_for(lk, std::chrono::milliseconds(mRetryWaitInMs), [this] {
+                    ScopedLockAssertion lockAssertion(mLock);
+                    return mTaskWaitStopped;
+                })) {
+                // If the stopped flag is set, we are quitting, exit the loop.
+                break;
+            }
+        }
+    }
+}
+
+ScopedAStatus RemoteAccessService::getDeviceId(std::string* deviceId) {
+    // TODO(b/241483300): Call VHAL to get VIN.
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteAccessService::getWakeupServiceName(std::string* wakeupServiceName) {
+    *wakeupServiceName = WAKEUP_SERVICE_NAME;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteAccessService::setRemoteTaskCallback(
+        [[maybe_unused]] const std::shared_ptr<IRemoteTaskCallback>& callback) {
+    std::lock_guard<std::mutex> lockGuard(mLock);
+    mRemoteTaskCallback = callback;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteAccessService::clearRemoteTaskCallback() {
+    std::lock_guard<std::mutex> lockGuard(mLock);
+    mRemoteTaskCallback.reset();
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteAccessService::notifyApStateChange(const ApState& newState) {
+    ClientContext context;
+    NotifyWakeupRequiredRequest request = {};
+    request.set_iswakeuprequired(newState.isWakeupRequired);
+    NotifyWakeupRequiredResponse response = {};
+    Status status = mGrpcStub->NotifyWakeupRequired(&context, request, &response);
+    if (!status.ok()) {
+        return rpcStatusToScopedAStatus(status, "Failed to notify isWakeupRequired");
+    }
+
+    if (newState.isReadyForRemoteTask) {
+        maybeStartTaskLoop();
+    } else {
+        maybeStopTaskLoop();
+    }
+    return ScopedAStatus::ok();
+}
+
+}  // namespace remoteaccess
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/remoteaccess/hal/default/test/Android.bp b/automotive/remoteaccess/hal/default/test/Android.bp
new file mode 100644
index 0000000..e92440f
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/test/Android.bp
@@ -0,0 +1,43 @@
+// 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+    name: "RemoteAccessServiceUnitTest",
+    vendor: true,
+    srcs: ["*.cpp"],
+    whole_static_libs: [
+        "RemoteAccessService",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+        "liblog",
+        "libutils",
+        "libgrpc++",
+        "libprotobuf-cpp-full",
+    ],
+    // libgrpc++.so is installed as root, require root to access it.
+    require_root: true,
+    static_libs: [
+        "libgtest",
+        "libgmock",
+    ],
+    cflags: [
+        "-Wno-unused-parameter",
+    ],
+    test_suites: ["device-tests"],
+}
diff --git a/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp b/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp
new file mode 100644
index 0000000..11523f6
--- /dev/null
+++ b/automotive/remoteaccess/hal/default/test/RemoteAccessServiceUnitTest.cpp
@@ -0,0 +1,288 @@
+/*
+ * 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 "RemoteAccessService.h"
+
+#include <aidl/android/hardware/automotive/remoteaccess/ApState.h>
+#include <aidl/android/hardware/automotive/remoteaccess/BnRemoteTaskCallback.h>
+#include <gmock/gmock.h>
+#include <grpcpp/test/mock_stream.h>
+#include <gtest/gtest.h>
+#include <wakeup_client.grpc.pb.h>
+#include <chrono>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace remoteaccess {
+
+using ::android::base::ScopedLockAssertion;
+
+using ::aidl::android::hardware::automotive::remoteaccess::ApState;
+using ::aidl::android::hardware::automotive::remoteaccess::BnRemoteTaskCallback;
+
+using ::grpc::ClientAsyncReaderInterface;
+using ::grpc::ClientAsyncResponseReaderInterface;
+using ::grpc::ClientContext;
+using ::grpc::ClientReader;
+using ::grpc::ClientReaderInterface;
+using ::grpc::CompletionQueue;
+using ::grpc::Status;
+using ::grpc::testing::MockClientReader;
+using ::ndk::ScopedAStatus;
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+
+class MockGrpcClientStub : public WakeupClient::StubInterface {
+  public:
+    MOCK_METHOD(ClientReaderInterface<GetRemoteTasksResponse>*, GetRemoteTasksRaw,
+                (ClientContext * context, const GetRemoteTasksRequest& request));
+    MOCK_METHOD(Status, NotifyWakeupRequired,
+                (ClientContext * context, const NotifyWakeupRequiredRequest& request,
+                 NotifyWakeupRequiredResponse* response));
+    // Async methods which we do not care.
+    MOCK_METHOD(ClientAsyncReaderInterface<GetRemoteTasksResponse>*, AsyncGetRemoteTasksRaw,
+                (ClientContext * context, const GetRemoteTasksRequest& request, CompletionQueue* cq,
+                 void* tag));
+    MOCK_METHOD(ClientAsyncReaderInterface<GetRemoteTasksResponse>*, PrepareAsyncGetRemoteTasksRaw,
+                (ClientContext * context, const GetRemoteTasksRequest& request,
+                 CompletionQueue* cq));
+    MOCK_METHOD(ClientAsyncResponseReaderInterface<NotifyWakeupRequiredResponse>*,
+                AsyncNotifyWakeupRequiredRaw,
+                (ClientContext * context, const NotifyWakeupRequiredRequest& request,
+                 CompletionQueue* cq));
+    MOCK_METHOD(ClientAsyncResponseReaderInterface<NotifyWakeupRequiredResponse>*,
+                PrepareAsyncNotifyWakeupRequiredRaw,
+                (ClientContext * context, const NotifyWakeupRequiredRequest& request,
+                 CompletionQueue* cq));
+};
+
+class FakeRemoteTaskCallback : public BnRemoteTaskCallback {
+  public:
+    ScopedAStatus onRemoteTaskRequested(const std::string& clientId,
+                                        const std::vector<uint8_t>& data) override {
+        std::lock_guard<std::mutex> lockGuard(mLock);
+        mDataByClientId[clientId] = data;
+        mTaskCount++;
+        mCv.notify_all();
+        return ScopedAStatus::ok();
+    }
+
+    std::vector<uint8_t> getData(const std::string& clientId) { return mDataByClientId[clientId]; }
+
+    bool wait(size_t taskCount, size_t timeoutInSec) {
+        std::unique_lock<std::mutex> lock(mLock);
+        return mCv.wait_for(lock, std::chrono::seconds(timeoutInSec), [taskCount, this] {
+            ScopedLockAssertion lockAssertion(mLock);
+            return mTaskCount >= taskCount;
+        });
+    }
+
+  private:
+    std::mutex mLock;
+    std::unordered_map<std::string, std::vector<uint8_t>> mDataByClientId GUARDED_BY(mLock);
+    size_t mTaskCount GUARDED_BY(mLock) = 0;
+    std::condition_variable mCv;
+};
+
+class RemoteAccessServiceUnitTest : public ::testing::Test {
+  public:
+    virtual void SetUp() override {
+        mGrpcWakeupClientStub = std::make_unique<MockGrpcClientStub>();
+        mService = ndk::SharedRefBase::make<RemoteAccessService>(mGrpcWakeupClientStub.get());
+    }
+
+    MockGrpcClientStub* getGrpcWakeupClientStub() { return mGrpcWakeupClientStub.get(); }
+
+    RemoteAccessService* getService() { return mService.get(); }
+
+    void setRetryWaitInMs(size_t retryWaitInMs) { mService->setRetryWaitInMs(retryWaitInMs); }
+
+  private:
+    std::unique_ptr<MockGrpcClientStub> mGrpcWakeupClientStub;
+    std::shared_ptr<RemoteAccessService> mService;
+    MockClientReader<GetRemoteTasksResponse>* mMockTaskReader;
+};
+
+TEST_F(RemoteAccessServiceUnitTest, TestGetWakeupServiceName) {
+    std::string serviceName;
+
+    ScopedAStatus status = getService()->getWakeupServiceName(&serviceName);
+
+    EXPECT_TRUE(status.isOk());
+    EXPECT_EQ(serviceName, "com.google.vehicle.wakeup");
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestNotifyApStateChangeWakeupRequired) {
+    bool isWakeupRequired = false;
+    EXPECT_CALL(*getGrpcWakeupClientStub(), NotifyWakeupRequired)
+            .WillOnce([&isWakeupRequired]([[maybe_unused]] ClientContext* context,
+                                          const NotifyWakeupRequiredRequest& request,
+                                          [[maybe_unused]] NotifyWakeupRequiredResponse* response) {
+                isWakeupRequired = request.iswakeuprequired();
+                return Status();
+            });
+
+    ApState newState = {
+            .isWakeupRequired = true,
+    };
+    ScopedAStatus status = getService()->notifyApStateChange(newState);
+
+    EXPECT_TRUE(status.isOk());
+    EXPECT_TRUE(isWakeupRequired);
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasks) {
+    GetRemoteTasksResponse response1;
+    std::vector<uint8_t> testData = {0xde, 0xad, 0xbe, 0xef};
+    response1.set_clientid("1");
+    response1.set_data(testData.data(), testData.size());
+    GetRemoteTasksResponse response2;
+    response2.set_clientid("2");
+    std::shared_ptr<FakeRemoteTaskCallback> callback =
+            ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
+
+    ON_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw)
+            .WillByDefault(
+                    [response1, response2]([[maybe_unused]] ClientContext* context,
+                                           [[maybe_unused]] const GetRemoteTasksRequest& request) {
+                        // mockReader ownership will be transferred to the client so we don't own it
+                        // here.
+                        MockClientReader<GetRemoteTasksResponse>* mockClientReader =
+                                new MockClientReader<GetRemoteTasksResponse>();
+                        EXPECT_CALL(*mockClientReader, Finish()).WillOnce(Return(Status::OK));
+                        EXPECT_CALL(*mockClientReader, Read(_))
+                                .WillOnce(DoAll(SetArgPointee<0>(response1), Return(true)))
+                                .WillOnce(DoAll(SetArgPointee<0>(response2), Return(true)))
+                                .WillRepeatedly(Return(false));
+                        return mockClientReader;
+                    });
+
+    getService()->setRemoteTaskCallback(callback);
+    // Start the long live connection to receive tasks.
+    ApState newState = {
+            .isReadyForRemoteTask = true,
+    };
+    ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk());
+
+    ASSERT_TRUE(callback->wait(/*taskCount=*/2, /*timeoutInSec=*/10))
+            << "Did not receive enough tasks";
+    EXPECT_EQ(callback->getData("1"), testData);
+    EXPECT_EQ(callback->getData("2"), std::vector<uint8_t>());
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasksRetryConnection) {
+    GetRemoteTasksResponse response;
+    std::shared_ptr<FakeRemoteTaskCallback> callback =
+            ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
+
+    ON_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw)
+            .WillByDefault([response]([[maybe_unused]] ClientContext* context,
+                                      [[maybe_unused]] const GetRemoteTasksRequest& request) {
+                // mockReader ownership will be transferred to the client so we don't own it here.
+                MockClientReader<GetRemoteTasksResponse>* mockClientReader =
+                        new MockClientReader<GetRemoteTasksResponse>();
+                EXPECT_CALL(*mockClientReader, Finish()).WillOnce(Return(Status::OK));
+                // Connection fails after receiving one task. Should retry after some time.
+                EXPECT_CALL(*mockClientReader, Read(_))
+                        .WillOnce(DoAll(SetArgPointee<0>(response), Return(true)))
+                        .WillRepeatedly(Return(false));
+                return mockClientReader;
+            });
+
+    getService()->setRemoteTaskCallback(callback);
+    setRetryWaitInMs(100);
+    // Start the long live connection to receive tasks.
+    ApState newState = {
+            .isReadyForRemoteTask = true,
+    };
+    ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk());
+
+    ASSERT_TRUE(callback->wait(/*taskCount=*/2, /*timeoutInSec=*/10))
+            << "Did not receive enough tasks";
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasksDefaultNotReady) {
+    GetRemoteTasksResponse response1;
+    std::vector<uint8_t> testData = {0xde, 0xad, 0xbe, 0xef};
+    response1.set_clientid("1");
+    response1.set_data(testData.data(), testData.size());
+    GetRemoteTasksResponse response2;
+    response2.set_clientid("2");
+    std::shared_ptr<FakeRemoteTaskCallback> callback =
+            ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
+
+    EXPECT_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw).Times(0);
+
+    // Default state is not ready for remote tasks, so no callback will be called.
+    getService()->setRemoteTaskCallback(callback);
+
+    std::this_thread::sleep_for(std::chrono::milliseconds(100));
+}
+
+TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasksNotReadyAfterReady) {
+    GetRemoteTasksResponse response1;
+    std::vector<uint8_t> testData = {0xde, 0xad, 0xbe, 0xef};
+    response1.set_clientid("1");
+    response1.set_data(testData.data(), testData.size());
+    GetRemoteTasksResponse response2;
+    response2.set_clientid("2");
+    std::shared_ptr<FakeRemoteTaskCallback> callback =
+            ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
+
+    ON_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw)
+            .WillByDefault(
+                    [response1, response2]([[maybe_unused]] ClientContext* context,
+                                           [[maybe_unused]] const GetRemoteTasksRequest& request) {
+                        // mockReader ownership will be transferred to the client so we don't own it
+                        // here.
+                        MockClientReader<GetRemoteTasksResponse>* mockClientReader =
+                                new MockClientReader<GetRemoteTasksResponse>();
+                        EXPECT_CALL(*mockClientReader, Finish()).WillOnce(Return(Status::OK));
+                        EXPECT_CALL(*mockClientReader, Read(_))
+                                .WillOnce(DoAll(SetArgPointee<0>(response1), Return(true)))
+                                .WillOnce(DoAll(SetArgPointee<0>(response2), Return(true)))
+                                .WillRepeatedly(Return(false));
+                        return mockClientReader;
+                    });
+    // Should only be called once when is is ready for remote task.
+    EXPECT_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw).Times(1);
+
+    getService()->setRemoteTaskCallback(callback);
+    setRetryWaitInMs(100);
+    // Start the long live connection to receive tasks.
+    ApState newState = {
+            .isReadyForRemoteTask = true,
+    };
+    ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk());
+    ASSERT_TRUE(callback->wait(/*taskCount=*/2, /*timeoutInSec=*/10))
+            << "Did not receive enough tasks";
+
+    // Stop the long live connection.
+    newState.isReadyForRemoteTask = false;
+    ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk());
+
+    // Wait for the retry delay, but the loop should already exit.
+    std::this_thread::sleep_for(std::chrono::milliseconds(150));
+}
+
+}  // namespace remoteaccess
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android