blob: c431d85eef220ebf695fc80f5f0e9af4194af405 [file] [log] [blame]
Yu Shan726d51a2022-02-22 17:37:21 -08001/*
2 * Copyright (C) 2022 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 "VtsHalAutomotiveVehicle"
18
19#include <IVhalClient.h>
20#include <VehicleHalTypes.h>
21#include <VehicleUtils.h>
22#include <aidl/Gtest.h>
23#include <aidl/Vintf.h>
24#include <aidl/android/hardware/automotive/vehicle/IVehicle.h>
25#include <android-base/stringprintf.h>
26#include <android-base/thread_annotations.h>
27#include <android/binder_process.h>
28#include <gtest/gtest.h>
29#include <hidl/GtestPrinter.h>
30#include <hidl/ServiceManagement.h>
31#include <inttypes.h>
32#include <utils/Log.h>
Yu Shan4569ef52022-03-18 14:34:25 -070033#include <utils/SystemClock.h>
Yu Shan726d51a2022-02-22 17:37:21 -080034
35#include <chrono>
36#include <mutex>
37#include <unordered_map>
38#include <unordered_set>
39#include <vector>
40
41using ::aidl::android::hardware::automotive::vehicle::IVehicle;
42using ::aidl::android::hardware::automotive::vehicle::StatusCode;
43using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
44using ::aidl::android::hardware::automotive::vehicle::VehicleArea;
45using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
46using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess;
47using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
48using ::android::getAidlHalInstanceNames;
49using ::android::base::ScopedLockAssertion;
50using ::android::base::StringPrintf;
51using ::android::frameworks::automotive::vhal::HalPropError;
52using ::android::frameworks::automotive::vhal::IHalPropConfig;
53using ::android::frameworks::automotive::vhal::IHalPropValue;
54using ::android::frameworks::automotive::vhal::ISubscriptionCallback;
55using ::android::frameworks::automotive::vhal::IVhalClient;
56using ::android::hardware::getAllHalInstanceNames;
57using ::android::hardware::Sanitize;
58using ::android::hardware::automotive::vehicle::toInt;
59
60constexpr int32_t kInvalidProp = 0x31600207;
61
62struct ServiceDescriptor {
63 std::string name;
64 bool isAidlService;
65};
66
67class VtsVehicleCallback final : public ISubscriptionCallback {
68 private:
69 std::mutex mLock;
70 std::unordered_map<int32_t, size_t> mEventsCount GUARDED_BY(mLock);
Yu Shan4569ef52022-03-18 14:34:25 -070071 std::unordered_map<int32_t, std::vector<int64_t>> mEventTimestamps GUARDED_BY(mLock);
Yu Shan726d51a2022-02-22 17:37:21 -080072 std::condition_variable mEventCond;
73
74 public:
75 void onPropertyEvent(const std::vector<std::unique_ptr<IHalPropValue>>& values) override {
76 {
77 std::lock_guard<std::mutex> lockGuard(mLock);
78 for (auto& value : values) {
Yu Shan4569ef52022-03-18 14:34:25 -070079 int32_t propId = value->getPropId();
80 mEventsCount[propId] += 1;
81 mEventTimestamps[propId].push_back(value->getTimestamp());
Yu Shan726d51a2022-02-22 17:37:21 -080082 }
83 }
84 mEventCond.notify_one();
85 }
86
87 void onPropertySetError([[maybe_unused]] const std::vector<HalPropError>& errors) override {
88 // Do nothing.
89 }
90
91 template <class Rep, class Period>
92 bool waitForExpectedEvents(int32_t propId, size_t expectedEvents,
93 const std::chrono::duration<Rep, Period>& timeout) {
94 std::unique_lock<std::mutex> uniqueLock(mLock);
95 return mEventCond.wait_for(uniqueLock, timeout, [this, propId, expectedEvents] {
96 ScopedLockAssertion lockAssertion(mLock);
97 return mEventsCount[propId] >= expectedEvents;
98 });
99 }
100
Yu Shan4569ef52022-03-18 14:34:25 -0700101 std::vector<int64_t> getEventTimestamps(int32_t propId) {
102 {
103 std::lock_guard<std::mutex> lockGuard(mLock);
104 return mEventTimestamps[propId];
105 }
106 }
107
Yu Shan726d51a2022-02-22 17:37:21 -0800108 void reset() {
109 std::lock_guard<std::mutex> lockGuard(mLock);
110 mEventsCount.clear();
111 }
112};
113
114class VtsHalAutomotiveVehicleTargetTest : public testing::TestWithParam<ServiceDescriptor> {
115 public:
116 virtual void SetUp() override {
117 auto descriptor = GetParam();
118 if (descriptor.isAidlService) {
119 mVhalClient = IVhalClient::tryCreateAidlClient(descriptor.name.c_str());
120 } else {
121 mVhalClient = IVhalClient::tryCreateHidlClient(descriptor.name.c_str());
122 }
123
124 ASSERT_NE(mVhalClient, nullptr) << "Failed to connect to VHAL";
125
126 mCallback = std::make_shared<VtsVehicleCallback>();
127 }
128
129 static bool isBooleanGlobalProp(int32_t property) {
130 return (property & toInt(VehiclePropertyType::MASK)) ==
131 toInt(VehiclePropertyType::BOOLEAN) &&
132 (property & toInt(VehicleArea::MASK)) == toInt(VehicleArea::GLOBAL);
133 }
134
135 protected:
136 std::shared_ptr<IVhalClient> mVhalClient;
137 std::shared_ptr<VtsVehicleCallback> mCallback;
138};
139
140TEST_P(VtsHalAutomotiveVehicleTargetTest, useAidlBackend) {
141 if (!mVhalClient->isAidlVhal()) {
142 GTEST_SKIP() << "AIDL backend is not available, HIDL backend is used instead";
143 }
144}
145
146TEST_P(VtsHalAutomotiveVehicleTargetTest, useHidlBackend) {
147 if (mVhalClient->isAidlVhal()) {
148 GTEST_SKIP() << "AIDL backend is available, HIDL backend is not used";
149 }
150}
151
152// Test getAllPropConfig() returns at least 4 property configs.
153TEST_P(VtsHalAutomotiveVehicleTargetTest, getAllPropConfigs) {
154 ALOGD("VtsHalAutomotiveVehicleTargetTest::getAllPropConfigs");
155
156 auto result = mVhalClient->getAllPropConfigs();
157
158 ASSERT_TRUE(result.ok()) << "Failed to get all property configs, error: "
159 << result.error().message();
160 ASSERT_GE(result.value().size(), 4u) << StringPrintf(
161 "Expect to get at least 4 property configs, got %zu", result.value().size());
162}
163
164// Test getPropConfigs() can query all properties listed in CDD.
165TEST_P(VtsHalAutomotiveVehicleTargetTest, getRequiredPropConfigs) {
166 ALOGD("VtsHalAutomotiveVehicleTargetTest::getRequiredPropConfigs");
167
168 // Check the properties listed in CDD
169 std::vector<int32_t> properties = {
170 toInt(VehicleProperty::GEAR_SELECTION), toInt(VehicleProperty::NIGHT_MODE),
171 toInt(VehicleProperty::PARKING_BRAKE_ON), toInt(VehicleProperty::PERF_VEHICLE_SPEED)};
172
173 auto result = mVhalClient->getPropConfigs(properties);
174
175 ASSERT_TRUE(result.ok()) << "Failed to get required property config, error: "
176 << result.error().message();
177 ASSERT_EQ(result.value().size(), 4u)
178 << StringPrintf("Expect to get exactly 4 configs, got %zu", result.value().size());
179}
180
181// Test getPropConfig() with an invalid propertyId returns an error code.
182TEST_P(VtsHalAutomotiveVehicleTargetTest, getPropConfigsWithInvalidProp) {
183 ALOGD("VtsHalAutomotiveVehicleTargetTest::getPropConfigsWithInvalidProp");
184
185 auto result = mVhalClient->getPropConfigs({kInvalidProp});
186
187 ASSERT_FALSE(result.ok()) << StringPrintf(
188 "Expect failure to get prop configs for invalid prop: %" PRId32, kInvalidProp);
189 ASSERT_NE(result.error().message(), "") << "Expect error message not to be empty";
190}
191
192// Test get() return current value for properties.
193TEST_P(VtsHalAutomotiveVehicleTargetTest, get) {
194 ALOGD("VtsHalAutomotiveVehicleTargetTest::get");
195
196 int32_t propId = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
197 auto result = mVhalClient->getValueSync(*mVhalClient->createHalPropValue(propId));
198
199 ASSERT_TRUE(result.ok()) << StringPrintf("Failed to get value for property: %" PRId32
200 ", error: %s",
201 propId, result.error().message().c_str());
202 ASSERT_NE(result.value(), nullptr) << "Result value must not be null";
203}
204
205// Test get() with an invalid propertyId return an error codes.
206TEST_P(VtsHalAutomotiveVehicleTargetTest, getInvalidProp) {
207 ALOGD("VtsHalAutomotiveVehicleTargetTest::getInvalidProp");
208
209 auto result = mVhalClient->getValueSync(*mVhalClient->createHalPropValue(kInvalidProp));
210
211 ASSERT_FALSE(result.ok()) << StringPrintf(
212 "Expect failure to get property for invalid prop: %" PRId32, kInvalidProp);
213}
214
215// Test set() on read_write properties.
216TEST_P(VtsHalAutomotiveVehicleTargetTest, setProp) {
217 ALOGD("VtsHalAutomotiveVehicleTargetTest::setProp");
218
219 // skip hvac related properties
220 std::unordered_set<int32_t> hvacProps = {toInt(VehicleProperty::HVAC_DEFROSTER),
221 toInt(VehicleProperty::HVAC_AC_ON),
222 toInt(VehicleProperty::HVAC_MAX_AC_ON),
223 toInt(VehicleProperty::HVAC_MAX_DEFROST_ON),
224 toInt(VehicleProperty::HVAC_RECIRC_ON),
225 toInt(VehicleProperty::HVAC_DUAL_ON),
226 toInt(VehicleProperty::HVAC_AUTO_ON),
227 toInt(VehicleProperty::HVAC_POWER_ON),
228 toInt(VehicleProperty::HVAC_AUTO_RECIRC_ON),
229 toInt(VehicleProperty::HVAC_ELECTRIC_DEFROSTER_ON)};
230 auto result = mVhalClient->getAllPropConfigs();
231 ASSERT_TRUE(result.ok());
232
233 for (const auto& cfgPtr : result.value()) {
234 const IHalPropConfig& cfg = *cfgPtr;
235 int32_t propId = cfg.getPropId();
236 // test on boolean and writable property
237 if (cfg.getAccess() == toInt(VehiclePropertyAccess::READ_WRITE) &&
238 isBooleanGlobalProp(propId) && !hvacProps.count(propId)) {
239 auto propToGet = mVhalClient->createHalPropValue(propId);
240 auto getValueResult = mVhalClient->getValueSync(*propToGet);
241
242 ASSERT_TRUE(getValueResult.ok())
243 << StringPrintf("Failed to get value for property: %" PRId32 ", error: %s",
244 propId, getValueResult.error().message().c_str());
245 ASSERT_NE(getValueResult.value(), nullptr)
246 << StringPrintf("Result value must not be null for property: %" PRId32, propId);
247
248 const IHalPropValue& value = *getValueResult.value();
249 size_t intValueSize = value.getInt32Values().size();
250 ASSERT_EQ(intValueSize, 1u) << StringPrintf(
251 "Expect exactly 1 int value for boolean property: %" PRId32 ", got %zu", propId,
252 intValueSize);
253
254 int setValue = value.getInt32Values()[0] == 1 ? 0 : 1;
255 auto propToSet = mVhalClient->createHalPropValue(propId);
256 propToSet->setInt32Values({setValue});
257 auto setValueResult = mVhalClient->setValueSync(*propToSet);
258
259 ASSERT_TRUE(setValueResult.ok())
260 << StringPrintf("Failed to set value for property: %" PRId32 ", error: %s",
261 propId, setValueResult.error().message().c_str());
262
263 // check set success
264 getValueResult = mVhalClient->getValueSync(*propToGet);
265 ASSERT_TRUE(getValueResult.ok())
266 << StringPrintf("Failed to get value for property: %" PRId32 ", error: %s",
267 propId, getValueResult.error().message().c_str());
268 ASSERT_NE(getValueResult.value(), nullptr)
269 << StringPrintf("Result value must not be null for property: %" PRId32, propId);
270 ASSERT_EQ(getValueResult.value()->getInt32Values(), std::vector<int32_t>({setValue}))
271 << StringPrintf("Boolean value not updated after set for property: %" PRId32,
272 propId);
273 }
274 }
275}
276
277// Test set() on an read_only property.
278TEST_P(VtsHalAutomotiveVehicleTargetTest, setNotWritableProp) {
279 ALOGD("VtsHalAutomotiveVehicleTargetTest::setNotWritableProp");
280
281 int32_t propId = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
282 auto getValueResult = mVhalClient->getValueSync(*mVhalClient->createHalPropValue(propId));
283 ASSERT_TRUE(getValueResult.ok())
284 << StringPrintf("Failed to get value for property: %" PRId32 ", error: %s", propId,
285 getValueResult.error().message().c_str());
286
287 auto setValueResult = mVhalClient->setValueSync(*getValueResult.value());
288
289 ASSERT_FALSE(setValueResult.ok()) << "Expect set a read-only value to fail";
Yu Shan24fee7b2022-03-03 15:10:51 -0800290 ASSERT_EQ(setValueResult.error().code(), StatusCode::ACCESS_DENIED);
Yu Shan726d51a2022-02-22 17:37:21 -0800291}
292
293// Test subscribe() and unsubscribe().
294TEST_P(VtsHalAutomotiveVehicleTargetTest, subscribeAndUnsubscribe) {
295 ALOGD("VtsHalAutomotiveVehicleTargetTest::subscribeAndUnsubscribe");
296
297 int32_t propId = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
298
Yu Shan4569ef52022-03-18 14:34:25 -0700299 auto propConfigsResult = mVhalClient->getPropConfigs({propId});
300
301 ASSERT_TRUE(propConfigsResult.ok()) << "Failed to get property config for PERF_VEHICLE_SPEED: "
302 << "error: " << propConfigsResult.error().message();
303 ASSERT_EQ(propConfigsResult.value().size(), 1u)
304 << "Expect to return 1 config for PERF_VEHICLE_SPEED";
305 auto& propConfig = propConfigsResult.value()[0];
306 float minSampleRate = propConfig->getMinSampleRate();
307 float maxSampleRate = propConfig->getMaxSampleRate();
308
309 if (minSampleRate < 1) {
310 GTEST_SKIP() << "Sample rate for vehicle speed < 1 times/sec, skip test since it would "
311 "take too long";
312 }
Yu Shan726d51a2022-02-22 17:37:21 -0800313
314 auto client = mVhalClient->getSubscriptionClient(mCallback);
315 ASSERT_NE(client, nullptr) << "Failed to get subscription client";
316
Yu Shan4569ef52022-03-18 14:34:25 -0700317 auto result = client->subscribe({{.propId = propId, .sampleRate = minSampleRate}});
Yu Shan726d51a2022-02-22 17:37:21 -0800318
319 ASSERT_TRUE(result.ok()) << StringPrintf("Failed to subscribe to property: %" PRId32
320 ", error: %s",
321 propId, result.error().message().c_str());
Yu Shan4569ef52022-03-18 14:34:25 -0700322
323 if (mVhalClient->isAidlVhal()) {
324 // Skip checking timestamp for HIDL because the behavior for sample rate and timestamp is
325 // only specified clearly for AIDL.
326
327 // Timeout is 2 seconds, which gives a 1 second buffer.
328 ASSERT_TRUE(mCallback->waitForExpectedEvents(propId, std::floor(minSampleRate),
329 std::chrono::seconds(2)))
330 << "Didn't get enough events for subscribing to minSampleRate";
331 }
332
333 result = client->subscribe({{.propId = propId, .sampleRate = maxSampleRate}});
334
335 ASSERT_TRUE(result.ok()) << StringPrintf("Failed to subscribe to property: %" PRId32
336 ", error: %s",
337 propId, result.error().message().c_str());
338
339 if (mVhalClient->isAidlVhal()) {
340 ASSERT_TRUE(mCallback->waitForExpectedEvents(propId, std::floor(maxSampleRate),
341 std::chrono::seconds(2)))
342 << "Didn't get enough events for subscribing to maxSampleRate";
343
344 std::unordered_set<int64_t> timestamps;
345 // Event event should have a different timestamp.
346 for (const int64_t& eventTimestamp : mCallback->getEventTimestamps(propId)) {
347 ASSERT_TRUE(timestamps.find(eventTimestamp) == timestamps.end())
348 << "two events for the same property must not have the same timestamp";
349 timestamps.insert(eventTimestamp);
350 }
351 }
Yu Shan726d51a2022-02-22 17:37:21 -0800352
353 result = client->unsubscribe({propId});
354 ASSERT_TRUE(result.ok()) << StringPrintf("Failed to unsubscribe to property: %" PRId32
355 ", error: %s",
356 propId, result.error().message().c_str());
357
358 mCallback->reset();
359 ASSERT_FALSE(mCallback->waitForExpectedEvents(propId, 10, std::chrono::seconds(1)))
360 << "Expect not to get events after unsubscription";
361}
362
363// Test subscribe() with an invalid property.
364TEST_P(VtsHalAutomotiveVehicleTargetTest, subscribeInvalidProp) {
365 ALOGD("VtsHalAutomotiveVehicleTargetTest::subscribeInvalidProp");
366
367 std::vector<SubscribeOptions> options = {
368 SubscribeOptions{.propId = kInvalidProp, .sampleRate = 10.0}};
369
370 auto client = mVhalClient->getSubscriptionClient(mCallback);
371 ASSERT_NE(client, nullptr) << "Failed to get subscription client";
372
373 auto result = client->subscribe(options);
374
375 ASSERT_FALSE(result.ok()) << StringPrintf("Expect subscribing to property: %" PRId32 " to fail",
376 kInvalidProp);
377}
378
Yu Shan4569ef52022-03-18 14:34:25 -0700379// Test the timestamp returned in GetValues results is the timestamp when the value is retrieved.
380TEST_P(VtsHalAutomotiveVehicleTargetTest, testGetValuesTimestampAIDL) {
381 if (!mVhalClient->isAidlVhal()) {
382 GTEST_SKIP() << "Skip checking timestamp for HIDL because the behavior is only specified "
383 "for AIDL";
384 }
385
386 int32_t propId = toInt(VehicleProperty::PARKING_BRAKE_ON);
387 auto prop = mVhalClient->createHalPropValue(propId);
388
389 auto result = mVhalClient->getValueSync(*prop);
390
391 ASSERT_TRUE(result.ok()) << StringPrintf("Failed to get value for property: %" PRId32
392 ", error: %s",
393 propId, result.error().message().c_str());
394 ASSERT_NE(result.value(), nullptr) << "Result value must not be null";
395 ASSERT_EQ(result.value()->getInt32Values().size(), 1u) << "Result must contain 1 int value";
396
397 bool parkBrakeOnValue1 = (result.value()->getInt32Values()[0] == 1);
398 int64_t timestampValue1 = result.value()->getTimestamp();
399
400 result = mVhalClient->getValueSync(*prop);
401
402 ASSERT_TRUE(result.ok()) << StringPrintf("Failed to get value for property: %" PRId32
403 ", error: %s",
404 propId, result.error().message().c_str());
405 ASSERT_NE(result.value(), nullptr) << "Result value must not be null";
406 ASSERT_EQ(result.value()->getInt32Values().size(), 1u) << "Result must contain 1 int value";
407
408 bool parkBarkeOnValue2 = (result.value()->getInt32Values()[0] == 1);
409 int64_t timestampValue2 = result.value()->getTimestamp();
410
411 if (parkBarkeOnValue2 == parkBrakeOnValue1) {
412 ASSERT_EQ(timestampValue2, timestampValue1)
413 << "getValue result must contain a timestamp updated when the value was updated, if"
414 "the value does not change, expect the same timestamp";
415 } else {
416 ASSERT_GT(timestampValue2, timestampValue1)
417 << "getValue result must contain a timestamp updated when the value was updated, if"
418 "the value changes, expect the newer value has a larger timestamp";
419 }
420}
421
Yu Shan726d51a2022-02-22 17:37:21 -0800422std::vector<ServiceDescriptor> getDescriptors() {
423 std::vector<ServiceDescriptor> descriptors;
424 for (std::string name : getAidlHalInstanceNames(IVehicle::descriptor)) {
425 descriptors.push_back({
426 .name = name,
427 .isAidlService = true,
428 });
429 }
430 for (std::string name : getAllHalInstanceNames(IVehicle::descriptor)) {
431 descriptors.push_back({
432 .name = name,
433 .isAidlService = false,
434 });
435 }
436 return descriptors;
437}
438
439GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VtsHalAutomotiveVehicleTargetTest);
440
441INSTANTIATE_TEST_SUITE_P(PerInstance, VtsHalAutomotiveVehicleTargetTest,
442 testing::ValuesIn(getDescriptors()),
443 [](const testing::TestParamInfo<ServiceDescriptor>& info) {
444 std::string name = "";
445 if (info.param.isAidlService) {
446 name += "aidl_";
447 } else {
448 name += "hidl_";
449 }
450 name += info.param.name;
451 return Sanitize(name);
452 });
453
454int main(int argc, char** argv) {
455 ::testing::InitGoogleTest(&argc, argv);
456 ABinderProcess_setThreadPoolMaxThreadCount(1);
457 return RUN_ALL_TESTS();
458}