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>
asBaseType(EnumType value)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
getHubsSync(sp<IContexthub> hubApi)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
getHubIds()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 (const 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
Instance()100 static ContexthubHidlEnvironment* Instance() {
101 static ContexthubHidlEnvironment* instance = new ContexthubHidlEnvironment;
102 return instance;
103 }
104
registerTestServices()105 virtual void registerTestServices() override { registerTestService<IContexthub>(); }
106 private:
ContexthubHidlEnvironment()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:
SetUp()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
TearDown()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:
getHubId()133 uint32_t getHubId() {
134 return GetParam();
135 }
136
registerCallback(sp<IContexthubCallback> cb)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:
handleClientMsg(const ContextHubMsg &)147 virtual Return<void> handleClientMsg(const ContextHubMsg& /*msg*/) override {
148 ALOGD("Got client message callback");
149 return Void();
150 }
151
handleTxnResult(uint32_t txnId,TransactionResult result)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
handleHubEvent(AsyncEventType evt)159 virtual Return<void> handleHubEvent(AsyncEventType evt) override {
160 ALOGD("Got hub event callback for event type %" PRIu32, evt);
161 return Void();
162 }
163
handleAppAbort(uint64_t appId,uint32_t abortCode)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
handleAppsInfo(const hidl_vec<HubAppInfo> &)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>
waitForCallback(std::future<ReturnType> future,ReturnType * result,std::chrono::milliseconds timeout=std::chrono::seconds (5))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
TEST_F(ContexthubHidlTestBase,TestGetHubs)205 TEST_F(ContexthubHidlTestBase, TestGetHubs) {
206 hidl_vec<ContextHub> hubs = getHubsSync(hubApi);
207 ALOGD("System reports %zu hubs", hubs.size());
208
209 for (const 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
TEST_P(ContexthubHidlTest,TestRegisterCallback)225 TEST_P(ContexthubHidlTest, TestRegisterCallback) {
226 ALOGD("TestRegisterCallback called, hubId %" PRIu32, getHubId());
227 ASSERT_OK(registerCallback(new ContexthubCallbackBase()));
228 }
229
TEST_P(ContexthubHidlTest,TestRegisterNullCallback)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:
handleAppsInfo(const hidl_vec<HubAppInfo> & appInfo)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
TEST_P(ContexthubHidlTest,TestQueryApps)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:
handleTxnResult(uint32_t txnId,TransactionResult result)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:
SetUp()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.
checkFailureSyncOrAsync(Result result,Result allowedSyncResult,std::future<TransactionResult> && future)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
TEST_P(ContexthubTxnTest,TestSendMessageToNonExistentNanoApp)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
TEST_P(ContexthubTxnTest,TestLoadEmptyNanoApp)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
TEST_P(ContexthubTxnTest,TestUnloadNonexistentNanoApp)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
TEST_P(ContexthubTxnTest,TestEnableNonexistentNanoApp)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
TEST_P(ContexthubTxnTest,TestDisableNonexistentNanoApp)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
main(int argc,char ** argv)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