Add VTS for remoteaccess HAL.

Test: atest VtsHalAutomotiveRemoteAccess_TargetTest
Bug: 277967402

Change-Id: I053d91a3a6c76632e166c9500411de278fb40ff7
diff --git a/automotive/remoteaccess/TEST_MAPPING b/automotive/remoteaccess/TEST_MAPPING
new file mode 100644
index 0000000..81c16c1
--- /dev/null
+++ b/automotive/remoteaccess/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "auto-presubmit": [
+    {
+      "name": "VtsHalAutomotiveRemoteAccess_TargetTest"
+    }
+  ]
+}
diff --git a/automotive/remoteaccess/vts/Android.bp b/automotive/remoteaccess/vts/Android.bp
new file mode 100644
index 0000000..8acd6a1
--- /dev/null
+++ b/automotive/remoteaccess/vts/Android.bp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 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"],
+}
+
+cc_test {
+    name: "VtsHalAutomotiveRemoteAccess_TargetTest",
+    srcs: [
+        "src/*.cpp",
+    ],
+    static_libs: [
+        "android.hardware.automotive.remoteaccess-V2-ndk",
+        "libgtest",
+        "libgmock",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+        "automotive-tests",
+        "automotive-general-tests",
+    ],
+    defaults: [
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    require_root: true,
+    disable_framework: true,
+}
diff --git a/automotive/remoteaccess/vts/src/VtsHalAutomotiveRemoteAccess_TargetTest.cpp b/automotive/remoteaccess/vts/src/VtsHalAutomotiveRemoteAccess_TargetTest.cpp
new file mode 100644
index 0000000..0fb743e
--- /dev/null
+++ b/automotive/remoteaccess/vts/src/VtsHalAutomotiveRemoteAccess_TargetTest.cpp
@@ -0,0 +1,453 @@
+/*
+ * Copyright (C) 2023 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 "VtsHalAutomotiveRemoteAccess"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/automotive/remoteaccess/ApState.h>
+#include <aidl/android/hardware/automotive/remoteaccess/BnRemoteTaskCallback.h>
+#include <aidl/android/hardware/automotive/remoteaccess/IRemoteAccess.h>
+#include <aidl/android/hardware/automotive/remoteaccess/ScheduleInfo.h>
+#include <aidl/android/hardware/automotive/remoteaccess/TaskType.h>
+#include <android-base/thread_annotations.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <chrono>
+#include <string>
+#include <thread>
+#include <vector>
+
+using ::aidl::android::hardware::automotive::remoteaccess::ApState;
+using ::aidl::android::hardware::automotive::remoteaccess::BnRemoteTaskCallback;
+using ::aidl::android::hardware::automotive::remoteaccess::IRemoteAccess;
+using ::aidl::android::hardware::automotive::remoteaccess::ScheduleInfo;
+using ::aidl::android::hardware::automotive::remoteaccess::TaskType;
+using ::android::getAidlHalInstanceNames;
+using ::android::PrintInstanceNameToString;
+using ::android::base::ScopedLockAssertion;
+using ::ndk::ScopedAStatus;
+using ::ndk::SharedRefBase;
+using ::ndk::SpAIBinder;
+using ::testing::UnorderedElementsAre;
+
+namespace {
+
+const std::string TEST_CLIENT_ID = "TEST CLIENT ID";
+const std::string TEST_SCHEDULE_ID = "TEST SCHEDULE ID";
+const std::string TEST_SCHEDULE_ID_1 = "TEST SCHEDULE ID 1";
+const std::string TEST_SCHEDULE_ID_2 = "TEST SCHEDULE ID 2";
+const std::vector<uint8_t> TEST_TASK_DATA =
+        std::vector<uint8_t>({static_cast<uint8_t>(0xde), static_cast<uint8_t>(0xad),
+                              static_cast<uint8_t>(0xbe), static_cast<uint8_t>(0xef)});
+const int32_t JOB_DELAY_IN_SECONDS = 5;
+
+}  // namespace
+
+class VtsHalAutomotiveRemoteAccessTargetTest : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        const std::string& name = GetParam();
+        mHal = IRemoteAccess::fromBinder(SpAIBinder(AServiceManager_waitForService(name.c_str())));
+        ASSERT_NE(mHal, nullptr) << "Failed to connect to remote access HAL: " << name;
+    }
+
+    virtual void TearDown() override {
+        if (mHal != nullptr) {
+            mHal->clearRemoteTaskCallback();
+            mHal->unscheduleAllTasks(TEST_CLIENT_ID);
+        }
+    }
+
+  protected:
+    std::shared_ptr<IRemoteAccess> mHal;
+
+    bool isTaskScheduleSupported();
+    int32_t getInterfaceVersion();
+    ScheduleInfo getTestScheduleInfo(int32_t delayInSeconds, int32_t count,
+                                     int32_t periodicInSeconds);
+    void setTaskCallbackAndReadyForTask(std::shared_ptr<BnRemoteTaskCallback> testCallback);
+};
+
+class TestRemoteTaskCallback final : public BnRemoteTaskCallback {
+  public:
+    ScopedAStatus onRemoteTaskRequested(const std::string& clientId,
+                                        const std::vector<uint8_t>& data) override {
+        {
+            std::unique_lock<std::mutex> lockGuard(mLock);
+            mClientIds.push_back(clientId);
+            mDataList.push_back(data);
+        }
+        mCv.notify_one();
+        return ScopedAStatus::ok();
+    }
+
+    const std::vector<std::string> getCalledClientIds() {
+        std::lock_guard<std::mutex> lockGuard(mLock);
+        return mClientIds;
+    }
+
+    const std::vector<std::vector<uint8_t>> getCalledDataList() {
+        std::lock_guard<std::mutex> lockGuard(mLock);
+        return mDataList;
+    }
+
+    bool waitForCallbacks(size_t count, int32_t timeoutInSeconds) {
+        std::unique_lock lk(mLock);
+        return mCv.wait_for(lk, std::chrono::seconds(timeoutInSeconds), [this, count] {
+            ScopedLockAssertion lockAssertion(mLock);
+            return mClientIds.size() >= count;
+        });
+    }
+
+  private:
+    std::mutex mLock;
+    std::vector<std::string> mClientIds GUARDED_BY(mLock);
+    std::vector<std::vector<uint8_t>> mDataList GUARDED_BY(mLock);
+    std::condition_variable mCv;
+};
+
+TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testGetVehicleId) {
+    std::string vehicleId;
+
+    ScopedAStatus status = mHal->getVehicleId(&vehicleId);
+
+    ASSERT_TRUE(status.isOk()) << "Failed to call getVehicleId";
+    ASSERT_FALSE(vehicleId.empty()) << "Vehicle ID must not be empty";
+}
+
+TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testGetWakeupServiceName) {
+    std::string wakeupServiceName;
+
+    ScopedAStatus status = mHal->getWakeupServiceName(&wakeupServiceName);
+
+    ASSERT_TRUE(status.isOk()) << "Failed to call getWakeupServiceName";
+    ASSERT_FALSE(wakeupServiceName.empty()) << "Wakeup service name must not be empty";
+}
+
+TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testGetProcessorId) {
+    std::string processorId;
+
+    ScopedAStatus status = mHal->getProcessorId(&processorId);
+
+    ASSERT_TRUE(status.isOk()) << "Failed to call getProcessorId";
+}
+
+TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testSetClearRemoteTaskCallback) {
+    std::shared_ptr<TestRemoteTaskCallback> testCallback =
+            SharedRefBase::make<TestRemoteTaskCallback>();
+
+    ScopedAStatus status = mHal->setRemoteTaskCallback(testCallback);
+
+    ASSERT_TRUE(status.isOk()) << "Failed to call setRemoteTaskCallback";
+
+    status = mHal->clearRemoteTaskCallback();
+
+    ASSERT_TRUE(status.isOk()) << "Failed to call clearRemoteTaskCallback";
+}
+
+TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testNotifyApStateChange) {
+    ApState apState = ApState{
+            .isReadyForRemoteTask = false,
+            .isWakeupRequired = false,
+    };
+
+    ScopedAStatus status = mHal->notifyApStateChange(apState);
+
+    ASSERT_TRUE(status.isOk()) << "Failed to call notifyApStateChange with state: "
+                               << apState.toString();
+
+    apState = ApState{
+            .isReadyForRemoteTask = true,
+            .isWakeupRequired = false,
+    };
+
+    ASSERT_TRUE(status.isOk()) << "Failed to call notifyApStateChange with state: "
+                               << apState.toString();
+}
+
+int32_t VtsHalAutomotiveRemoteAccessTargetTest::getInterfaceVersion() {
+    int32_t interfaceVersion = 0;
+    mHal->getInterfaceVersion(&interfaceVersion);
+    return interfaceVersion;
+}
+
+TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testIsTaskScheduleSupported) {
+    if (getInterfaceVersion() < 2) {
+        GTEST_SKIP() << "Require RemoteAccess HAL v2";
+    }
+
+    bool supported;
+
+    ScopedAStatus status = mHal->isTaskScheduleSupported(&supported);
+
+    ASSERT_TRUE(status.isOk()) << "Failed to call isTaskScheduleSupported";
+}
+
+bool VtsHalAutomotiveRemoteAccessTargetTest::isTaskScheduleSupported() {
+    bool supported = false;
+    mHal->isTaskScheduleSupported(&supported);
+    return supported;
+}
+
+TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testGetSupportedTaskTypesForScheduling) {
+    if (getInterfaceVersion() < 2) {
+        GTEST_SKIP() << "Require RemoteAccess HAL v2";
+    }
+
+    std::vector<TaskType> supportedTaskTypes;
+
+    ScopedAStatus status = mHal->getSupportedTaskTypesForScheduling(&supportedTaskTypes);
+
+    ASSERT_TRUE(status.isOk()) << "Failed to call getSupportedTaskTypesForScheduling";
+
+    if (!isTaskScheduleSupported()) {
+        ASSERT_TRUE(supportedTaskTypes.empty())
+                << "getSupportedTaskTypesForScheduling must return empty array "
+                << "if isTaskScheduleSupported is false";
+        return;
+    }
+
+    ASSERT_TRUE(std::find(supportedTaskTypes.begin(), supportedTaskTypes.end(), TaskType::CUSTOM) !=
+                supportedTaskTypes.end())
+            << "getSupportedTaskTypesForScheduling must contain TaskType::CUSTOM";
+}
+
+ScheduleInfo VtsHalAutomotiveRemoteAccessTargetTest::getTestScheduleInfo(
+        int32_t delayInSeconds, int32_t count, int32_t periodicInSeconds) {
+    auto nowInEpochSeconds = std::chrono::duration_cast<std::chrono::seconds>(
+                                     std::chrono::system_clock::now().time_since_epoch())
+                                     .count();
+
+    return ScheduleInfo{
+            .clientId = TEST_CLIENT_ID,
+            .scheduleId = TEST_SCHEDULE_ID,
+            .taskType = TaskType::CUSTOM,
+            .taskData = TEST_TASK_DATA,
+            .count = count,
+            .startTimeInEpochSeconds = nowInEpochSeconds + delayInSeconds,
+            .periodicInSeconds = periodicInSeconds,
+    };
+}
+
+void VtsHalAutomotiveRemoteAccessTargetTest::setTaskCallbackAndReadyForTask(
+        std::shared_ptr<BnRemoteTaskCallback> testCallback) {
+    mHal->setRemoteTaskCallback(testCallback);
+    // Notify isReadForRemoteTask to be true.
+    mHal->notifyApStateChange(ApState{
+            .isReadyForRemoteTask = true,
+            .isWakeupRequired = false,
+    });
+}
+
+TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testScheduleTask) {
+    if (getInterfaceVersion() < 2) {
+        GTEST_SKIP() << "Require RemoteAccess HAL v2";
+    }
+
+    std::shared_ptr<TestRemoteTaskCallback> testCallback =
+            SharedRefBase::make<TestRemoteTaskCallback>();
+    setTaskCallbackAndReadyForTask(testCallback);
+
+    int32_t count = 2;
+    ScheduleInfo scheduleInfo = getTestScheduleInfo(
+            /*delayInSeconds=*/JOB_DELAY_IN_SECONDS, count, /*periodicInSeconds=*/1);
+    ScopedAStatus status = mHal->scheduleTask(scheduleInfo);
+
+    if (!isTaskScheduleSupported()) {
+        ASSERT_FALSE(status.isOk()) << "scheduleTask must return EX_ILLEGAL_ARGUMENT "
+                                    << "if isTaskScheduleSupported is false";
+        ASSERT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT)
+                << "scheduleTask must return EX_ILLEGAL_ARGUMENT "
+                << "if isTaskScheduleSupported is false";
+        return;
+    }
+
+    ASSERT_TRUE(status.isOk()) << "Failed to call scheduleTask with scheduleInfo: "
+                               << scheduleInfo.toString();
+
+    int32_t timeoutInSeconds = JOB_DELAY_IN_SECONDS + 5;
+    bool gotCallbacks = testCallback->waitForCallbacks(count, timeoutInSeconds);
+    // unschedule the task before checking the result.
+    mHal->unscheduleTask(TEST_CLIENT_ID, TEST_SCHEDULE_ID);
+
+    ASSERT_TRUE(gotCallbacks) << "Callbacks is not called enough times before timeout: "
+                              << timeoutInSeconds << "s";
+    std::vector<std::vector<uint8_t>> dataList = testCallback->getCalledDataList();
+    std::vector<std::string> clientIds = testCallback->getCalledClientIds();
+
+    for (size_t i = 0; i < dataList.size(); i++) {
+        EXPECT_EQ(dataList[i], TEST_TASK_DATA) << "Must receive expected task data";
+        EXPECT_EQ(clientIds[i], TEST_CLIENT_ID) << "Must receive expected client id";
+    }
+}
+
+TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testUnscheduleTask) {
+    if (getInterfaceVersion() < 2) {
+        GTEST_SKIP() << "Require RemoteAccess HAL v2";
+    }
+
+    std::shared_ptr<TestRemoteTaskCallback> testCallback =
+            SharedRefBase::make<TestRemoteTaskCallback>();
+    setTaskCallbackAndReadyForTask(testCallback);
+
+    ScheduleInfo scheduleInfo = getTestScheduleInfo(
+            /*delayInSeconds=*/JOB_DELAY_IN_SECONDS, /*count=*/1, /*periodicInSeconds=*/0);
+    mHal->scheduleTask(scheduleInfo);
+    ScopedAStatus status = mHal->unscheduleTask(TEST_CLIENT_ID, TEST_SCHEDULE_ID);
+
+    ASSERT_TRUE(status.isOk()) << "Failed to call unscheduleTask";
+
+    // If not cancelled, should be called in 5s, wait for 6s and make sure no task arrives.
+    std::this_thread::sleep_for(std::chrono::seconds(JOB_DELAY_IN_SECONDS + 1));
+
+    ASSERT_TRUE(testCallback->getCalledClientIds().empty())
+            << "Remote task callback must not be called if the task is cancelled";
+}
+
+TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testUnscheduleAllTasks) {
+    if (getInterfaceVersion() < 2) {
+        GTEST_SKIP() << "Require RemoteAccess HAL v2";
+    }
+
+    std::shared_ptr<TestRemoteTaskCallback> testCallback =
+            SharedRefBase::make<TestRemoteTaskCallback>();
+    setTaskCallbackAndReadyForTask(testCallback);
+
+    ScheduleInfo scheduleInfo = getTestScheduleInfo(
+            /*delayInSeconds=*/JOB_DELAY_IN_SECONDS, /*count=*/1, /*periodicInSeconds=*/0);
+    mHal->scheduleTask(scheduleInfo);
+    ScopedAStatus status = mHal->unscheduleAllTasks(TEST_CLIENT_ID);
+
+    ASSERT_TRUE(status.isOk()) << "Failed to call unscheduleAllTasks";
+
+    // If not cancelled, should be called in 5s, wait for 6s and make sure no task arrives.
+    std::this_thread::sleep_for(std::chrono::seconds(JOB_DELAY_IN_SECONDS + 1));
+
+    ASSERT_TRUE(testCallback->getCalledClientIds().empty())
+            << "Remote task callback must not be called if the task is cancelled";
+}
+
+TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testIsTaskScheduled) {
+    if (getInterfaceVersion() < 2) {
+        GTEST_SKIP() << "Require RemoteAccess HAL v2";
+    }
+
+    std::shared_ptr<TestRemoteTaskCallback> testCallback =
+            SharedRefBase::make<TestRemoteTaskCallback>();
+    setTaskCallbackAndReadyForTask(testCallback);
+
+    ScheduleInfo scheduleInfo = getTestScheduleInfo(
+            /*delayInSeconds=*/JOB_DELAY_IN_SECONDS, /*count=*/1, /*periodicInSeconds=*/0);
+    mHal->scheduleTask(scheduleInfo);
+
+    bool scheduled;
+    ScopedAStatus status = mHal->isTaskScheduled(TEST_CLIENT_ID, TEST_SCHEDULE_ID, &scheduled);
+
+    ASSERT_TRUE(status.isOk()) << "Failed to call unscheduleTask";
+
+    if (!isTaskScheduleSupported()) {
+        ASSERT_FALSE(scheduled) << "isTaskScheduled must return false "
+                                << "if isTaskScheduleSupported is false";
+        return;
+    }
+
+    ASSERT_TRUE(scheduled) << "isTaskScheduled must return true if the task is scheduled";
+
+    mHal->unscheduleAllTasks(TEST_CLIENT_ID);
+    status = mHal->isTaskScheduled(TEST_CLIENT_ID, TEST_SCHEDULE_ID, &scheduled);
+
+    ASSERT_TRUE(status.isOk()) << "Failed to call unscheduleTask";
+    ASSERT_FALSE(scheduled) << "isTaskScheduled must return false if the task is not scheduled";
+}
+
+TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testGetAllPendingScheduledTasks) {
+    if (getInterfaceVersion() < 2) {
+        GTEST_SKIP() << "Require RemoteAccess HAL v2";
+    }
+
+    std::shared_ptr<TestRemoteTaskCallback> testCallback =
+            SharedRefBase::make<TestRemoteTaskCallback>();
+    setTaskCallbackAndReadyForTask(testCallback);
+
+    auto nowInEpochSeconds = std::chrono::duration_cast<std::chrono::seconds>(
+                                     std::chrono::system_clock::now().time_since_epoch())
+                                     .count();
+
+    ScheduleInfo scheduleInfo1 = ScheduleInfo{
+            .clientId = TEST_CLIENT_ID,
+            .scheduleId = TEST_SCHEDULE_ID_1,
+            .taskType = TaskType::CUSTOM,
+            .taskData = TEST_TASK_DATA,
+            .count = 1,
+            .startTimeInEpochSeconds = nowInEpochSeconds + 5,
+            .periodicInSeconds = 0,
+    };
+    ScheduleInfo scheduleInfo2 = ScheduleInfo{
+            .clientId = TEST_CLIENT_ID,
+            .scheduleId = TEST_SCHEDULE_ID_2,
+            .taskType = TaskType::CUSTOM,
+            .taskData = TEST_TASK_DATA,
+            .count = 10,
+            .startTimeInEpochSeconds = nowInEpochSeconds + 10,
+            .periodicInSeconds = 1,
+    };
+    mHal->scheduleTask(scheduleInfo1);
+    mHal->scheduleTask(scheduleInfo2);
+
+    std::vector<ScheduleInfo> outScheduleInfo;
+    ScopedAStatus status = mHal->getAllPendingScheduledTasks(TEST_CLIENT_ID, &outScheduleInfo);
+
+    ASSERT_TRUE(status.isOk()) << "Failed to call getAllPendingScheduledTasks";
+
+    if (!isTaskScheduleSupported()) {
+        ASSERT_TRUE(outScheduleInfo.empty())
+                << "Must return empty array for getAllPendingScheduledTasks "
+                << "if isTaskScheduleSupported is false";
+        return;
+    }
+
+    ASSERT_THAT(outScheduleInfo, UnorderedElementsAre(scheduleInfo1, scheduleInfo2))
+            << "expected all pending schedule info mismatch";
+
+    mHal->unscheduleTask(TEST_CLIENT_ID, TEST_SCHEDULE_ID_1);
+
+    status = mHal->getAllPendingScheduledTasks(TEST_CLIENT_ID, &outScheduleInfo);
+
+    ASSERT_TRUE(status.isOk()) << "Failed to call getAllPendingScheduledTasks";
+
+    ASSERT_THAT(outScheduleInfo, UnorderedElementsAre(scheduleInfo2))
+            << "expected all pending schedule info mismatch";
+}
+
+// It is possible that no remote access HAL is registered.
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VtsHalAutomotiveRemoteAccessTargetTest);
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, VtsHalAutomotiveRemoteAccessTargetTest,
+                         testing::ValuesIn(getAidlHalInstanceNames(IRemoteAccess::descriptor)),
+                         PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    // Starts a process pool for callbacks.
+    ABinderProcess_setThreadPoolMaxThreadCount(1);
+    ABinderProcess_startThreadPool();
+    return RUN_ALL_TESTS();
+}