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