1 /*
2 * Copyright (C) 2023 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 "VtsHalAutomotiveRemoteAccess"
18
19 #include <aidl/Gtest.h>
20 #include <aidl/Vintf.h>
21 #include <aidl/android/hardware/automotive/remoteaccess/ApState.h>
22 #include <aidl/android/hardware/automotive/remoteaccess/BnRemoteTaskCallback.h>
23 #include <aidl/android/hardware/automotive/remoteaccess/IRemoteAccess.h>
24 #include <aidl/android/hardware/automotive/remoteaccess/ScheduleInfo.h>
25 #include <aidl/android/hardware/automotive/remoteaccess/TaskType.h>
26 #include <android-base/thread_annotations.h>
27 #include <android/binder_manager.h>
28 #include <android/binder_process.h>
29 #include <gmock/gmock.h>
30 #include <gtest/gtest.h>
31
32 #include <algorithm>
33 #include <chrono>
34 #include <string>
35 #include <thread>
36 #include <vector>
37
38 using ::aidl::android::hardware::automotive::remoteaccess::ApState;
39 using ::aidl::android::hardware::automotive::remoteaccess::BnRemoteTaskCallback;
40 using ::aidl::android::hardware::automotive::remoteaccess::IRemoteAccess;
41 using ::aidl::android::hardware::automotive::remoteaccess::ScheduleInfo;
42 using ::aidl::android::hardware::automotive::remoteaccess::TaskType;
43 using ::android::getAidlHalInstanceNames;
44 using ::android::PrintInstanceNameToString;
45 using ::android::base::ScopedLockAssertion;
46 using ::ndk::ScopedAStatus;
47 using ::ndk::SharedRefBase;
48 using ::ndk::SpAIBinder;
49 using ::testing::UnorderedElementsAre;
50
51 namespace {
52
53 const std::string TEST_CLIENT_ID = "TEST CLIENT ID";
54 const std::string TEST_SCHEDULE_ID = "TEST SCHEDULE ID";
55 const std::string TEST_SCHEDULE_ID_1 = "TEST SCHEDULE ID 1";
56 const std::string TEST_SCHEDULE_ID_2 = "TEST SCHEDULE ID 2";
57 const std::vector<uint8_t> TEST_TASK_DATA =
58 std::vector<uint8_t>({static_cast<uint8_t>(0xde), static_cast<uint8_t>(0xad),
59 static_cast<uint8_t>(0xbe), static_cast<uint8_t>(0xef)});
60 const int32_t JOB_DELAY_IN_SECONDS = 5;
61
62 } // namespace
63
64 class VtsHalAutomotiveRemoteAccessTargetTest : public testing::TestWithParam<std::string> {
65 public:
SetUp()66 virtual void SetUp() override {
67 const std::string& name = GetParam();
68 mHal = IRemoteAccess::fromBinder(SpAIBinder(AServiceManager_waitForService(name.c_str())));
69 ASSERT_NE(mHal, nullptr) << "Failed to connect to remote access HAL: " << name;
70 }
71
TearDown()72 virtual void TearDown() override {
73 if (mHal != nullptr) {
74 mHal->clearRemoteTaskCallback();
75 mHal->unscheduleAllTasks(TEST_CLIENT_ID);
76 }
77 }
78
79 protected:
80 std::shared_ptr<IRemoteAccess> mHal;
81
82 bool isTaskScheduleSupported();
83 int32_t getInterfaceVersion();
84 ScheduleInfo getTestScheduleInfo(int32_t delayInSeconds, int32_t count,
85 int32_t periodicInSeconds);
86 void setTaskCallbackAndReadyForTask(std::shared_ptr<BnRemoteTaskCallback> testCallback);
87 };
88
89 class TestRemoteTaskCallback final : public BnRemoteTaskCallback {
90 public:
onRemoteTaskRequested(const std::string & clientId,const std::vector<uint8_t> & data)91 ScopedAStatus onRemoteTaskRequested(const std::string& clientId,
92 const std::vector<uint8_t>& data) override {
93 {
94 std::unique_lock<std::mutex> lockGuard(mLock);
95 mClientIds.push_back(clientId);
96 mDataList.push_back(data);
97 }
98 mCv.notify_one();
99 return ScopedAStatus::ok();
100 }
101
getCalledClientIds()102 const std::vector<std::string> getCalledClientIds() {
103 std::lock_guard<std::mutex> lockGuard(mLock);
104 return mClientIds;
105 }
106
getCalledDataList()107 const std::vector<std::vector<uint8_t>> getCalledDataList() {
108 std::lock_guard<std::mutex> lockGuard(mLock);
109 return mDataList;
110 }
111
waitForCallbacks(size_t count,int32_t timeoutInSeconds)112 bool waitForCallbacks(size_t count, int32_t timeoutInSeconds) {
113 std::unique_lock lk(mLock);
114 return mCv.wait_for(lk, std::chrono::seconds(timeoutInSeconds), [this, count] {
115 ScopedLockAssertion lockAssertion(mLock);
116 return mClientIds.size() >= count;
117 });
118 }
119
120 private:
121 std::mutex mLock;
122 std::vector<std::string> mClientIds GUARDED_BY(mLock);
123 std::vector<std::vector<uint8_t>> mDataList GUARDED_BY(mLock);
124 std::condition_variable mCv;
125 };
126
TEST_P(VtsHalAutomotiveRemoteAccessTargetTest,testGetVehicleId)127 TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testGetVehicleId) {
128 std::string vehicleId;
129
130 ScopedAStatus status = mHal->getVehicleId(&vehicleId);
131
132 ASSERT_TRUE(status.isOk()) << "Failed to call getVehicleId";
133 ASSERT_FALSE(vehicleId.empty()) << "Vehicle ID must not be empty";
134 }
135
TEST_P(VtsHalAutomotiveRemoteAccessTargetTest,testGetWakeupServiceName)136 TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testGetWakeupServiceName) {
137 std::string wakeupServiceName;
138
139 ScopedAStatus status = mHal->getWakeupServiceName(&wakeupServiceName);
140
141 ASSERT_TRUE(status.isOk()) << "Failed to call getWakeupServiceName";
142 ASSERT_FALSE(wakeupServiceName.empty()) << "Wakeup service name must not be empty";
143 }
144
TEST_P(VtsHalAutomotiveRemoteAccessTargetTest,testGetProcessorId)145 TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testGetProcessorId) {
146 std::string processorId;
147
148 ScopedAStatus status = mHal->getProcessorId(&processorId);
149
150 ASSERT_TRUE(status.isOk()) << "Failed to call getProcessorId";
151 }
152
TEST_P(VtsHalAutomotiveRemoteAccessTargetTest,testSetClearRemoteTaskCallback)153 TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testSetClearRemoteTaskCallback) {
154 std::shared_ptr<TestRemoteTaskCallback> testCallback =
155 SharedRefBase::make<TestRemoteTaskCallback>();
156
157 ScopedAStatus status = mHal->setRemoteTaskCallback(testCallback);
158
159 ASSERT_TRUE(status.isOk()) << "Failed to call setRemoteTaskCallback";
160
161 status = mHal->clearRemoteTaskCallback();
162
163 ASSERT_TRUE(status.isOk()) << "Failed to call clearRemoteTaskCallback";
164 }
165
TEST_P(VtsHalAutomotiveRemoteAccessTargetTest,testNotifyApStateChange)166 TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testNotifyApStateChange) {
167 ApState apState = ApState{
168 .isReadyForRemoteTask = false,
169 .isWakeupRequired = false,
170 };
171
172 ScopedAStatus status = mHal->notifyApStateChange(apState);
173
174 ASSERT_TRUE(status.isOk()) << "Failed to call notifyApStateChange with state: "
175 << apState.toString();
176
177 apState = ApState{
178 .isReadyForRemoteTask = true,
179 .isWakeupRequired = false,
180 };
181
182 ASSERT_TRUE(status.isOk()) << "Failed to call notifyApStateChange with state: "
183 << apState.toString();
184 }
185
getInterfaceVersion()186 int32_t VtsHalAutomotiveRemoteAccessTargetTest::getInterfaceVersion() {
187 int32_t interfaceVersion = 0;
188 mHal->getInterfaceVersion(&interfaceVersion);
189 return interfaceVersion;
190 }
191
TEST_P(VtsHalAutomotiveRemoteAccessTargetTest,testIsTaskScheduleSupported)192 TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testIsTaskScheduleSupported) {
193 if (getInterfaceVersion() < 2) {
194 GTEST_SKIP() << "Require RemoteAccess HAL v2";
195 }
196
197 bool supported;
198
199 ScopedAStatus status = mHal->isTaskScheduleSupported(&supported);
200
201 ASSERT_TRUE(status.isOk()) << "Failed to call isTaskScheduleSupported";
202 }
203
isTaskScheduleSupported()204 bool VtsHalAutomotiveRemoteAccessTargetTest::isTaskScheduleSupported() {
205 bool supported = false;
206 mHal->isTaskScheduleSupported(&supported);
207 return supported;
208 }
209
TEST_P(VtsHalAutomotiveRemoteAccessTargetTest,testGetSupportedTaskTypesForScheduling)210 TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testGetSupportedTaskTypesForScheduling) {
211 if (getInterfaceVersion() < 2) {
212 GTEST_SKIP() << "Require RemoteAccess HAL v2";
213 }
214
215 std::vector<TaskType> supportedTaskTypes;
216
217 ScopedAStatus status = mHal->getSupportedTaskTypesForScheduling(&supportedTaskTypes);
218
219 ASSERT_TRUE(status.isOk()) << "Failed to call getSupportedTaskTypesForScheduling";
220
221 if (!isTaskScheduleSupported()) {
222 ASSERT_TRUE(supportedTaskTypes.empty())
223 << "getSupportedTaskTypesForScheduling must return empty array "
224 << "if isTaskScheduleSupported is false";
225 return;
226 }
227
228 ASSERT_TRUE(std::find(supportedTaskTypes.begin(), supportedTaskTypes.end(), TaskType::CUSTOM) !=
229 supportedTaskTypes.end())
230 << "getSupportedTaskTypesForScheduling must contain TaskType::CUSTOM";
231 }
232
getTestScheduleInfo(int32_t delayInSeconds,int32_t count,int32_t periodicInSeconds)233 ScheduleInfo VtsHalAutomotiveRemoteAccessTargetTest::getTestScheduleInfo(
234 int32_t delayInSeconds, int32_t count, int32_t periodicInSeconds) {
235 auto nowInEpochSeconds = std::chrono::duration_cast<std::chrono::seconds>(
236 std::chrono::system_clock::now().time_since_epoch())
237 .count();
238
239 return ScheduleInfo{
240 .clientId = TEST_CLIENT_ID,
241 .scheduleId = TEST_SCHEDULE_ID,
242 .taskType = TaskType::CUSTOM,
243 .taskData = TEST_TASK_DATA,
244 .count = count,
245 .startTimeInEpochSeconds = nowInEpochSeconds + delayInSeconds,
246 .periodicInSeconds = periodicInSeconds,
247 };
248 }
249
setTaskCallbackAndReadyForTask(std::shared_ptr<BnRemoteTaskCallback> testCallback)250 void VtsHalAutomotiveRemoteAccessTargetTest::setTaskCallbackAndReadyForTask(
251 std::shared_ptr<BnRemoteTaskCallback> testCallback) {
252 mHal->setRemoteTaskCallback(testCallback);
253 // Notify isReadForRemoteTask to be true.
254 mHal->notifyApStateChange(ApState{
255 .isReadyForRemoteTask = true,
256 .isWakeupRequired = false,
257 });
258 }
259
TEST_P(VtsHalAutomotiveRemoteAccessTargetTest,testScheduleTask)260 TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testScheduleTask) {
261 if (getInterfaceVersion() < 2) {
262 GTEST_SKIP() << "Require RemoteAccess HAL v2";
263 }
264
265 std::shared_ptr<TestRemoteTaskCallback> testCallback =
266 SharedRefBase::make<TestRemoteTaskCallback>();
267 setTaskCallbackAndReadyForTask(testCallback);
268
269 int32_t count = 2;
270 ScheduleInfo scheduleInfo = getTestScheduleInfo(
271 /*delayInSeconds=*/JOB_DELAY_IN_SECONDS, count, /*periodicInSeconds=*/1);
272 ScopedAStatus status = mHal->scheduleTask(scheduleInfo);
273
274 if (!isTaskScheduleSupported()) {
275 ASSERT_FALSE(status.isOk()) << "scheduleTask must return EX_ILLEGAL_ARGUMENT "
276 << "if isTaskScheduleSupported is false";
277 ASSERT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT)
278 << "scheduleTask must return EX_ILLEGAL_ARGUMENT "
279 << "if isTaskScheduleSupported is false";
280 return;
281 }
282
283 ASSERT_TRUE(status.isOk()) << "Failed to call scheduleTask with scheduleInfo: "
284 << scheduleInfo.toString();
285
286 int32_t timeoutInSeconds = JOB_DELAY_IN_SECONDS + 5;
287 bool gotCallbacks = testCallback->waitForCallbacks(count, timeoutInSeconds);
288 // unschedule the task before checking the result.
289 mHal->unscheduleTask(TEST_CLIENT_ID, TEST_SCHEDULE_ID);
290
291 ASSERT_TRUE(gotCallbacks) << "Callbacks is not called enough times before timeout: "
292 << timeoutInSeconds << "s";
293 std::vector<std::vector<uint8_t>> dataList = testCallback->getCalledDataList();
294 std::vector<std::string> clientIds = testCallback->getCalledClientIds();
295
296 for (size_t i = 0; i < dataList.size(); i++) {
297 EXPECT_EQ(dataList[i], TEST_TASK_DATA) << "Must receive expected task data";
298 EXPECT_EQ(clientIds[i], TEST_CLIENT_ID) << "Must receive expected client id";
299 }
300 }
301
TEST_P(VtsHalAutomotiveRemoteAccessTargetTest,testUnscheduleTask)302 TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testUnscheduleTask) {
303 if (getInterfaceVersion() < 2) {
304 GTEST_SKIP() << "Require RemoteAccess HAL v2";
305 }
306
307 std::shared_ptr<TestRemoteTaskCallback> testCallback =
308 SharedRefBase::make<TestRemoteTaskCallback>();
309 setTaskCallbackAndReadyForTask(testCallback);
310
311 ScheduleInfo scheduleInfo = getTestScheduleInfo(
312 /*delayInSeconds=*/JOB_DELAY_IN_SECONDS, /*count=*/1, /*periodicInSeconds=*/0);
313 mHal->scheduleTask(scheduleInfo);
314 ScopedAStatus status = mHal->unscheduleTask(TEST_CLIENT_ID, TEST_SCHEDULE_ID);
315
316 ASSERT_TRUE(status.isOk()) << "Failed to call unscheduleTask";
317
318 // If not cancelled, should be called in 5s, wait for 6s and make sure no task arrives.
319 std::this_thread::sleep_for(std::chrono::seconds(JOB_DELAY_IN_SECONDS + 1));
320
321 ASSERT_TRUE(testCallback->getCalledClientIds().empty())
322 << "Remote task callback must not be called if the task is cancelled";
323 }
324
TEST_P(VtsHalAutomotiveRemoteAccessTargetTest,testUnscheduleAllTasks)325 TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testUnscheduleAllTasks) {
326 if (getInterfaceVersion() < 2) {
327 GTEST_SKIP() << "Require RemoteAccess HAL v2";
328 }
329
330 std::shared_ptr<TestRemoteTaskCallback> testCallback =
331 SharedRefBase::make<TestRemoteTaskCallback>();
332 setTaskCallbackAndReadyForTask(testCallback);
333
334 ScheduleInfo scheduleInfo = getTestScheduleInfo(
335 /*delayInSeconds=*/JOB_DELAY_IN_SECONDS, /*count=*/1, /*periodicInSeconds=*/0);
336 mHal->scheduleTask(scheduleInfo);
337 ScopedAStatus status = mHal->unscheduleAllTasks(TEST_CLIENT_ID);
338
339 ASSERT_TRUE(status.isOk()) << "Failed to call unscheduleAllTasks";
340
341 // If not cancelled, should be called in 5s, wait for 6s and make sure no task arrives.
342 std::this_thread::sleep_for(std::chrono::seconds(JOB_DELAY_IN_SECONDS + 1));
343
344 ASSERT_TRUE(testCallback->getCalledClientIds().empty())
345 << "Remote task callback must not be called if the task is cancelled";
346 }
347
TEST_P(VtsHalAutomotiveRemoteAccessTargetTest,testIsTaskScheduled)348 TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testIsTaskScheduled) {
349 if (getInterfaceVersion() < 2) {
350 GTEST_SKIP() << "Require RemoteAccess HAL v2";
351 }
352
353 std::shared_ptr<TestRemoteTaskCallback> testCallback =
354 SharedRefBase::make<TestRemoteTaskCallback>();
355 setTaskCallbackAndReadyForTask(testCallback);
356
357 ScheduleInfo scheduleInfo = getTestScheduleInfo(
358 /*delayInSeconds=*/JOB_DELAY_IN_SECONDS, /*count=*/1, /*periodicInSeconds=*/0);
359 mHal->scheduleTask(scheduleInfo);
360
361 bool scheduled;
362 ScopedAStatus status = mHal->isTaskScheduled(TEST_CLIENT_ID, TEST_SCHEDULE_ID, &scheduled);
363
364 ASSERT_TRUE(status.isOk()) << "Failed to call unscheduleTask";
365
366 if (!isTaskScheduleSupported()) {
367 ASSERT_FALSE(scheduled) << "isTaskScheduled must return false "
368 << "if isTaskScheduleSupported is false";
369 return;
370 }
371
372 ASSERT_TRUE(scheduled) << "isTaskScheduled must return true if the task is scheduled";
373
374 mHal->unscheduleAllTasks(TEST_CLIENT_ID);
375 status = mHal->isTaskScheduled(TEST_CLIENT_ID, TEST_SCHEDULE_ID, &scheduled);
376
377 ASSERT_TRUE(status.isOk()) << "Failed to call unscheduleTask";
378 ASSERT_FALSE(scheduled) << "isTaskScheduled must return false if the task is not scheduled";
379 }
380
TEST_P(VtsHalAutomotiveRemoteAccessTargetTest,testGetAllPendingScheduledTasks)381 TEST_P(VtsHalAutomotiveRemoteAccessTargetTest, testGetAllPendingScheduledTasks) {
382 if (getInterfaceVersion() < 2) {
383 GTEST_SKIP() << "Require RemoteAccess HAL v2";
384 }
385
386 std::shared_ptr<TestRemoteTaskCallback> testCallback =
387 SharedRefBase::make<TestRemoteTaskCallback>();
388 setTaskCallbackAndReadyForTask(testCallback);
389
390 auto nowInEpochSeconds = std::chrono::duration_cast<std::chrono::seconds>(
391 std::chrono::system_clock::now().time_since_epoch())
392 .count();
393
394 ScheduleInfo scheduleInfo1 = ScheduleInfo{
395 .clientId = TEST_CLIENT_ID,
396 .scheduleId = TEST_SCHEDULE_ID_1,
397 .taskType = TaskType::CUSTOM,
398 .taskData = TEST_TASK_DATA,
399 .count = 1,
400 .startTimeInEpochSeconds = nowInEpochSeconds + 5,
401 .periodicInSeconds = 0,
402 };
403 ScheduleInfo scheduleInfo2 = ScheduleInfo{
404 .clientId = TEST_CLIENT_ID,
405 .scheduleId = TEST_SCHEDULE_ID_2,
406 .taskType = TaskType::CUSTOM,
407 .taskData = TEST_TASK_DATA,
408 .count = 10,
409 .startTimeInEpochSeconds = nowInEpochSeconds + 10,
410 .periodicInSeconds = 1,
411 };
412 mHal->scheduleTask(scheduleInfo1);
413 mHal->scheduleTask(scheduleInfo2);
414
415 std::vector<ScheduleInfo> outScheduleInfo;
416 ScopedAStatus status = mHal->getAllPendingScheduledTasks(TEST_CLIENT_ID, &outScheduleInfo);
417
418 ASSERT_TRUE(status.isOk()) << "Failed to call getAllPendingScheduledTasks";
419
420 if (!isTaskScheduleSupported()) {
421 ASSERT_TRUE(outScheduleInfo.empty())
422 << "Must return empty array for getAllPendingScheduledTasks "
423 << "if isTaskScheduleSupported is false";
424 return;
425 }
426
427 ASSERT_THAT(outScheduleInfo, UnorderedElementsAre(scheduleInfo1, scheduleInfo2))
428 << "expected all pending schedule info mismatch";
429
430 mHal->unscheduleTask(TEST_CLIENT_ID, TEST_SCHEDULE_ID_1);
431
432 status = mHal->getAllPendingScheduledTasks(TEST_CLIENT_ID, &outScheduleInfo);
433
434 ASSERT_TRUE(status.isOk()) << "Failed to call getAllPendingScheduledTasks";
435
436 ASSERT_THAT(outScheduleInfo, UnorderedElementsAre(scheduleInfo2))
437 << "expected all pending schedule info mismatch";
438 }
439
440 // It is possible that no remote access HAL is registered.
441 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VtsHalAutomotiveRemoteAccessTargetTest);
442
443 INSTANTIATE_TEST_SUITE_P(PerInstance, VtsHalAutomotiveRemoteAccessTargetTest,
444 testing::ValuesIn(getAidlHalInstanceNames(IRemoteAccess::descriptor)),
445 PrintInstanceNameToString);
446
main(int argc,char ** argv)447 int main(int argc, char** argv) {
448 ::testing::InitGoogleTest(&argc, argv);
449 // Starts a process pool for callbacks.
450 ABinderProcess_setThreadPoolMaxThreadCount(1);
451 ABinderProcess_startThreadPool();
452 return RUN_ALL_TESTS();
453 }
454