Adds VTS test for Context Hub AIDL

Bug: 194285834
Test: Run test on device with Context Hub AIDL and verify pass
Change-Id: I1b38c4071ceaf30da77ad29b3b564bafe76ab2b8
diff --git a/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp b/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
new file mode 100644
index 0000000..3601f13
--- /dev/null
+++ b/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2021 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 <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+
+#include "VtsHalContexthubUtilsCommon.h"
+
+#include <android/hardware/contexthub/BnContextHub.h>
+#include <android/hardware/contexthub/BnContextHubCallback.h>
+#include <android/hardware/contexthub/IContextHub.h>
+#include <android/hardware/contexthub/IContextHubCallback.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <log/log.h>
+
+#include <cinttypes>
+#include <future>
+
+using ::android::ProcessState;
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+using ::android::hardware::contexthub::AsyncEventType;
+using ::android::hardware::contexthub::ContextHubInfo;
+using ::android::hardware::contexthub::ContextHubMessage;
+using ::android::hardware::contexthub::IContextHub;
+using ::android::hardware::contexthub::IContextHubCallbackDefault;
+using ::android::hardware::contexthub::NanoappBinary;
+using ::android::hardware::contexthub::NanoappInfo;
+using ::android::hardware::contexthub::Setting;
+using ::android::hardware::contexthub::vts_utils::kNonExistentAppId;
+using ::android::hardware::contexthub::vts_utils::waitForCallback;
+
+class ContextHubAidl : public testing::TestWithParam<std::tuple<std::string, int32_t>> {
+  public:
+    virtual void SetUp() override {
+        contextHub = android::waitForDeclaredService<IContextHub>(
+                String16(std::get<0>(GetParam()).c_str()));
+        ASSERT_NE(contextHub, nullptr);
+    }
+
+    uint32_t getHubId() { return std::get<1>(GetParam()); }
+
+    void testSettingChanged(Setting setting);
+
+    sp<IContextHub> contextHub;
+};
+
+TEST_P(ContextHubAidl, TestGetHubs) {
+    std::vector<ContextHubInfo> hubs;
+    ASSERT_TRUE(contextHub->getContextHubs(&hubs).isOk());
+
+    ALOGD("System reports %zu hubs", hubs.size());
+
+    for (const ContextHubInfo& hub : hubs) {
+        ALOGD("Checking hub ID %" PRIu32, hub.id);
+
+        EXPECT_GT(hub.name.size(), 0);
+        EXPECT_GT(hub.vendor.size(), 0);
+        EXPECT_GT(hub.toolchain.size(), 0);
+        EXPECT_GT(hub.peakMips, 0);
+        EXPECT_GT(hub.chrePlatformId, 0);
+        EXPECT_GT(hub.chreApiMajorVersion, 0);
+        EXPECT_GT(hub.chreApiMinorVersion, 0);
+        EXPECT_GT(hub.chrePatchVersion, 0);
+
+        // Minimum 128 byte MTU as required by CHRE API v1.0
+        EXPECT_GE(hub.maxSupportedMessageLengthBytes, UINT32_C(128));
+    }
+}
+
+TEST_P(ContextHubAidl, TestRegisterCallback) {
+    bool success;
+    ASSERT_TRUE(contextHub->registerCallback(getHubId(), new IContextHubCallbackDefault(), &success)
+                        .isOk());
+    ASSERT_TRUE(success);
+}
+
+TEST_P(ContextHubAidl, TestRegisterNullCallback) {
+    bool success;
+    ASSERT_TRUE(contextHub->registerCallback(getHubId(), nullptr, &success).isOk());
+}
+
+// Helper callback that puts the async appInfo callback data into a promise
+class QueryAppsCallback : public android::hardware::contexthub::BnContextHubCallback {
+  public:
+    Status handleNanoappInfo(const std::vector<NanoappInfo>& appInfo) override {
+        ALOGD("Got app info callback with %zu apps", appInfo.size());
+        promise.set_value(appInfo);
+        return Status::ok();
+    }
+
+    Status handleContextHubMessage(const ContextHubMessage& /* msg */,
+                                   const std::vector<String16>& /* msgContentPerms */) override {
+        return Status::ok();
+    }
+
+    Status handleContextHubAsyncEvent(AsyncEventType /* evt */) override { return Status::ok(); }
+
+    Status handleTransactionResult(int32_t /* transactionId */, bool /* success */) override {
+        return Status::ok();
+    }
+
+    std::promise<std::vector<NanoappInfo>> promise;
+};
+
+// Calls queryApps() and checks the returned metadata
+TEST_P(ContextHubAidl, TestQueryApps) {
+    sp<QueryAppsCallback> cb = sp<QueryAppsCallback>::make();
+    bool success;
+    ASSERT_TRUE(contextHub->registerCallback(getHubId(), cb, &success).isOk());
+    ASSERT_TRUE(success);
+
+    ASSERT_TRUE(contextHub->queryNanoapps(getHubId(), &success).isOk());
+    ASSERT_TRUE(success);
+
+    std::vector<NanoappInfo> appInfoList;
+    ASSERT_TRUE(waitForCallback(cb->promise.get_future(), &appInfoList));
+    for (const NanoappInfo& appInfo : appInfoList) {
+        EXPECT_NE(appInfo.nanoappId, UINT64_C(0));
+        EXPECT_NE(appInfo.nanoappId, kNonExistentAppId);
+    }
+}
+
+// Helper callback that puts the TransactionResult for the expectedTransactionId into a
+// promise
+class TransactionResultCallback : public android::hardware::contexthub::BnContextHubCallback {
+  public:
+    Status handleNanoappInfo(const std::vector<NanoappInfo>& /* appInfo */) override {
+        return Status::ok();
+    }
+
+    Status handleContextHubMessage(const ContextHubMessage& /* msg */,
+                                   const std::vector<String16>& /* msgContentPerms */) override {
+        return Status::ok();
+    }
+
+    Status handleContextHubAsyncEvent(AsyncEventType /* evt */) override { return Status::ok(); }
+
+    Status handleTransactionResult(int32_t transactionId, bool success) override {
+        ALOGD("Got transaction result callback for transactionId %" PRIu32 " (expecting %" PRIu32
+              ") with success %d",
+              transactionId, expectedTransactionId, success);
+        if (transactionId == expectedTransactionId) {
+            promise.set_value(success);
+        }
+        return Status::ok();
+    }
+
+    uint32_t expectedTransactionId = 0;
+    std::promise<bool> promise;
+};
+
+// Parameterized fixture that sets the callback to TransactionResultCallback
+class ContextHubTransactionTest : public ContextHubAidl {
+  public:
+    virtual void SetUp() override {
+        ContextHubAidl::SetUp();
+        bool success;
+        ASSERT_TRUE(contextHub->registerCallback(getHubId(), cb, &success).isOk());
+        ASSERT_TRUE(success);
+    }
+
+    sp<TransactionResultCallback> cb = sp<TransactionResultCallback>::make();
+};
+
+TEST_P(ContextHubTransactionTest, TestSendMessageToNonExistentNanoapp) {
+    ContextHubMessage message;
+    message.nanoappId = kNonExistentAppId;
+    message.messageType = 1;
+    message.messageBody.resize(4);
+    std::fill(message.messageBody.begin(), message.messageBody.end(), 0);
+
+    ALOGD("Sending message to non-existent nanoapp");
+    bool success;
+    ASSERT_TRUE(contextHub->sendMessageToHub(getHubId(), message, &success).isOk());
+    ASSERT_TRUE(success);
+}
+
+TEST_P(ContextHubTransactionTest, TestLoadEmptyNanoapp) {
+    cb->expectedTransactionId = 0123;
+    NanoappBinary emptyApp;
+
+    emptyApp.nanoappId = kNonExistentAppId;
+    emptyApp.nanoappVersion = 1;
+    emptyApp.flags = 0;
+    emptyApp.targetChreApiMajorVersion = 1;
+    emptyApp.targetChreApiMinorVersion = 0;
+
+    ALOGD("Loading empty nanoapp");
+    bool success;
+    ASSERT_TRUE(contextHub->loadNanoapp(getHubId(), emptyApp, cb->expectedTransactionId, &success)
+                        .isOk());
+    if (success) {
+        bool transactionSuccess;
+        ASSERT_TRUE(waitForCallback(cb->promise.get_future(), &transactionSuccess));
+        ASSERT_FALSE(transactionSuccess);
+    }
+}
+
+TEST_P(ContextHubTransactionTest, TestUnloadNonexistentNanoapp) {
+    cb->expectedTransactionId = 1234;
+
+    ALOGD("Unloading nonexistent nanoapp");
+    bool success;
+    ASSERT_TRUE(contextHub
+                        ->unloadNanoapp(getHubId(), kNonExistentAppId, cb->expectedTransactionId,
+                                        &success)
+                        .isOk());
+    if (success) {
+        bool transactionSuccess;
+        ASSERT_TRUE(waitForCallback(cb->promise.get_future(), &transactionSuccess));
+        ASSERT_FALSE(transactionSuccess);
+    }
+}
+
+TEST_P(ContextHubTransactionTest, TestEnableNonexistentNanoapp) {
+    cb->expectedTransactionId = 2345;
+
+    ALOGD("Enabling nonexistent nanoapp");
+    bool success;
+    ASSERT_TRUE(contextHub
+                        ->enableNanoapp(getHubId(), kNonExistentAppId, cb->expectedTransactionId,
+                                        &success)
+                        .isOk());
+    if (success) {
+        bool transactionSuccess;
+        ASSERT_TRUE(waitForCallback(cb->promise.get_future(), &transactionSuccess));
+        ASSERT_FALSE(transactionSuccess);
+    }
+}
+
+TEST_P(ContextHubTransactionTest, TestDisableNonexistentNanoapp) {
+    cb->expectedTransactionId = 3456;
+
+    ALOGD("Disabling nonexistent nanoapp");
+    bool success;
+    ASSERT_TRUE(contextHub
+                        ->disableNanoapp(getHubId(), kNonExistentAppId, cb->expectedTransactionId,
+                                         &success)
+                        .isOk());
+    if (success) {
+        bool transactionSuccess;
+        ASSERT_TRUE(waitForCallback(cb->promise.get_future(), &transactionSuccess));
+        ASSERT_FALSE(transactionSuccess);
+    }
+}
+
+void ContextHubAidl::testSettingChanged(Setting setting) {
+    // In VTS, we only test that sending the values doesn't cause things to blow up - GTS tests
+    // verify the expected E2E behavior in CHRE
+    bool success;
+    ASSERT_TRUE(contextHub->registerCallback(getHubId(), new IContextHubCallbackDefault(), &success)
+                        .isOk());
+    ASSERT_TRUE(success);
+
+    ASSERT_TRUE(contextHub->onSettingChanged(setting, true /* enabled */).isOk());
+    ASSERT_TRUE(contextHub->onSettingChanged(setting, false /* enabled */).isOk());
+
+    ASSERT_TRUE(contextHub->registerCallback(getHubId(), nullptr, &success).isOk());
+    ASSERT_TRUE(success);
+}
+
+TEST_P(ContextHubAidl, TestOnLocationSettingChanged) {
+    testSettingChanged(Setting::LOCATION);
+}
+
+TEST_P(ContextHubAidl, TestOnWifiMainSettingChanged) {
+    testSettingChanged(Setting::WIFI_MAIN);
+}
+
+TEST_P(ContextHubAidl, TestOnWifiScanningSettingChanged) {
+    testSettingChanged(Setting::WIFI_SCANNING);
+}
+
+TEST_P(ContextHubAidl, TestOnAirplaneModeSettingChanged) {
+    testSettingChanged(Setting::AIRPLANE_MODE);
+}
+
+TEST_P(ContextHubAidl, TestOnMicrophoneSettingChanged) {
+    testSettingChanged(Setting::MICROPHONE);
+}
+
+std::vector<std::tuple<std::string, int32_t>> generateContextHubMapping() {
+    std::vector<std::tuple<std::string, int32_t>> tuples;
+    auto contextHubAidlNames = android::getAidlHalInstanceNames(IContextHub::descriptor);
+    std::vector<ContextHubInfo> contextHubInfos;
+
+    for (int i = 0; i < contextHubAidlNames.size(); i++) {
+        auto contextHubName = contextHubAidlNames[i].c_str();
+        auto contextHub = android::waitForDeclaredService<IContextHub>(String16(contextHubName));
+        if (contextHub->getContextHubs(&contextHubInfos).isOk()) {
+            for (auto& info : contextHubInfos) {
+                tuples.push_back(std::make_tuple(contextHubName, info.id));
+            }
+        }
+    }
+
+    return tuples;
+}
+
+std::string PrintGeneratedTest(const testing::TestParamInfo<ContextHubAidl::ParamType>& info) {
+    return std::string("CONTEXT_HUB_ID_") + std::to_string(std::get<1>(info.param));
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ContextHubAidl);
+INSTANTIATE_TEST_SUITE_P(ContextHub, ContextHubAidl, testing::ValuesIn(generateContextHubMapping()),
+                         PrintGeneratedTest);
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ContextHubTransactionTest);
+INSTANTIATE_TEST_SUITE_P(ContextHub, ContextHubTransactionTest,
+                         testing::ValuesIn(generateContextHubMapping()), PrintGeneratedTest);
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ProcessState::self()->setThreadPoolMaxThreadCount(1);
+    ProcessState::self()->startThreadPool();
+    return RUN_ALL_TESTS();
+}