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