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();
+}