blob: 32382f242ce0273a79557d8ef7e57fe16209a861 [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>
33
34#include <chrono>
35#include <mutex>
36#include <unordered_map>
37#include <unordered_set>
38#include <vector>
39
40using ::aidl::android::hardware::automotive::vehicle::IVehicle;
41using ::aidl::android::hardware::automotive::vehicle::StatusCode;
42using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
43using ::aidl::android::hardware::automotive::vehicle::VehicleArea;
44using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
45using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess;
46using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
47using ::android::getAidlHalInstanceNames;
48using ::android::base::ScopedLockAssertion;
49using ::android::base::StringPrintf;
50using ::android::frameworks::automotive::vhal::HalPropError;
51using ::android::frameworks::automotive::vhal::IHalPropConfig;
52using ::android::frameworks::automotive::vhal::IHalPropValue;
53using ::android::frameworks::automotive::vhal::ISubscriptionCallback;
54using ::android::frameworks::automotive::vhal::IVhalClient;
55using ::android::hardware::getAllHalInstanceNames;
56using ::android::hardware::Sanitize;
57using ::android::hardware::automotive::vehicle::toInt;
58
59constexpr int32_t kInvalidProp = 0x31600207;
60
61struct ServiceDescriptor {
62 std::string name;
63 bool isAidlService;
64};
65
66class VtsVehicleCallback final : public ISubscriptionCallback {
67 private:
68 std::mutex mLock;
69 std::unordered_map<int32_t, size_t> mEventsCount GUARDED_BY(mLock);
70 std::condition_variable mEventCond;
71
72 public:
73 void onPropertyEvent(const std::vector<std::unique_ptr<IHalPropValue>>& values) override {
74 {
75 std::lock_guard<std::mutex> lockGuard(mLock);
76 for (auto& value : values) {
77 mEventsCount[value->getPropId()] += 1;
78 }
79 }
80 mEventCond.notify_one();
81 }
82
83 void onPropertySetError([[maybe_unused]] const std::vector<HalPropError>& errors) override {
84 // Do nothing.
85 }
86
87 template <class Rep, class Period>
88 bool waitForExpectedEvents(int32_t propId, size_t expectedEvents,
89 const std::chrono::duration<Rep, Period>& timeout) {
90 std::unique_lock<std::mutex> uniqueLock(mLock);
91 return mEventCond.wait_for(uniqueLock, timeout, [this, propId, expectedEvents] {
92 ScopedLockAssertion lockAssertion(mLock);
93 return mEventsCount[propId] >= expectedEvents;
94 });
95 }
96
97 void reset() {
98 std::lock_guard<std::mutex> lockGuard(mLock);
99 mEventsCount.clear();
100 }
101};
102
103class VtsHalAutomotiveVehicleTargetTest : public testing::TestWithParam<ServiceDescriptor> {
104 public:
105 virtual void SetUp() override {
106 auto descriptor = GetParam();
107 if (descriptor.isAidlService) {
108 mVhalClient = IVhalClient::tryCreateAidlClient(descriptor.name.c_str());
109 } else {
110 mVhalClient = IVhalClient::tryCreateHidlClient(descriptor.name.c_str());
111 }
112
113 ASSERT_NE(mVhalClient, nullptr) << "Failed to connect to VHAL";
114
115 mCallback = std::make_shared<VtsVehicleCallback>();
116 }
117
118 static bool isBooleanGlobalProp(int32_t property) {
119 return (property & toInt(VehiclePropertyType::MASK)) ==
120 toInt(VehiclePropertyType::BOOLEAN) &&
121 (property & toInt(VehicleArea::MASK)) == toInt(VehicleArea::GLOBAL);
122 }
123
124 protected:
125 std::shared_ptr<IVhalClient> mVhalClient;
126 std::shared_ptr<VtsVehicleCallback> mCallback;
127};
128
129TEST_P(VtsHalAutomotiveVehicleTargetTest, useAidlBackend) {
130 if (!mVhalClient->isAidlVhal()) {
131 GTEST_SKIP() << "AIDL backend is not available, HIDL backend is used instead";
132 }
133}
134
135TEST_P(VtsHalAutomotiveVehicleTargetTest, useHidlBackend) {
136 if (mVhalClient->isAidlVhal()) {
137 GTEST_SKIP() << "AIDL backend is available, HIDL backend is not used";
138 }
139}
140
141// Test getAllPropConfig() returns at least 4 property configs.
142TEST_P(VtsHalAutomotiveVehicleTargetTest, getAllPropConfigs) {
143 ALOGD("VtsHalAutomotiveVehicleTargetTest::getAllPropConfigs");
144
145 auto result = mVhalClient->getAllPropConfigs();
146
147 ASSERT_TRUE(result.ok()) << "Failed to get all property configs, error: "
148 << result.error().message();
149 ASSERT_GE(result.value().size(), 4u) << StringPrintf(
150 "Expect to get at least 4 property configs, got %zu", result.value().size());
151}
152
153// Test getPropConfigs() can query all properties listed in CDD.
154TEST_P(VtsHalAutomotiveVehicleTargetTest, getRequiredPropConfigs) {
155 ALOGD("VtsHalAutomotiveVehicleTargetTest::getRequiredPropConfigs");
156
157 // Check the properties listed in CDD
158 std::vector<int32_t> properties = {
159 toInt(VehicleProperty::GEAR_SELECTION), toInt(VehicleProperty::NIGHT_MODE),
160 toInt(VehicleProperty::PARKING_BRAKE_ON), toInt(VehicleProperty::PERF_VEHICLE_SPEED)};
161
162 auto result = mVhalClient->getPropConfigs(properties);
163
164 ASSERT_TRUE(result.ok()) << "Failed to get required property config, error: "
165 << result.error().message();
166 ASSERT_EQ(result.value().size(), 4u)
167 << StringPrintf("Expect to get exactly 4 configs, got %zu", result.value().size());
168}
169
170// Test getPropConfig() with an invalid propertyId returns an error code.
171TEST_P(VtsHalAutomotiveVehicleTargetTest, getPropConfigsWithInvalidProp) {
172 ALOGD("VtsHalAutomotiveVehicleTargetTest::getPropConfigsWithInvalidProp");
173
174 auto result = mVhalClient->getPropConfigs({kInvalidProp});
175
176 ASSERT_FALSE(result.ok()) << StringPrintf(
177 "Expect failure to get prop configs for invalid prop: %" PRId32, kInvalidProp);
178 ASSERT_NE(result.error().message(), "") << "Expect error message not to be empty";
179}
180
181// Test get() return current value for properties.
182TEST_P(VtsHalAutomotiveVehicleTargetTest, get) {
183 ALOGD("VtsHalAutomotiveVehicleTargetTest::get");
184
185 int32_t propId = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
186 auto result = mVhalClient->getValueSync(*mVhalClient->createHalPropValue(propId));
187
188 ASSERT_TRUE(result.ok()) << StringPrintf("Failed to get value for property: %" PRId32
189 ", error: %s",
190 propId, result.error().message().c_str());
191 ASSERT_NE(result.value(), nullptr) << "Result value must not be null";
192}
193
194// Test get() with an invalid propertyId return an error codes.
195TEST_P(VtsHalAutomotiveVehicleTargetTest, getInvalidProp) {
196 ALOGD("VtsHalAutomotiveVehicleTargetTest::getInvalidProp");
197
198 auto result = mVhalClient->getValueSync(*mVhalClient->createHalPropValue(kInvalidProp));
199
200 ASSERT_FALSE(result.ok()) << StringPrintf(
201 "Expect failure to get property for invalid prop: %" PRId32, kInvalidProp);
202}
203
204// Test set() on read_write properties.
205TEST_P(VtsHalAutomotiveVehicleTargetTest, setProp) {
206 ALOGD("VtsHalAutomotiveVehicleTargetTest::setProp");
207
208 // skip hvac related properties
209 std::unordered_set<int32_t> hvacProps = {toInt(VehicleProperty::HVAC_DEFROSTER),
210 toInt(VehicleProperty::HVAC_AC_ON),
211 toInt(VehicleProperty::HVAC_MAX_AC_ON),
212 toInt(VehicleProperty::HVAC_MAX_DEFROST_ON),
213 toInt(VehicleProperty::HVAC_RECIRC_ON),
214 toInt(VehicleProperty::HVAC_DUAL_ON),
215 toInt(VehicleProperty::HVAC_AUTO_ON),
216 toInt(VehicleProperty::HVAC_POWER_ON),
217 toInt(VehicleProperty::HVAC_AUTO_RECIRC_ON),
218 toInt(VehicleProperty::HVAC_ELECTRIC_DEFROSTER_ON)};
219 auto result = mVhalClient->getAllPropConfigs();
220 ASSERT_TRUE(result.ok());
221
222 for (const auto& cfgPtr : result.value()) {
223 const IHalPropConfig& cfg = *cfgPtr;
224 int32_t propId = cfg.getPropId();
225 // test on boolean and writable property
226 if (cfg.getAccess() == toInt(VehiclePropertyAccess::READ_WRITE) &&
227 isBooleanGlobalProp(propId) && !hvacProps.count(propId)) {
228 auto propToGet = mVhalClient->createHalPropValue(propId);
229 auto getValueResult = mVhalClient->getValueSync(*propToGet);
230
231 ASSERT_TRUE(getValueResult.ok())
232 << StringPrintf("Failed to get value for property: %" PRId32 ", error: %s",
233 propId, getValueResult.error().message().c_str());
234 ASSERT_NE(getValueResult.value(), nullptr)
235 << StringPrintf("Result value must not be null for property: %" PRId32, propId);
236
237 const IHalPropValue& value = *getValueResult.value();
238 size_t intValueSize = value.getInt32Values().size();
239 ASSERT_EQ(intValueSize, 1u) << StringPrintf(
240 "Expect exactly 1 int value for boolean property: %" PRId32 ", got %zu", propId,
241 intValueSize);
242
243 int setValue = value.getInt32Values()[0] == 1 ? 0 : 1;
244 auto propToSet = mVhalClient->createHalPropValue(propId);
245 propToSet->setInt32Values({setValue});
246 auto setValueResult = mVhalClient->setValueSync(*propToSet);
247
248 ASSERT_TRUE(setValueResult.ok())
249 << StringPrintf("Failed to set value for property: %" PRId32 ", error: %s",
250 propId, setValueResult.error().message().c_str());
251
252 // check set success
253 getValueResult = mVhalClient->getValueSync(*propToGet);
254 ASSERT_TRUE(getValueResult.ok())
255 << StringPrintf("Failed to get value for property: %" PRId32 ", error: %s",
256 propId, getValueResult.error().message().c_str());
257 ASSERT_NE(getValueResult.value(), nullptr)
258 << StringPrintf("Result value must not be null for property: %" PRId32, propId);
259 ASSERT_EQ(getValueResult.value()->getInt32Values(), std::vector<int32_t>({setValue}))
260 << StringPrintf("Boolean value not updated after set for property: %" PRId32,
261 propId);
262 }
263 }
264}
265
266// Test set() on an read_only property.
267TEST_P(VtsHalAutomotiveVehicleTargetTest, setNotWritableProp) {
268 ALOGD("VtsHalAutomotiveVehicleTargetTest::setNotWritableProp");
269
270 int32_t propId = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
271 auto getValueResult = mVhalClient->getValueSync(*mVhalClient->createHalPropValue(propId));
272 ASSERT_TRUE(getValueResult.ok())
273 << StringPrintf("Failed to get value for property: %" PRId32 ", error: %s", propId,
274 getValueResult.error().message().c_str());
275
276 auto setValueResult = mVhalClient->setValueSync(*getValueResult.value());
277
278 ASSERT_FALSE(setValueResult.ok()) << "Expect set a read-only value to fail";
279 ASSERT_EQ(setValueResult.error().code(), toInt(StatusCode::ACCESS_DENIED));
280}
281
282// Test subscribe() and unsubscribe().
283TEST_P(VtsHalAutomotiveVehicleTargetTest, subscribeAndUnsubscribe) {
284 ALOGD("VtsHalAutomotiveVehicleTargetTest::subscribeAndUnsubscribe");
285
286 int32_t propId = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
287
288 std::vector<SubscribeOptions> options = {
289 SubscribeOptions{.propId = propId, .sampleRate = 10.0}};
290
291 auto client = mVhalClient->getSubscriptionClient(mCallback);
292 ASSERT_NE(client, nullptr) << "Failed to get subscription client";
293
294 auto result = client->subscribe(options);
295
296 ASSERT_TRUE(result.ok()) << StringPrintf("Failed to subscribe to property: %" PRId32
297 ", error: %s",
298 propId, result.error().message().c_str());
299 ASSERT_TRUE(mCallback->waitForExpectedEvents(propId, 10, std::chrono::seconds(10)))
300 << "Didn't get enough events for subscription";
301
302 result = client->unsubscribe({propId});
303 ASSERT_TRUE(result.ok()) << StringPrintf("Failed to unsubscribe to property: %" PRId32
304 ", error: %s",
305 propId, result.error().message().c_str());
306
307 mCallback->reset();
308 ASSERT_FALSE(mCallback->waitForExpectedEvents(propId, 10, std::chrono::seconds(1)))
309 << "Expect not to get events after unsubscription";
310}
311
312// Test subscribe() with an invalid property.
313TEST_P(VtsHalAutomotiveVehicleTargetTest, subscribeInvalidProp) {
314 ALOGD("VtsHalAutomotiveVehicleTargetTest::subscribeInvalidProp");
315
316 std::vector<SubscribeOptions> options = {
317 SubscribeOptions{.propId = kInvalidProp, .sampleRate = 10.0}};
318
319 auto client = mVhalClient->getSubscriptionClient(mCallback);
320 ASSERT_NE(client, nullptr) << "Failed to get subscription client";
321
322 auto result = client->subscribe(options);
323
324 ASSERT_FALSE(result.ok()) << StringPrintf("Expect subscribing to property: %" PRId32 " to fail",
325 kInvalidProp);
326}
327
328std::vector<ServiceDescriptor> getDescriptors() {
329 std::vector<ServiceDescriptor> descriptors;
330 for (std::string name : getAidlHalInstanceNames(IVehicle::descriptor)) {
331 descriptors.push_back({
332 .name = name,
333 .isAidlService = true,
334 });
335 }
336 for (std::string name : getAllHalInstanceNames(IVehicle::descriptor)) {
337 descriptors.push_back({
338 .name = name,
339 .isAidlService = false,
340 });
341 }
342 return descriptors;
343}
344
345GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VtsHalAutomotiveVehicleTargetTest);
346
347INSTANTIATE_TEST_SUITE_P(PerInstance, VtsHalAutomotiveVehicleTargetTest,
348 testing::ValuesIn(getDescriptors()),
349 [](const testing::TestParamInfo<ServiceDescriptor>& info) {
350 std::string name = "";
351 if (info.param.isAidlService) {
352 name += "aidl_";
353 } else {
354 name += "hidl_";
355 }
356 name += info.param.name;
357 return Sanitize(name);
358 });
359
360int main(int argc, char** argv) {
361 ::testing::InitGoogleTest(&argc, argv);
362 ABinderProcess_setThreadPoolMaxThreadCount(1);
363 return RUN_ALL_TESTS();
364}