1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "contexthub_hidl_hal_test" 18 19 #include <VtsHalHidlTargetTestBase.h> 20 #include <VtsHalHidlTargetTestEnvBase.h> 21 #include <android-base/logging.h> 22 #include <android/hardware/contexthub/1.0/IContexthub.h> 23 #include <android/hardware/contexthub/1.0/IContexthubCallback.h> 24 #include <android/hardware/contexthub/1.0/types.h> 25 #include <android/log.h> 26 #include <log/log.h> 27 28 #include <cinttypes> 29 #include <future> 30 #include <utility> 31 32 using ::android::hardware::Return; 33 using ::android::hardware::Void; 34 using ::android::hardware::hidl_string; 35 using ::android::hardware::hidl_vec; 36 using ::android::hardware::contexthub::V1_0::AsyncEventType; 37 using ::android::hardware::contexthub::V1_0::ContextHub; 38 using ::android::hardware::contexthub::V1_0::ContextHubMsg; 39 using ::android::hardware::contexthub::V1_0::HubAppInfo; 40 using ::android::hardware::contexthub::V1_0::IContexthub; 41 using ::android::hardware::contexthub::V1_0::IContexthubCallback; 42 using ::android::hardware::contexthub::V1_0::NanoAppBinary; 43 using ::android::hardware::contexthub::V1_0::Result; 44 using ::android::hardware::contexthub::V1_0::TransactionResult; 45 using ::android::sp; 46 47 #define ASSERT_OK(result) ASSERT_EQ(result, Result::OK) 48 #define EXPECT_OK(result) EXPECT_EQ(result, Result::OK) 49 50 namespace { 51 52 // App ID with vendor "GoogT" (Google Testing), app identifier 0x555555. This 53 // app ID is reserved and must never appear in the list of loaded apps. 54 constexpr uint64_t kNonExistentAppId = 0x476f6f6754555555; 55 56 // Helper that does explicit conversion of an enum class to its underlying/base 57 // type. Useful for stream output of enum values. 58 template<typename EnumType> 59 constexpr typename std::underlying_type<EnumType>::type asBaseType( 60 EnumType value) { 61 return static_cast<typename std::underlying_type<EnumType>::type>(value); 62 } 63 64 // Synchronously queries IContexthub::getHubs() and returns the result 65 hidl_vec<ContextHub> getHubsSync(sp<IContexthub> hubApi) { 66 hidl_vec<ContextHub> hubList; 67 std::promise<void> barrier; 68 69 hubApi->getHubs([&hubList, &barrier](const hidl_vec<ContextHub>& hubs) { 70 hubList = hubs; 71 barrier.set_value(); 72 }); 73 barrier.get_future().wait_for(std::chrono::seconds(1)); 74 75 return hubList; 76 } 77 78 // Gets a list of valid hub IDs in the system 79 std::vector<uint32_t> getHubIds() { 80 static std::vector<uint32_t> hubIds; 81 82 if (hubIds.size() == 0) { 83 sp<IContexthub> hubApi = ::testing::VtsHalHidlTargetTestBase::getService<IContexthub>(); 84 85 if (hubApi != nullptr) { 86 for (ContextHub hub : getHubsSync(hubApi)) { 87 hubIds.push_back(hub.hubId); 88 } 89 } 90 } 91 92 ALOGD("Running tests against all %zu reported hubs", hubIds.size()); 93 return hubIds; 94 } 95 96 // Test environment for Contexthub HIDL HAL. 97 class ContexthubHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase { 98 public: 99 // get the test environment singleton 100 static ContexthubHidlEnvironment* Instance() { 101 static ContexthubHidlEnvironment* instance = new ContexthubHidlEnvironment; 102 return instance; 103 } 104 105 virtual void registerTestServices() override { registerTestService<IContexthub>(); } 106 private: 107 ContexthubHidlEnvironment() {} 108 }; 109 110 // Base test fixture that initializes the HAL and makes the context hub API 111 // handle available 112 class ContexthubHidlTestBase : public ::testing::VtsHalHidlTargetTestBase { 113 public: 114 virtual void SetUp() override { 115 hubApi = ::testing::VtsHalHidlTargetTestBase::getService<IContexthub>( 116 ContexthubHidlEnvironment::Instance()->getServiceName<IContexthub>()); 117 ASSERT_NE(hubApi, nullptr); 118 119 // getHubs() must be called at least once for proper initialization of the 120 // HAL implementation 121 getHubsSync(hubApi); 122 } 123 124 virtual void TearDown() override {} 125 126 sp<IContexthub> hubApi; 127 }; 128 129 // Test fixture parameterized by hub ID 130 class ContexthubHidlTest : public ContexthubHidlTestBase, 131 public ::testing::WithParamInterface<uint32_t> { 132 public: 133 uint32_t getHubId() { 134 return GetParam(); 135 } 136 137 Result registerCallback(sp<IContexthubCallback> cb) { 138 Result result = hubApi->registerCallback(getHubId(), cb); 139 ALOGD("Registered callback, result %" PRIu32, result); 140 return result; 141 } 142 }; 143 144 // Base callback implementation that just logs all callbacks by default 145 class ContexthubCallbackBase : public IContexthubCallback { 146 public: 147 virtual Return<void> handleClientMsg(const ContextHubMsg& /*msg*/) override { 148 ALOGD("Got client message callback"); 149 return Void(); 150 } 151 152 virtual Return<void> handleTxnResult( 153 uint32_t txnId, TransactionResult result) override { 154 ALOGD("Got transaction result callback for txnId %" PRIu32 " with result %" 155 PRId32, txnId, result); 156 return Void(); 157 } 158 159 virtual Return<void> handleHubEvent(AsyncEventType evt) override { 160 ALOGD("Got hub event callback for event type %" PRIu32, evt); 161 return Void(); 162 } 163 164 virtual Return<void> handleAppAbort(uint64_t appId, uint32_t abortCode) 165 override { 166 ALOGD("Got app abort notification for appId 0x%" PRIx64 " with abort code " 167 "0x%" PRIx32, appId, abortCode); 168 return Void(); 169 } 170 171 virtual Return<void> handleAppsInfo(const hidl_vec<HubAppInfo>& /*appInfo*/) 172 override { 173 ALOGD("Got app info callback"); 174 return Void(); 175 } 176 }; 177 178 // Wait for a callback to occur (signaled by the given future) up to the 179 // provided timeout. If the future is invalid or the callback does not come 180 // within the given time, returns false. 181 template<class ReturnType> 182 bool waitForCallback( 183 std::future<ReturnType> future, 184 ReturnType *result, 185 std::chrono::milliseconds timeout = std::chrono::seconds(5)) { 186 auto expiration = std::chrono::system_clock::now() + timeout; 187 188 EXPECT_NE(result, nullptr); 189 EXPECT_TRUE(future.valid()); 190 if (result != nullptr && future.valid()) { 191 std::future_status status = future.wait_until(expiration); 192 EXPECT_NE(status, std::future_status::timeout) 193 << "Timed out waiting for callback"; 194 195 if (status == std::future_status::ready) { 196 *result = future.get(); 197 return true; 198 } 199 } 200 201 return false; 202 } 203 204 // Ensures that the metadata reported in getHubs() is sane 205 TEST_F(ContexthubHidlTestBase, TestGetHubs) { 206 hidl_vec<ContextHub> hubs = getHubsSync(hubApi); 207 ALOGD("System reports %zu hubs", hubs.size()); 208 209 for (ContextHub hub : hubs) { 210 ALOGD("Checking hub ID %" PRIu32, hub.hubId); 211 212 EXPECT_FALSE(hub.name.empty()); 213 EXPECT_FALSE(hub.vendor.empty()); 214 EXPECT_FALSE(hub.toolchain.empty()); 215 EXPECT_GT(hub.peakMips, 0); 216 EXPECT_GE(hub.stoppedPowerDrawMw, 0); 217 EXPECT_GE(hub.sleepPowerDrawMw, 0); 218 EXPECT_GT(hub.peakPowerDrawMw, 0); 219 220 // Minimum 128 byte MTU as required by CHRE API v1.0 221 EXPECT_GE(hub.maxSupportedMsgLen, UINT32_C(128)); 222 } 223 } 224 225 TEST_P(ContexthubHidlTest, TestRegisterCallback) { 226 ALOGD("TestRegisterCallback called, hubId %" PRIu32, getHubId()); 227 ASSERT_OK(registerCallback(new ContexthubCallbackBase())); 228 } 229 230 TEST_P(ContexthubHidlTest, TestRegisterNullCallback) { 231 ALOGD("TestRegisterNullCallback called, hubId %" PRIu32, getHubId()); 232 ASSERT_OK(registerCallback(nullptr)); 233 } 234 235 // Helper callback that puts the async appInfo callback data into a promise 236 class QueryAppsCallback : public ContexthubCallbackBase { 237 public: 238 virtual Return<void> handleAppsInfo(const hidl_vec<HubAppInfo>& appInfo) 239 override { 240 ALOGD("Got app info callback with %zu apps", appInfo.size()); 241 promise.set_value(appInfo); 242 return Void(); 243 } 244 245 std::promise<hidl_vec<HubAppInfo>> promise; 246 }; 247 248 // Calls queryApps() and checks the returned metadata 249 TEST_P(ContexthubHidlTest, TestQueryApps) { 250 ALOGD("TestQueryApps called, hubId %u", getHubId()); 251 sp<QueryAppsCallback> cb = new QueryAppsCallback(); 252 ASSERT_OK(registerCallback(cb)); 253 254 Result result = hubApi->queryApps(getHubId()); 255 ASSERT_OK(result); 256 257 ALOGD("Waiting for app info callback"); 258 hidl_vec<HubAppInfo> appList; 259 ASSERT_TRUE(waitForCallback(cb->promise.get_future(), &appList)); 260 for (const HubAppInfo &appInfo : appList) { 261 EXPECT_NE(appInfo.appId, UINT64_C(0)); 262 EXPECT_NE(appInfo.appId, kNonExistentAppId); 263 } 264 } 265 266 // Helper callback that puts the TransactionResult for the expectedTxnId into a 267 // promise 268 class TxnResultCallback : public ContexthubCallbackBase { 269 public: 270 virtual Return<void> handleTxnResult( 271 uint32_t txnId, TransactionResult result) override { 272 ALOGD("Got transaction result callback for txnId %" PRIu32 " (expecting %" 273 PRIu32 ") with result %" PRId32, txnId, expectedTxnId, result); 274 if (txnId == expectedTxnId) { 275 promise.set_value(result); 276 } 277 return Void(); 278 } 279 280 uint32_t expectedTxnId = 0; 281 std::promise<TransactionResult> promise; 282 }; 283 284 // Parameterized fixture that sets the callback to TxnResultCallback 285 class ContexthubTxnTest : public ContexthubHidlTest { 286 public: 287 virtual void SetUp() override { 288 ContexthubHidlTest::SetUp(); 289 ASSERT_OK(registerCallback(cb)); 290 } 291 292 sp<TxnResultCallback> cb = new TxnResultCallback(); 293 }; 294 295 296 // Checks cases where the hub implementation is expected to return an error, but 297 // that error can be returned either synchronously or in the asynchronous 298 // transaction callback. Returns an AssertionResult that can be used in 299 // ASSERT/EXPECT_TRUE. Allows checking the sync result against 1 additional 300 // allowed error code apart from OK and TRANSACTION_FAILED, which are always 301 // allowed. 302 ::testing::AssertionResult checkFailureSyncOrAsync( 303 Result result, Result allowedSyncResult, 304 std::future<TransactionResult>&& future) { 305 if (result == Result::OK) { 306 // No error reported synchronously - this is OK, but then we should get an 307 // async callback with a failure status 308 TransactionResult asyncResult; 309 if (!waitForCallback(std::forward<std::future<TransactionResult>>(future), 310 &asyncResult)) { 311 return ::testing::AssertionFailure() 312 << "Got successful sync result, then failed to receive async cb"; 313 } else if (asyncResult == TransactionResult::SUCCESS) { 314 return ::testing::AssertionFailure() 315 << "Got successful sync result, then unexpected successful async " 316 "result"; 317 } 318 } else if (result != allowedSyncResult && 319 result != Result::TRANSACTION_FAILED) { 320 return ::testing::AssertionFailure() << "Got sync result " 321 << asBaseType(result) << ", expected TRANSACTION_FAILED or " 322 << asBaseType(allowedSyncResult); 323 } 324 325 return ::testing::AssertionSuccess(); 326 } 327 328 TEST_P(ContexthubTxnTest, TestSendMessageToNonExistentNanoApp) { 329 ContextHubMsg msg; 330 msg.appName = kNonExistentAppId; 331 msg.msgType = 1; 332 msg.msg.resize(4); 333 std::fill(msg.msg.begin(), msg.msg.end(), 0); 334 335 ALOGD("Sending message to non-existent nanoapp"); 336 Result result = hubApi->sendMessageToHub(getHubId(), msg); 337 if (result != Result::OK && 338 result != Result::BAD_PARAMS && 339 result != Result::TRANSACTION_FAILED) { 340 FAIL() << "Got result " << asBaseType(result) << ", expected OK, BAD_PARAMS" 341 << ", or TRANSACTION_FAILED"; 342 } 343 } 344 345 TEST_P(ContexthubTxnTest, TestLoadEmptyNanoApp) { 346 cb->expectedTxnId = 0123; 347 NanoAppBinary emptyApp; 348 349 emptyApp.appId = kNonExistentAppId; 350 emptyApp.appVersion = 1; 351 emptyApp.flags = 0; 352 emptyApp.targetChreApiMajorVersion = 1; 353 emptyApp.targetChreApiMinorVersion = 0; 354 355 ALOGD("Loading empty nanoapp"); 356 Result result = hubApi->loadNanoApp(getHubId(), emptyApp, cb->expectedTxnId); 357 EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS, 358 cb->promise.get_future())); 359 } 360 361 TEST_P(ContexthubTxnTest, TestUnloadNonexistentNanoApp) { 362 cb->expectedTxnId = 1234; 363 364 ALOGD("Unloading nonexistent nanoapp"); 365 Result result = hubApi->unloadNanoApp(getHubId(), kNonExistentAppId, 366 cb->expectedTxnId); 367 EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS, 368 cb->promise.get_future())); 369 } 370 371 TEST_P(ContexthubTxnTest, TestEnableNonexistentNanoApp) { 372 cb->expectedTxnId = 2345; 373 374 ALOGD("Enabling nonexistent nanoapp"); 375 Result result = hubApi->enableNanoApp(getHubId(), kNonExistentAppId, 376 cb->expectedTxnId); 377 EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS, 378 cb->promise.get_future())); 379 } 380 381 TEST_P(ContexthubTxnTest, TestDisableNonexistentNanoApp) { 382 cb->expectedTxnId = 3456; 383 384 ALOGD("Disabling nonexistent nanoapp"); 385 Result result = hubApi->disableNanoApp(getHubId(), kNonExistentAppId, 386 cb->expectedTxnId); 387 EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS, 388 cb->promise.get_future())); 389 } 390 391 // Parameterize all SingleContexthubTest tests against each valid hub ID 392 INSTANTIATE_TEST_CASE_P(HubIdSpecificTests, ContexthubHidlTest, 393 ::testing::ValuesIn(getHubIds())); 394 INSTANTIATE_TEST_CASE_P(HubIdSpecificTests, ContexthubTxnTest, 395 ::testing::ValuesIn(getHubIds())); 396 397 } // anonymous namespace 398 399 int main(int argc, char **argv) { 400 ::testing::AddGlobalTestEnvironment(ContexthubHidlEnvironment::Instance()); 401 ::testing::InitGoogleTest(&argc, argv); 402 ContexthubHidlEnvironment::Instance()->init(&argc, argv); 403 int status = RUN_ALL_TESTS(); 404 ALOGI ("Test result = %d", status); 405 return status; 406 } 407 408