• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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