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