blob: f249cf612b5c0beb577e3aceb80a476adf5b54c6 [file] [log] [blame] [edit]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "VtsHalAutomotiveVehicle"
#include <AccessForVehicleProperty.h>
#include <AnnotationsForVehicleProperty.h>
#include <ChangeModeForVehicleProperty.h>
#include <EnumForVehicleProperty.h>
#include <IVhalClient.h>
#include <VehicleHalTypes.h>
#include <VehicleUtils.h>
#include <VersionForVehicleProperty.h>
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
#include <aidl/android/hardware/automotive/vehicle/HasSupportedValueInfo.h>
#include <aidl/android/hardware/automotive/vehicle/IVehicle.h>
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
#include <android/binder_process.h>
#include <android/hardware/automotive/vehicle/2.0/IVehicle.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
#include <inttypes.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
#include <chrono>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <unordered_set>
#include <vector>
using ::aidl::android::hardware::automotive::vehicle::AllowedAccessForVehicleProperty;
using ::aidl::android::hardware::automotive::vehicle::AnnotationsForVehicleProperty;
using ::aidl::android::hardware::automotive::vehicle::ChangeModeForVehicleProperty;
using ::aidl::android::hardware::automotive::vehicle::getSupportedEnumValuesForProperty;
using ::aidl::android::hardware::automotive::vehicle::HasSupportedValueInfo;
using ::aidl::android::hardware::automotive::vehicle::IVehicle;
using ::aidl::android::hardware::automotive::vehicle::MinMaxSupportedValueResult;
using ::aidl::android::hardware::automotive::vehicle::PropIdAreaId;
using ::aidl::android::hardware::automotive::vehicle::RawPropValues;
using ::aidl::android::hardware::automotive::vehicle::StatusCode;
using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
using ::aidl::android::hardware::automotive::vehicle::SupportedValuesListResult;
using ::aidl::android::hardware::automotive::vehicle::toString;
using ::aidl::android::hardware::automotive::vehicle::VehicleArea;
using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyGroup;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
using ::aidl::android::hardware::automotive::vehicle::VersionForVehicleProperty;
using ::android::getAidlHalInstanceNames;
using ::android::uptimeMillis;
using ::android::base::ScopedLockAssertion;
using ::android::base::StringPrintf;
using ::android::frameworks::automotive::vhal::ErrorCode;
using ::android::frameworks::automotive::vhal::HalPropError;
using ::android::frameworks::automotive::vhal::IHalAreaConfig;
using ::android::frameworks::automotive::vhal::IHalPropConfig;
using ::android::frameworks::automotive::vhal::IHalPropValue;
using ::android::frameworks::automotive::vhal::ISubscriptionCallback;
using ::android::frameworks::automotive::vhal::IVhalClient;
using ::android::frameworks::automotive::vhal::SubscribeOptionsBuilder;
using ::android::frameworks::automotive::vhal::VhalClientResult;
using ::android::hardware::getAllHalInstanceNames;
using ::android::hardware::Sanitize;
using ::android::hardware::automotive::vehicle::isSystemProp;
using ::android::hardware::automotive::vehicle::propIdToString;
using ::android::hardware::automotive::vehicle::toInt;
using ::testing::Ge;
constexpr int32_t kInvalidProp = 0x31600207;
// The timeout for retrying getting prop value after setting prop value.
constexpr int64_t kRetryGetPropAfterSetPropTimeoutMillis = 10'000;
static constexpr char ANNOTATION_REQUIRE_MIN_MAX_VALUE[] = "require_min_max_supported_value";
static constexpr char ANNOTATION_REQUIRE_SUPPORTED_VALUES[] = "require_supported_values_list";
static constexpr char ANNOTATION_SUPPORTED_VALUES_IN_CONFIG[] = "legacy_supported_values_in_config";
static constexpr char ANNOTATIONS_DATA_ENUM[] = "data_enum";
inline VehiclePropertyType getPropertyType(int32_t propId) {
return static_cast<VehiclePropertyType>(propId & toInt(VehiclePropertyType::MASK));
}
struct ServiceDescriptor {
std::string name;
bool isAidlService;
};
struct PropertyConfigTestParam {
VehicleProperty propId;
std::vector<VehiclePropertyAccess> accessModes;
VehiclePropertyChangeMode changeMode;
};
class VtsVehicleCallback final : public ISubscriptionCallback {
private:
std::mutex mLock;
std::unordered_map<int32_t, std::vector<std::unique_ptr<IHalPropValue>>> mEvents
GUARDED_BY(mLock);
std::condition_variable mEventCond;
public:
void onPropertyEvent(const std::vector<std::unique_ptr<IHalPropValue>>& values) override {
{
std::lock_guard<std::mutex> lockGuard(mLock);
for (auto& value : values) {
int32_t propId = value->getPropId();
mEvents[propId].push_back(value->clone());
}
}
mEventCond.notify_one();
}
void onPropertySetError([[maybe_unused]] const std::vector<HalPropError>& errors) override {
// Do nothing.
}
template <class Rep, class Period>
bool waitForExpectedEvents(int32_t propId, size_t expectedEvents,
const std::chrono::duration<Rep, Period>& timeout) {
std::unique_lock<std::mutex> uniqueLock(mLock);
return mEventCond.wait_for(uniqueLock, timeout, [this, propId, expectedEvents] {
ScopedLockAssertion lockAssertion(mLock);
return mEvents[propId].size() >= expectedEvents;
});
}
std::vector<std::unique_ptr<IHalPropValue>> getEvents(int32_t propId) {
std::lock_guard<std::mutex> lockGuard(mLock);
std::vector<std::unique_ptr<IHalPropValue>> events;
if (mEvents.find(propId) == mEvents.end()) {
return events;
}
for (const auto& eventPtr : mEvents[propId]) {
events.push_back(eventPtr->clone());
}
return events;
}
std::vector<int64_t> getEventTimestamps(int32_t propId) {
std::lock_guard<std::mutex> lockGuard(mLock);
std::vector<int64_t> timestamps;
if (mEvents.find(propId) == mEvents.end()) {
return timestamps;
}
for (const auto& valuePtr : mEvents[propId]) {
timestamps.push_back(valuePtr->getTimestamp());
}
return timestamps;
}
void reset() {
std::lock_guard<std::mutex> lockGuard(mLock);
mEvents.clear();
}
};
class VtsHalAutomotiveTest : public testing::Test {
public:
void verifyAccessMode(int actualAccess, std::vector<VehiclePropertyAccess> expectedAccess);
void verifyGlobalAccessIsMaximalAreaAccessSubset(
int propertyLevelAccess,
const std::vector<std::unique_ptr<IHalAreaConfig>>& areaConfigs) const;
void verifyProperty(VehicleProperty propId, std::vector<VehiclePropertyAccess> accessModes,
VehiclePropertyChangeMode changeMode);
void testGetMinMaxSupportedValueForPropIdAreaId(int32_t propId,
const IHalAreaConfig& areaConfig,
bool minMaxValueRequired);
void testGetSupportedValuesListsForPropIdAreaId(int32_t propId,
const IHalAreaConfig& areaConfig,
bool supportedValuesRequired);
static bool isBooleanGlobalProp(int32_t property) {
return getPropertyType(property) == VehiclePropertyType::BOOLEAN &&
(property & toInt(VehicleArea::MASK)) == toInt(VehicleArea::GLOBAL);
}
protected:
std::shared_ptr<IVhalClient> mVhalClient;
std::shared_ptr<VtsVehicleCallback> mCallback;
bool checkIsSupported(int32_t propertyId);
void connectToVhal(const ServiceDescriptor& descriptor);
static bool isUnavailable(const VhalClientResult<std::unique_ptr<IHalPropValue>>& result);
static bool isResultOkayWithValue(
const VhalClientResult<std::unique_ptr<IHalPropValue>>& result, int32_t value);
};
bool VtsHalAutomotiveTest::checkIsSupported(int32_t propertyId) {
auto result = mVhalClient->getPropConfigs({propertyId});
return result.ok();
}
void VtsHalAutomotiveTest::connectToVhal(const ServiceDescriptor& descriptor) {
if (descriptor.isAidlService) {
mVhalClient = IVhalClient::tryCreateAidlClient(descriptor.name.c_str());
} else {
mVhalClient = IVhalClient::tryCreateHidlClient(descriptor.name.c_str());
}
ASSERT_NE(mVhalClient, nullptr) << "Failed to connect to VHAL";
mCallback = std::make_shared<VtsVehicleCallback>();
}
bool VtsHalAutomotiveTest::isResultOkayWithValue(
const VhalClientResult<std::unique_ptr<IHalPropValue>>& result, int32_t value) {
return result.ok() && result.value() != nullptr &&
result.value()->getStatus() == VehiclePropertyStatus::AVAILABLE &&
result.value()->getInt32Values().size() == 1 &&
result.value()->getInt32Values()[0] == value;
}
bool VtsHalAutomotiveTest::isUnavailable(
const VhalClientResult<std::unique_ptr<IHalPropValue>>& result) {
if (!result.ok()) {
return result.error().code() == ErrorCode::NOT_AVAILABLE_FROM_VHAL;
}
if (result.value() != nullptr &&
result.value()->getStatus() == VehiclePropertyStatus::UNAVAILABLE) {
return true;
}
return false;
}
void VtsHalAutomotiveTest::verifyAccessMode(int actualAccess,
std::vector<VehiclePropertyAccess> expectedAccess) {
if (actualAccess == toInt(VehiclePropertyAccess::NONE)) {
return;
}
EXPECT_THAT(expectedAccess,
::testing::Contains(static_cast<VehiclePropertyAccess>(actualAccess)))
<< "Invalid property access mode, not one of the allowed access modes";
}
void VtsHalAutomotiveTest::verifyGlobalAccessIsMaximalAreaAccessSubset(
int propertyLevelAccess,
const std::vector<std::unique_ptr<IHalAreaConfig>>& areaConfigs) const {
bool readOnlyPresent = false;
bool writeOnlyPresent = false;
bool readWritePresent = false;
int maximalAreaAccessSubset = toInt(VehiclePropertyAccess::NONE);
for (size_t i = 0; i < areaConfigs.size(); i++) {
int access = areaConfigs[i]->getAccess();
switch (access) {
case toInt(VehiclePropertyAccess::READ):
readOnlyPresent = true;
break;
case toInt(VehiclePropertyAccess::WRITE):
writeOnlyPresent = true;
break;
case toInt(VehiclePropertyAccess::READ_WRITE):
readWritePresent = true;
break;
default:
ASSERT_EQ(access, toInt(VehiclePropertyAccess::NONE))
<< "Area access can be NONE only if global property access is also NONE";
return;
}
}
if (readOnlyPresent) {
ASSERT_FALSE(writeOnlyPresent)
<< "Found both READ_ONLY and WRITE_ONLY access modes in area configs, which is not "
"supported";
maximalAreaAccessSubset = toInt(VehiclePropertyAccess::READ);
} else if (writeOnlyPresent) {
ASSERT_FALSE(readWritePresent) << "Found both WRITE_ONLY and READ_WRITE access modes in "
"area configs, which is not "
"supported";
maximalAreaAccessSubset = toInt(VehiclePropertyAccess::WRITE);
} else if (readWritePresent) {
maximalAreaAccessSubset = toInt(VehiclePropertyAccess::READ_WRITE);
}
ASSERT_EQ(propertyLevelAccess, maximalAreaAccessSubset) << StringPrintf(
"Expected global access to be equal to maximal area access subset %d, Instead got %d",
maximalAreaAccessSubset, propertyLevelAccess);
}
class VtsHalAutomotiveVehicleTargetTest : public VtsHalAutomotiveTest,
public testing::WithParamInterface<ServiceDescriptor> {
virtual void SetUp() override { ASSERT_NO_FATAL_FAILURE(connectToVhal(GetParam())); }
};
class VtsHalAutomotivePropertyConfigTest
: public VtsHalAutomotiveTest,
public testing::WithParamInterface<std::tuple<PropertyConfigTestParam, ServiceDescriptor>> {
virtual void SetUp() override {
ASSERT_NO_FATAL_FAILURE(connectToVhal(std::get<1>(GetParam())));
}
};
TEST_P(VtsHalAutomotiveVehicleTargetTest, useAidlBackend) {
if (!mVhalClient->isAidlVhal()) {
GTEST_SKIP() << "AIDL backend is not available, HIDL backend is used instead";
}
}
TEST_P(VtsHalAutomotiveVehicleTargetTest, useHidlBackend) {
if (mVhalClient->isAidlVhal()) {
GTEST_SKIP() << "AIDL backend is available, HIDL backend is not used";
}
}
// Test getAllPropConfigs() returns at least 1 property configs.
TEST_P(VtsHalAutomotiveVehicleTargetTest, getAllPropConfigs) {
ALOGD("VtsHalAutomotiveVehicleTargetTest::getAllPropConfigs");
auto result = mVhalClient->getAllPropConfigs();
ASSERT_TRUE(result.ok()) << "Failed to get all property configs, error: "
<< result.error().message();
ASSERT_GE(result.value().size(), 1u) << StringPrintf(
"Expect to get at least 1 property config, got %zu", result.value().size());
}
// Test getPropConfigs() can query properties returned by getAllPropConfigs.
TEST_P(VtsHalAutomotiveVehicleTargetTest, getPropConfigsWithValidProps) {
ALOGD("VtsHalAutomotiveVehicleTargetTest::getRequiredPropConfigs");
std::vector<int32_t> properties;
auto result = mVhalClient->getAllPropConfigs();
ASSERT_TRUE(result.ok()) << "Failed to get all property configs, error: "
<< result.error().message();
for (const auto& cfgPtr : result.value()) {
properties.push_back(cfgPtr->getPropId());
}
result = mVhalClient->getPropConfigs(properties);
ASSERT_TRUE(result.ok()) << "Failed to get required property config, error: "
<< result.error().message();
ASSERT_EQ(result.value().size(), properties.size()) << StringPrintf(
"Expect to get exactly %zu configs, got %zu", properties.size(), result.value().size());
}
// Test getPropConfig() with an invalid propertyId returns an error code.
TEST_P(VtsHalAutomotiveVehicleTargetTest, getPropConfigsWithInvalidProp) {
ALOGD("VtsHalAutomotiveVehicleTargetTest::getPropConfigsWithInvalidProp");
auto result = mVhalClient->getPropConfigs({kInvalidProp});
ASSERT_FALSE(result.ok()) << StringPrintf(
"Expect failure to get prop configs for invalid prop: %" PRId32, kInvalidProp);
ASSERT_NE(result.error().message(), "") << "Expect error message not to be empty";
}
// Test system property IDs returned by getPropConfigs() are defined in the VHAL property interface.
TEST_P(VtsHalAutomotiveVehicleTargetTest, testPropConfigs_onlyDefinedSystemPropertyIdsReturned) {
if (!mVhalClient->isAidlVhal()) {
GTEST_SKIP() << "Skip for HIDL VHAL because HAL interface run-time version is only"
<< "introduced for AIDL";
}
auto result = mVhalClient->getAllPropConfigs();
ASSERT_TRUE(result.ok()) << "Failed to get all property configs, error: "
<< result.error().message();
int32_t vhalVersion = mVhalClient->getRemoteInterfaceVersion();
const auto& configs = result.value();
for (size_t i = 0; i < configs.size(); i++) {
int32_t propId = configs[i]->getPropId();
if (!isSystemProp(propId)) {
continue;
}
std::string propName = propIdToString(propId);
auto it = VersionForVehicleProperty.find(static_cast<VehicleProperty>(propId));
bool found = (it != VersionForVehicleProperty.end());
EXPECT_TRUE(found) << "System Property: " << propName
<< " is not defined in VHAL property interface";
if (!found) {
continue;
}
int32_t requiredVersion = it->second;
EXPECT_THAT(vhalVersion, Ge(requiredVersion))
<< "System Property: " << propName << " requires VHAL version: " << requiredVersion
<< ", but the current VHAL version"
<< " is " << vhalVersion << ", must not be supported";
}
}
TEST_P(VtsHalAutomotiveVehicleTargetTest, testPropConfigs_globalAccessIsMaximalAreaAccessSubset) {
if (!mVhalClient->isAidlVhal()) {
GTEST_SKIP() << "Skip for HIDL VHAL because HAL interface run-time version is only"
<< "introduced for AIDL";
}
auto result = mVhalClient->getAllPropConfigs();
ASSERT_TRUE(result.ok()) << "Failed to get all property configs, error: "
<< result.error().message();
const auto& configs = result.value();
for (size_t i = 0; i < configs.size(); i++) {
verifyGlobalAccessIsMaximalAreaAccessSubset(configs[i]->getAccess(),
configs[i]->getAreaConfigs());
}
}
// Test get() return current value for properties.
TEST_P(VtsHalAutomotiveVehicleTargetTest, get) {
ALOGD("VtsHalAutomotiveVehicleTargetTest::get");
int32_t propId = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
if (!checkIsSupported(propId)) {
GTEST_SKIP() << "Property: " << propId << " is not supported, skip the test";
}
auto result = mVhalClient->getValueSync(*mVhalClient->createHalPropValue(propId));
ASSERT_TRUE(result.ok()) << StringPrintf("Failed to get value for property: %" PRId32
", error: %s",
propId, result.error().message().c_str());
ASSERT_NE(result.value(), nullptr) << "Result value must not be null";
}
// Test get() with an invalid propertyId return an error codes.
TEST_P(VtsHalAutomotiveVehicleTargetTest, getInvalidProp) {
ALOGD("VtsHalAutomotiveVehicleTargetTest::getInvalidProp");
auto result = mVhalClient->getValueSync(*mVhalClient->createHalPropValue(kInvalidProp));
ASSERT_FALSE(result.ok()) << StringPrintf(
"Expect failure to get property for invalid prop: %" PRId32, kInvalidProp);
}
// Test set() on read_write properties.
TEST_P(VtsHalAutomotiveVehicleTargetTest, setProp) {
ALOGD("VtsHalAutomotiveVehicleTargetTest::setProp");
// skip hvac related properties
std::unordered_set<int32_t> hvacProps = {toInt(VehicleProperty::HVAC_DEFROSTER),
toInt(VehicleProperty::HVAC_AC_ON),
toInt(VehicleProperty::HVAC_MAX_AC_ON),
toInt(VehicleProperty::HVAC_MAX_DEFROST_ON),
toInt(VehicleProperty::HVAC_RECIRC_ON),
toInt(VehicleProperty::HVAC_DUAL_ON),
toInt(VehicleProperty::HVAC_AUTO_ON),
toInt(VehicleProperty::HVAC_POWER_ON),
toInt(VehicleProperty::HVAC_AUTO_RECIRC_ON),
toInt(VehicleProperty::HVAC_ELECTRIC_DEFROSTER_ON)};
auto result = mVhalClient->getAllPropConfigs();
ASSERT_TRUE(result.ok());
for (const auto& cfgPtr : result.value()) {
const IHalPropConfig& cfg = *cfgPtr;
int32_t propId = cfg.getPropId();
// test on boolean and writable property
bool isReadWrite = (cfg.getAccess() == toInt(VehiclePropertyAccess::READ_WRITE));
if (cfg.getAreaConfigSize() != 0 &&
cfg.getAreaConfigs()[0]->getAccess() != toInt(VehiclePropertyAccess::NONE)) {
isReadWrite = (cfg.getAreaConfigs()[0]->getAccess() ==
toInt(VehiclePropertyAccess::READ_WRITE));
}
if (isReadWrite && isBooleanGlobalProp(propId) && !hvacProps.count(propId)) {
auto propToGet = mVhalClient->createHalPropValue(propId);
auto getValueResult = mVhalClient->getValueSync(*propToGet);
if (isUnavailable(getValueResult)) {
ALOGW("getProperty for %" PRId32
" returns NOT_AVAILABLE, "
"skip testing setProp",
propId);
return;
}
ASSERT_TRUE(getValueResult.ok())
<< StringPrintf("Failed to get value for property: %" PRId32 ", error: %s",
propId, getValueResult.error().message().c_str());
ASSERT_NE(getValueResult.value(), nullptr)
<< StringPrintf("Result value must not be null for property: %" PRId32, propId);
const IHalPropValue& value = *getValueResult.value();
size_t intValueSize = value.getInt32Values().size();
ASSERT_EQ(intValueSize, 1u) << StringPrintf(
"Expect exactly 1 int value for boolean property: %" PRId32 ", got %zu", propId,
intValueSize);
int32_t setValue = value.getInt32Values()[0] == 1 ? 0 : 1;
auto propToSet = mVhalClient->createHalPropValue(propId);
propToSet->setInt32Values({setValue});
auto setValueResult = mVhalClient->setValueSync(*propToSet);
if (!setValueResult.ok() &&
setValueResult.error().code() == ErrorCode::NOT_AVAILABLE_FROM_VHAL) {
ALOGW("setProperty for %" PRId32
" returns NOT_AVAILABLE, "
"skip verifying getProperty returns the same value",
propId);
return;
}
ASSERT_TRUE(setValueResult.ok())
<< StringPrintf("Failed to set value for property: %" PRId32 ", error: %s",
propId, setValueResult.error().message().c_str());
// Retry getting the value until we pass the timeout. getValue might not return
// the expected value immediately since setValue is async.
auto timeoutMillis = uptimeMillis() + kRetryGetPropAfterSetPropTimeoutMillis;
while (true) {
getValueResult = mVhalClient->getValueSync(*propToGet);
if (isResultOkayWithValue(getValueResult, setValue)) {
break;
}
if (uptimeMillis() >= timeoutMillis) {
// Reach timeout, the following assert should fail.
break;
}
// Sleep for 100ms between each getValueSync retry.
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
if (isUnavailable(getValueResult)) {
ALOGW("getProperty for %" PRId32
" returns NOT_AVAILABLE, "
"skip verifying the return value",
propId);
return;
}
ASSERT_TRUE(getValueResult.ok())
<< StringPrintf("Failed to get value for property: %" PRId32 ", error: %s",
propId, getValueResult.error().message().c_str());
ASSERT_NE(getValueResult.value(), nullptr)
<< StringPrintf("Result value must not be null for property: %" PRId32, propId);
ASSERT_EQ(getValueResult.value()->getInt32Values(), std::vector<int32_t>({setValue}))
<< StringPrintf("Boolean value not updated after set for property: %" PRId32,
propId);
}
}
}
// Test set() on an read_only property.
TEST_P(VtsHalAutomotiveVehicleTargetTest, setNotWritableProp) {
ALOGD("VtsHalAutomotiveVehicleTargetTest::setNotWritableProp");
int32_t propId = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
if (!checkIsSupported(propId)) {
GTEST_SKIP() << "Property: " << propId << " is not supported, skip the test";
}
auto getValueResult = mVhalClient->getValueSync(*mVhalClient->createHalPropValue(propId));
ASSERT_TRUE(getValueResult.ok())
<< StringPrintf("Failed to get value for property: %" PRId32 ", error: %s", propId,
getValueResult.error().message().c_str());
auto setValueResult = mVhalClient->setValueSync(*getValueResult.value());
ASSERT_FALSE(setValueResult.ok()) << "Expect set a read-only value to fail";
ASSERT_EQ(setValueResult.error().code(), ErrorCode::ACCESS_DENIED_FROM_VHAL);
}
// Test get(), set() and getAllPropConfigs() on VehicleProperty::INVALID.
TEST_P(VtsHalAutomotiveVehicleTargetTest, getSetPropertyIdInvalid) {
ALOGD("VtsHalAutomotiveVehicleTargetTest::getSetPropertyIdInvalid");
int32_t propId = toInt(VehicleProperty::INVALID);
auto getValueResult = mVhalClient->getValueSync(*mVhalClient->createHalPropValue(propId));
ASSERT_FALSE(getValueResult.ok()) << "Expect get on VehicleProperty::INVALID to fail";
ASSERT_EQ(getValueResult.error().code(), ErrorCode::INVALID_ARG);
auto propToSet = mVhalClient->createHalPropValue(propId);
propToSet->setInt32Values({0});
auto setValueResult = mVhalClient->setValueSync(*propToSet);
ASSERT_FALSE(setValueResult.ok()) << "Expect set on VehicleProperty::INVALID to fail";
ASSERT_EQ(setValueResult.error().code(), ErrorCode::INVALID_ARG);
auto result = mVhalClient->getAllPropConfigs();
ASSERT_TRUE(result.ok());
for (const auto& cfgPtr : result.value()) {
const IHalPropConfig& cfg = *cfgPtr;
ASSERT_FALSE(cfg.getPropId() == propId) << "Expect VehicleProperty::INVALID to not be "
"included in propConfigs";
}
}
// Test subscribe() and unsubscribe().
TEST_P(VtsHalAutomotiveVehicleTargetTest, subscribeAndUnsubscribe) {
ALOGD("VtsHalAutomotiveVehicleTargetTest::subscribeAndUnsubscribe");
int32_t propId = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
if (!checkIsSupported(propId)) {
GTEST_SKIP() << "Property: " << propId << " is not supported, skip the test";
}
auto propConfigsResult = mVhalClient->getPropConfigs({propId});
ASSERT_TRUE(propConfigsResult.ok()) << "Failed to get property config for PERF_VEHICLE_SPEED: "
<< "error: " << propConfigsResult.error().message();
ASSERT_EQ(propConfigsResult.value().size(), 1u)
<< "Expect to return 1 config for PERF_VEHICLE_SPEED";
auto& propConfig = propConfigsResult.value()[0];
float minSampleRate = propConfig->getMinSampleRate();
float maxSampleRate = propConfig->getMaxSampleRate();
if (minSampleRate < 1) {
GTEST_SKIP() << "Sample rate for vehicle speed < 1 times/sec, skip test since it would "
"take too long";
}
auto client = mVhalClient->getSubscriptionClient(mCallback);
ASSERT_NE(client, nullptr) << "Failed to get subscription client";
auto result = client->subscribe({{.propId = propId, .sampleRate = minSampleRate}});
ASSERT_TRUE(result.ok()) << StringPrintf("Failed to subscribe to property: %" PRId32
", error: %s",
propId, result.error().message().c_str());
if (mVhalClient->isAidlVhal()) {
// Skip checking timestamp for HIDL because the behavior for sample rate and timestamp is
// only specified clearly for AIDL.
// Timeout is 2 seconds, which gives a 1 second buffer.
ASSERT_TRUE(mCallback->waitForExpectedEvents(propId, std::floor(minSampleRate),
std::chrono::seconds(2)))
<< "Didn't get enough events for subscribing to minSampleRate";
}
result = client->subscribe({{.propId = propId, .sampleRate = maxSampleRate}});
ASSERT_TRUE(result.ok()) << StringPrintf("Failed to subscribe to property: %" PRId32
", error: %s",
propId, result.error().message().c_str());
if (mVhalClient->isAidlVhal()) {
ASSERT_TRUE(mCallback->waitForExpectedEvents(propId, std::floor(maxSampleRate),
std::chrono::seconds(2)))
<< "Didn't get enough events for subscribing to maxSampleRate";
std::unordered_set<int64_t> timestamps;
// Event event should have a different timestamp.
for (const int64_t& eventTimestamp : mCallback->getEventTimestamps(propId)) {
ASSERT_TRUE(timestamps.find(eventTimestamp) == timestamps.end())
<< "two events for the same property must not have the same timestamp";
timestamps.insert(eventTimestamp);
}
}
result = client->unsubscribe({propId});
ASSERT_TRUE(result.ok()) << StringPrintf("Failed to unsubscribe to property: %" PRId32
", error: %s",
propId, result.error().message().c_str());
mCallback->reset();
ASSERT_FALSE(mCallback->waitForExpectedEvents(propId, 10, std::chrono::seconds(1)))
<< "Expect not to get events after unsubscription";
}
bool isVariableUpdateRateSupported(const std::unique_ptr<IHalPropConfig>& config, int32_t areaId) {
for (const auto& areaConfigPtr : config->getAreaConfigs()) {
if (areaConfigPtr->getAreaId() == areaId &&
areaConfigPtr->isVariableUpdateRateSupported()) {
return true;
}
}
return false;
}
// Test subscribe with variable update rate enabled if supported.
TEST_P(VtsHalAutomotiveVehicleTargetTest, subscribe_enableVurIfSupported) {
ALOGD("VtsHalAutomotiveVehicleTargetTest::subscribe_enableVurIfSupported");
int32_t propId = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
if (!checkIsSupported(propId)) {
GTEST_SKIP() << "Property: " << propId << " is not supported, skip the test";
}
if (!mVhalClient->isAidlVhal()) {
GTEST_SKIP() << "Variable update rate is only supported by AIDL VHAL";
}
auto propConfigsResult = mVhalClient->getPropConfigs({propId});
ASSERT_TRUE(propConfigsResult.ok()) << "Failed to get property config for PERF_VEHICLE_SPEED: "
<< "error: " << propConfigsResult.error().message();
ASSERT_EQ(propConfigsResult.value().size(), 1u)
<< "Expect to return 1 config for PERF_VEHICLE_SPEED";
auto& propConfig = propConfigsResult.value()[0];
float maxSampleRate = propConfig->getMaxSampleRate();
if (maxSampleRate < 1) {
GTEST_SKIP() << "Sample rate for vehicle speed < 1 times/sec, skip test since it would "
"take too long";
}
// PERF_VEHICLE_SPEED is a global property, so areaId is 0.
if (!isVariableUpdateRateSupported(propConfig, /* areaId= */ 0)) {
GTEST_SKIP() << "Variable update rate is not supported for PERF_VEHICLE_SPEED, "
<< "skip testing";
}
// Subscribe to PERF_VEHICLE_SPEED using the max sample rate.
auto client = mVhalClient->getSubscriptionClient(mCallback);
ASSERT_NE(client, nullptr) << "Failed to get subscription client";
SubscribeOptionsBuilder builder(propId);
// By default variable update rate is true.
builder.setSampleRate(maxSampleRate);
auto option = builder.build();
auto result = client->subscribe({option});
ASSERT_TRUE(result.ok()) << StringPrintf("Failed to subscribe to property: %" PRId32
", error: %s",
propId, result.error().message().c_str());
// Sleep for 1 seconds to wait for more possible events to arrive.
std::this_thread::sleep_for(std::chrono::seconds(1));
client->unsubscribe({propId});
auto events = mCallback->getEvents(propId);
if (events.size() <= 1) {
// We received 0 or 1 event, the value is not changing so nothing to check here.
// If all VHAL clients are subscribing to PERF_VEHICLE_SPEED with VUR on, then we
// will receive 0 event. If there are other VHAL clients subscribing to PERF_VEHICLE_SPEED
// with VUR off, then we will receive 1 event which is the initial value.
return;
}
// Sort the values by the timestamp.
std::map<int64_t, float> valuesByTimestamp;
for (size_t i = 0; i < events.size(); i++) {
valuesByTimestamp[events[i]->getTimestamp()] = events[i]->getFloatValues()[0];
}
size_t i = 0;
float previousValue;
for (const auto& [_, value] : valuesByTimestamp) {
if (i != 0) {
ASSERT_FALSE(value != previousValue) << "received duplicate value: " << value
<< " when variable update rate is true";
}
previousValue = value;
i++;
}
}
// Test subscribe() with an invalid property.
TEST_P(VtsHalAutomotiveVehicleTargetTest, subscribeInvalidProp) {
ALOGD("VtsHalAutomotiveVehicleTargetTest::subscribeInvalidProp");
std::vector<SubscribeOptions> options = {
SubscribeOptions{.propId = kInvalidProp, .sampleRate = 10.0}};
auto client = mVhalClient->getSubscriptionClient(mCallback);
ASSERT_NE(client, nullptr) << "Failed to get subscription client";
auto result = client->subscribe(options);
ASSERT_FALSE(result.ok()) << StringPrintf("Expect subscribing to property: %" PRId32 " to fail",
kInvalidProp);
}
// Test the timestamp returned in GetValues results is the timestamp when the value is retrieved.
TEST_P(VtsHalAutomotiveVehicleTargetTest, testGetValuesTimestampAIDL) {
if (!mVhalClient->isAidlVhal()) {
GTEST_SKIP() << "Skip checking timestamp for HIDL because the behavior is only specified "
"for AIDL";
}
int32_t propId = toInt(VehicleProperty::PARKING_BRAKE_ON);
if (!checkIsSupported(propId)) {
GTEST_SKIP() << "Property: " << propId << " is not supported, skip the test";
}
auto prop = mVhalClient->createHalPropValue(propId);
auto result = mVhalClient->getValueSync(*prop);
ASSERT_TRUE(result.ok()) << StringPrintf("Failed to get value for property: %" PRId32
", error: %s",
propId, result.error().message().c_str());
ASSERT_NE(result.value(), nullptr) << "Result value must not be null";
ASSERT_EQ(result.value()->getInt32Values().size(), 1u) << "Result must contain 1 int value";
bool parkBrakeOnValue1 = (result.value()->getInt32Values()[0] == 1);
int64_t timestampValue1 = result.value()->getTimestamp();
result = mVhalClient->getValueSync(*prop);
ASSERT_TRUE(result.ok()) << StringPrintf("Failed to get value for property: %" PRId32
", error: %s",
propId, result.error().message().c_str());
ASSERT_NE(result.value(), nullptr) << "Result value must not be null";
ASSERT_EQ(result.value()->getInt32Values().size(), 1u) << "Result must contain 1 int value";
bool parkBarkeOnValue2 = (result.value()->getInt32Values()[0] == 1);
int64_t timestampValue2 = result.value()->getTimestamp();
if (parkBarkeOnValue2 == parkBrakeOnValue1) {
ASSERT_EQ(timestampValue2, timestampValue1)
<< "getValue result must contain a timestamp updated when the value was updated, if"
"the value does not change, expect the same timestamp";
} else {
ASSERT_GT(timestampValue2, timestampValue1)
<< "getValue result must contain a timestamp updated when the value was updated, if"
"the value changes, expect the newer value has a larger timestamp";
}
}
void verifyRawPropValues(const RawPropValues& rawPropValues, VehiclePropertyType propertyType) {
switch (propertyType) {
case VehiclePropertyType::INT32:
ASSERT_THAT(rawPropValues.int32Values, ::testing::SizeIs(1))
<< "int32Values field must contain exactly one element for INT32 type";
break;
case VehiclePropertyType::FLOAT:
ASSERT_THAT(rawPropValues.floatValues, ::testing::SizeIs(1))
<< "floatValues field must contain exactly one element for FLOAT type";
break;
case VehiclePropertyType::INT64:
ASSERT_THAT(rawPropValues.int64Values, ::testing::SizeIs(1))
<< "int64Values field must contain exactly one element for INT64 type";
break;
default:
// We do not check for other types.
break;
}
}
void VtsHalAutomotiveTest::testGetMinMaxSupportedValueForPropIdAreaId(
int32_t propId, const IHalAreaConfig& areaConfig, bool minMaxValueRequired) {
int32_t areaId = areaConfig.getAreaId();
VehiclePropertyType propertyType = getPropertyType(propId);
std::optional<HasSupportedValueInfo> maybeHasSupportedValueInfo =
areaConfig.getHasSupportedValueInfo();
if (!maybeHasSupportedValueInfo.has_value()) {
return;
}
if (!maybeHasSupportedValueInfo->hasMinSupportedValue &&
!maybeHasSupportedValueInfo->hasMaxSupportedValue) {
return;
}
VhalClientResult<std::vector<MinMaxSupportedValueResult>> result =
mVhalClient->getMinMaxSupportedValue({PropIdAreaId{
.propId = propId,
.areaId = areaId,
}});
ASSERT_RESULT_OK(result)
<< "getMinMaxSupportedValue must return okay result if hasMaxSupportedValue "
<< " or hasMaxSupportedValue is true";
ASSERT_THAT(*result, ::testing::SizeIs(1))
<< "getMinMaxSupportedValue result list size must be 1 for 1 request";
const MinMaxSupportedValueResult& individualResult = (*result)[0];
if (minMaxValueRequired) {
ASSERT_EQ(individualResult.status, StatusCode::OK)
<< "getMinMaxSupportedValue must return okay status if min/max value is required";
}
if (individualResult.status != StatusCode::OK) {
return;
}
bool hasMinValue = individualResult.minSupportedValue.has_value();
bool hasMaxValue = individualResult.maxSupportedValue.has_value();
if (maybeHasSupportedValueInfo->hasMinSupportedValue) {
ASSERT_TRUE(hasMinValue)
<< "minSupportedValue field must not be null if hasMinSupportedValue is true";
}
if (maybeHasSupportedValueInfo->hasMaxSupportedValue) {
ASSERT_TRUE(hasMaxValue)
<< "minSupportedValue field must not be null if hasMinSupportedValue is true";
}
if (hasMinValue) {
ASSERT_NO_FATAL_FAILURE(
verifyRawPropValues(*individualResult.minSupportedValue, propertyType))
<< "MinMaxSupportedValueResult.minSupportedValue is not a valid RawPropValues for "
<< "the property type, value: " << (individualResult.minSupportedValue)->toString();
}
if (hasMaxValue) {
ASSERT_NO_FATAL_FAILURE(
verifyRawPropValues(*individualResult.maxSupportedValue, propertyType))
<< "MinMaxSupportedValueResult.maxSupportedValue is not a valid RawPropValues for "
<< "the property type, value: " << (individualResult.maxSupportedValue)->toString();
}
if (hasMinValue && hasMaxValue) {
int32_t minInt32Value;
int32_t maxInt32Value;
float minFloatValue;
float maxFloatValue;
int64_t minInt64Value;
int64_t maxInt64Value;
switch (propertyType) {
case VehiclePropertyType::INT32:
minInt32Value = (individualResult.minSupportedValue)->int32Values[0];
maxInt32Value = (individualResult.maxSupportedValue)->int32Values[0];
ASSERT_LE(minInt32Value, maxInt32Value)
<< "minSupportedValue must be less or equal to maxSupportedValue";
break;
case VehiclePropertyType::FLOAT:
minFloatValue = (individualResult.minSupportedValue)->floatValues[0];
maxFloatValue = (individualResult.maxSupportedValue)->floatValues[0];
ASSERT_LE(minFloatValue, maxFloatValue)
<< "minSupportedValue must be less or equal to maxSupportedValue";
break;
case VehiclePropertyType::INT64:
minInt64Value = (individualResult.minSupportedValue)->int64Values[0];
maxInt64Value = (individualResult.maxSupportedValue)->int64Values[0];
ASSERT_LE(minInt64Value, maxInt64Value)
<< "minSupportedValue must be less or equal to maxSupportedValue";
break;
default:
// This must not happen since we already checked this condition in
// verifyPropertyConfigMinMaxValue
FAIL() << "minSupportedValue or maxSupportedValue must only be specified for "
"INT32, INT64 or FLOAT type property";
break;
}
}
}
// Test the getMinMaxSupportedValue API. We use this one test case to cover all properties that
// may support this API.
TEST_P(VtsHalAutomotiveVehicleTargetTest, testGetMinMaxSupportedValue) {
if (!mVhalClient->isAidlVhal() || mVhalClient->getRemoteInterfaceVersion() < 4) {
GTEST_SKIP() << "Skip checking getMinMaxSupportedValue because the behavior is not "
"supported for current VHAL version";
}
auto configsResult = mVhalClient->getAllPropConfigs();
ASSERT_TRUE(configsResult.ok())
<< "Failed to get all property configs, error: " << configsResult.error().message();
for (const auto& cfgPtr : configsResult.value()) {
int32_t propId = cfgPtr->getPropId();
bool minMaxValueRequired = false;
std::unordered_set<std::string> annotations;
auto it = AnnotationsForVehicleProperty.find(static_cast<VehicleProperty>(propId));
if (it != AnnotationsForVehicleProperty.end()) {
annotations = it->second;
}
if (annotations.find(ANNOTATION_REQUIRE_MIN_MAX_VALUE) != annotations.end()) {
minMaxValueRequired = true;
}
const std::vector<std::unique_ptr<IHalAreaConfig>>& areaConfigs = cfgPtr->getAreaConfigs();
for (const auto& areaCfgPtr : areaConfigs) {
EXPECT_NO_FATAL_FAILURE(testGetMinMaxSupportedValueForPropIdAreaId(propId, *areaCfgPtr,
minMaxValueRequired))
<< "test getMinMaxSupportedValue failed for property: "
<< propIdToString(propId) << ", areaId: " << areaCfgPtr->getAreaId();
}
}
}
void VtsHalAutomotiveTest::testGetSupportedValuesListsForPropIdAreaId(
int32_t propId, const IHalAreaConfig& areaConfig, bool supportedValuesRequired) {
int32_t areaId = areaConfig.getAreaId();
VehiclePropertyType propertyType = getPropertyType(propId);
std::optional<HasSupportedValueInfo> maybeHasSupportedValueInfo =
areaConfig.getHasSupportedValueInfo();
if (!maybeHasSupportedValueInfo.has_value()) {
return;
}
if (!maybeHasSupportedValueInfo->hasSupportedValuesList) {
return;
}
VhalClientResult<std::vector<SupportedValuesListResult>> result =
mVhalClient->getSupportedValuesLists({PropIdAreaId{
.propId = propId,
.areaId = areaId,
}});
ASSERT_RESULT_OK(result)
<< "getSupportedValuesLists must return okay result if hasSupportedValuesList is true";
ASSERT_THAT(*result, ::testing::SizeIs(1))
<< "getSupportedValuesLists result list size must be 1 for 1 request";
const SupportedValuesListResult& individualResult = (*result)[0];
if (supportedValuesRequired) {
ASSERT_EQ(individualResult.status, StatusCode::OK)
<< "getSupportedValuesLists must return okay status if supported values are "
"required";
}
if (individualResult.status != StatusCode::OK) {
return;
}
ASSERT_TRUE(individualResult.supportedValuesList.has_value())
<< "supportedValuesList field must not be null if hasSupportedValuesList is true";
const std::vector<std::optional<RawPropValues>>& supportedValuesList =
individualResult.supportedValuesList.value();
if (supportedValuesRequired) {
ASSERT_THAT(supportedValuesList, ::testing::Not(::testing::IsEmpty()))
<< "supportedValuesList must not be empty if supported values are required";
}
for (const std::optional<RawPropValues>& supportedValue : supportedValuesList) {
ASSERT_TRUE(supportedValue.has_value())
<< "Each item in supportedValuesList must not be null";
ASSERT_NO_FATAL_FAILURE(verifyRawPropValues(*supportedValue, propertyType))
<< "one of supported value is not a valid RawPropValues for "
<< "the property type, value: " << supportedValue->toString();
}
}
// Test the getSupportedValues API. We use this one test case to cover all properties that
// may support this API.
TEST_P(VtsHalAutomotiveVehicleTargetTest, testGetSupportedValuesLists) {
if (!mVhalClient->isAidlVhal() || mVhalClient->getRemoteInterfaceVersion() < 4) {
GTEST_SKIP() << "Skip checking getSupportedValuesLists because the behavior is not "
"supported for current VHAL version";
}
auto configsResult = mVhalClient->getAllPropConfigs();
ASSERT_TRUE(configsResult.ok())
<< "Failed to get all property configs, error: " << configsResult.error().message();
for (const auto& cfgPtr : configsResult.value()) {
int32_t propId = cfgPtr->getPropId();
bool supportedValuesRequired = false;
std::unordered_set<std::string> annotations;
auto it = AnnotationsForVehicleProperty.find(static_cast<VehicleProperty>(propId));
if (it != AnnotationsForVehicleProperty.end()) {
annotations = it->second;
}
if (annotations.find(ANNOTATION_REQUIRE_SUPPORTED_VALUES) != annotations.end()) {
supportedValuesRequired = true;
}
const std::vector<std::unique_ptr<IHalAreaConfig>>& areaConfigs = cfgPtr->getAreaConfigs();
for (const auto& areaCfgPtr : areaConfigs) {
EXPECT_NO_FATAL_FAILURE(testGetSupportedValuesListsForPropIdAreaId(
propId, *areaCfgPtr, supportedValuesRequired))
<< "test getSupportedValues failed for property: " << propIdToString(propId)
<< ", areaId: " << areaCfgPtr->getAreaId();
}
}
}
void verifyPropertyConfigMinMaxValue(const IHalPropConfig* config,
VehiclePropertyType propertyType) {
for (const auto& areaConfig : config->getAreaConfigs()) {
std::optional<HasSupportedValueInfo> maybeHasSupportedValueInfo =
areaConfig->getHasSupportedValueInfo();
if (areaConfig->getMinInt32Value() != 0 || areaConfig->getMaxInt32Value() != 0) {
EXPECT_EQ(propertyType, VehiclePropertyType::INT32)
<< "minInt32Value and maxInt32Value must not be specified for INT32 type "
"property";
EXPECT_THAT(areaConfig->getMinInt32Value(),
::testing::Le(areaConfig->getMaxInt32Value()))
<< "minInt32Value must be less or equal to maxInt32Value";
if (maybeHasSupportedValueInfo.has_value()) {
EXPECT_TRUE(maybeHasSupportedValueInfo->hasMinSupportedValue)
<< "HasSupportedValueInfo.hasMinSupportedValue must be true because"
"minInt32Value is specified in VehicleAreaConfig";
EXPECT_TRUE(maybeHasSupportedValueInfo->hasMaxSupportedValue)
<< "HasSupportedValueInfo.hasMaxSupportedValue must be true because"
"maxInt32Value is specified in VehicleAreaConfig";
}
}
if (areaConfig->getMinFloatValue() != 0 || areaConfig->getMaxFloatValue() != 0) {
EXPECT_EQ(propertyType, VehiclePropertyType::FLOAT)
<< "minFloatValue and maxFloatValue must not be specified for FLOAT type "
"property";
EXPECT_THAT(areaConfig->getMinFloatValue(),
::testing::Le(areaConfig->getMaxFloatValue()))
<< "minFloatValue must be less or equal to maxFloatValue";
if (maybeHasSupportedValueInfo.has_value()) {
EXPECT_TRUE(maybeHasSupportedValueInfo->hasMinSupportedValue)
<< "HasSupportedValueInfo.hasMinSupportedValue must be true because"
"minFloatValue is specified in VehicleAreaConfig";
EXPECT_TRUE(maybeHasSupportedValueInfo->hasMaxSupportedValue)
<< "HasSupportedValueInfo.hasMaxSupportedValue must be true because"
"maxFloatValue is specified in VehicleAreaConfig";
}
}
if (areaConfig->getMinInt64Value() != 0 || areaConfig->getMaxInt64Value() != 0) {
EXPECT_EQ(propertyType, VehiclePropertyType::INT64)
<< "minInt64Value and maxInt64Value must not be specified for INT64 type "
"property";
EXPECT_THAT(areaConfig->getMinInt64Value(),
::testing::Le(areaConfig->getMaxInt64Value()))
<< "minInt64Value must be less or equal to maxInt64Value";
if (maybeHasSupportedValueInfo.has_value()) {
EXPECT_TRUE(maybeHasSupportedValueInfo->hasMinSupportedValue)
<< "HasSupportedValueInfo.hasMinSupportedValue must be true because"
"minInt64Value is specified in VehicleAreaConfig";
EXPECT_TRUE(maybeHasSupportedValueInfo->hasMaxSupportedValue)
<< "HasSupportedValueInfo.hasMaxSupportedValue must be true because"
"maxInt64Value is specified in VehicleAreaConfig";
}
}
if (maybeHasSupportedValueInfo.has_value() &&
(maybeHasSupportedValueInfo->hasMinSupportedValue ||
maybeHasSupportedValueInfo->hasMaxSupportedValue)) {
EXPECT_THAT(propertyType,
::testing::AnyOf(VehiclePropertyType::INT32, VehiclePropertyType::INT64,
VehiclePropertyType::FLOAT))
<< "HasSupportedValueInfo.hasMinSupportedValue and "
"HasSupportedValueInfo.hasMaxSupportedValue is only allowed to be set to "
"true "
"for INT32, INT64 or FLOAT type property";
}
}
}
void verifyPropertyConfigRequireMinMaxValue(const IHalPropConfig* config,
VehiclePropertyType propertyType) {
for (const auto& areaConfig : config->getAreaConfigs()) {
switch (propertyType) {
case VehiclePropertyType::INT32:
EXPECT_FALSE(areaConfig->getMinInt32Value() == 0 &&
areaConfig->getMaxInt32Value() == 0)
<< "minInt32Value and maxInt32Value must not both be 0 because "
"min and max value is required for this property";
break;
case VehiclePropertyType::FLOAT:
EXPECT_FALSE(areaConfig->getMinFloatValue() == 0 &&
areaConfig->getMaxFloatValue() == 0)
<< "minFloatValue and maxFloatValue must not both be 0 because "
"min and max value is required for this property";
break;
case VehiclePropertyType::INT64:
EXPECT_FALSE(areaConfig->getMinInt64Value() == 0 &&
areaConfig->getMaxInt64Value() == 0)
<< "minInt64Value and maxInt64Value must not both be 0 because "
"min and max value is required for this property";
break;
default:
// Do nothing.
break;
}
std::optional<HasSupportedValueInfo> maybeHasSupportedValueInfo =
areaConfig->getHasSupportedValueInfo();
if (maybeHasSupportedValueInfo.has_value()) {
EXPECT_TRUE(maybeHasSupportedValueInfo->hasMinSupportedValue)
<< "HasSupportedValueInfo.hasMinSupportedValue must be true because"
"min and max value is required for this property";
EXPECT_TRUE(maybeHasSupportedValueInfo->hasMaxSupportedValue)
<< "HasSupportedValueInfo.hasMaxSupportedValue must be true because"
"min and max value is required for this property";
}
}
}
void verifyPropertyConfigRequireSupportedValues(
const IHalPropConfig* config, const std::unordered_set<std::string>& annotations) {
bool supportedValuesInConfig =
(annotations.find(ANNOTATION_SUPPORTED_VALUES_IN_CONFIG) != annotations.end());
if (supportedValuesInConfig) {
const std::vector<int32_t>& configArray = config->getConfigArray();
EXPECT_THAT(configArray, Not(::testing::IsEmpty()))
<< "Config array must not be empty because supported values list must be specified"
<< " by the config array";
}
for (const auto& areaConfig : config->getAreaConfigs()) {
std::optional<HasSupportedValueInfo> maybeHasSupportedValueInfo =
areaConfig->getHasSupportedValueInfo();
if (maybeHasSupportedValueInfo.has_value()) {
EXPECT_TRUE(maybeHasSupportedValueInfo->hasSupportedValuesList)
<< "HasSupportedValueInfo.hasSupportedValuesList must be true because"
"supported values list is required for this property";
}
}
}
void verifyPropertyConfigDataEnum(const IHalPropConfig* config) {
for (const auto& areaConfig : config->getAreaConfigs()) {
std::optional<std::vector<int64_t>> maybeSupportedEnumValues =
areaConfig->getSupportedEnumValues();
if (!maybeSupportedEnumValues.has_value()) {
continue;
}
std::optional<HasSupportedValueInfo> maybeHasSupportedValueInfo =
areaConfig->getHasSupportedValueInfo();
const std::vector<int64_t>& supportedEnumValues = *maybeSupportedEnumValues;
if (!supportedEnumValues.empty() && maybeHasSupportedValueInfo.has_value()) {
EXPECT_TRUE(maybeHasSupportedValueInfo->hasSupportedValuesList)
<< "HasSupportedValueInfo.hasSupportedValuesList must be true because"
"supported enum values is not empty";
}
if (!supportedEnumValues.empty()) {
std::unordered_set<int64_t> allowedEnumValues = getSupportedEnumValuesForProperty(
static_cast<VehicleProperty>(config->getPropId()));
EXPECT_THAT(supportedEnumValues, ::testing::IsSubsetOf(allowedEnumValues))
<< "Supported enum values must be part of defined enum type";
}
}
}
/**
* Verifies that each property's property config is consistent with the requirement
* documented in VehicleProperty.aidl.
*/
TEST_P(VtsHalAutomotivePropertyConfigTest, verifyPropertyConfig) {
const PropertyConfigTestParam& param = std::get<0>(GetParam());
int expectedPropId = toInt(param.propId);
int expectedChangeMode = toInt(param.changeMode);
auto result = mVhalClient->getAllPropConfigs();
ASSERT_TRUE(result.ok()) << "Failed to get all property configs, error: "
<< result.error().message();
// Check if property is implemented by getting all configs and looking to see if the expected
// property id is in that list.
bool isExpectedPropIdImplemented = false;
for (const auto& cfgPtr : result.value()) {
const IHalPropConfig& cfg = *cfgPtr;
if (expectedPropId == cfg.getPropId()) {
isExpectedPropIdImplemented = true;
break;
}
}
if (!isExpectedPropIdImplemented) {
GTEST_SKIP() << StringPrintf("Property %" PRId32 " has not been implemented",
expectedPropId);
}
result = mVhalClient->getPropConfigs({expectedPropId});
ASSERT_TRUE(result.ok()) << "Failed to get required property config, error: "
<< result.error().message();
ASSERT_EQ(result.value().size(), 1u)
<< StringPrintf("Expect to get exactly 1 config, got %zu", result.value().size());
const auto& config = result.value().at(0);
int actualPropId = config->getPropId();
int actualChangeMode = config->getChangeMode();
ASSERT_EQ(actualPropId, expectedPropId)
<< StringPrintf("Expect to get property ID: %i, got %i", expectedPropId, actualPropId);
int globalAccess = config->getAccess();
if (config->getAreaConfigSize() == 0) {
verifyAccessMode(globalAccess, param.accessModes);
} else {
for (const auto& areaConfig : config->getAreaConfigs()) {
int areaConfigAccess = areaConfig->getAccess();
int actualAccess = (areaConfigAccess != toInt(VehiclePropertyAccess::NONE))
? areaConfigAccess
: globalAccess;
verifyAccessMode(actualAccess, param.accessModes);
}
}
if (actualPropId != toInt(VehicleProperty::REMOVE_USER)) {
EXPECT_EQ(actualChangeMode, expectedChangeMode)
<< StringPrintf("Expect to get VehiclePropertyChangeMode: %i, got %i",
expectedChangeMode, actualChangeMode);
} else {
// Special logic for REMOVE_USER property. We allow both STATIC and ON_CHANGE change mode
// because historically we define the change mode to be STATIC which is incorrect, it should
// be on_change. For backward compatibility, we have to allow both.
EXPECT_THAT(actualChangeMode, ::testing::AnyOf(toInt(VehiclePropertyChangeMode::STATIC),
toInt(VehiclePropertyChangeMode::ON_CHANGE)))
<< StringPrintf(
"Expect to get VehiclePropertyChangeMode as one of: "
"[STATIC, ON_CHANGE], got %i",
actualChangeMode);
}
std::unordered_set<std::string> annotations;
auto it = AnnotationsForVehicleProperty.find(param.propId);
if (it != AnnotationsForVehicleProperty.end()) {
annotations = it->second;
}
VehiclePropertyType propertyType = getPropertyType(expectedPropId);
verifyPropertyConfigMinMaxValue(config.get(), propertyType);
if (annotations.find(ANNOTATION_REQUIRE_MIN_MAX_VALUE) != annotations.end()) {
verifyPropertyConfigRequireMinMaxValue(config.get(), propertyType);
}
if (annotations.find(ANNOTATION_REQUIRE_SUPPORTED_VALUES) != annotations.end()) {
verifyPropertyConfigRequireSupportedValues(config.get(), annotations);
}
if (annotations.find(ANNOTATIONS_DATA_ENUM) != annotations.end()) {
verifyPropertyConfigDataEnum(config.get());
}
}
std::vector<ServiceDescriptor> getDescriptors() {
std::vector<ServiceDescriptor> descriptors;
for (std::string name : getAidlHalInstanceNames(IVehicle::descriptor)) {
descriptors.push_back({
.name = name,
.isAidlService = true,
});
}
for (std::string name : getAllHalInstanceNames(
android::hardware::automotive::vehicle::V2_0::IVehicle::descriptor)) {
descriptors.push_back({
.name = name,
.isAidlService = false,
});
}
return descriptors;
}
std::vector<PropertyConfigTestParam> getPropertyConfigTestParams() {
std::vector<PropertyConfigTestParam> testParams;
for (const auto& [propId, accessModes] : AllowedAccessForVehicleProperty) {
PropertyConfigTestParam param;
param.propId = propId;
param.accessModes = accessModes;
param.changeMode = ChangeModeForVehicleProperty[propId];
testParams.push_back(param);
}
return testParams;
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VtsHalAutomotiveVehicleTargetTest);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VtsHalAutomotivePropertyConfigTest);
INSTANTIATE_TEST_SUITE_P(PerInstance, VtsHalAutomotiveVehicleTargetTest,
testing::ValuesIn(getDescriptors()),
[](const testing::TestParamInfo<ServiceDescriptor>& info) {
std::string name = "";
if (info.param.isAidlService) {
name += "aidl_";
} else {
name += "hidl_";
}
name += info.param.name;
return Sanitize(name);
});
INSTANTIATE_TEST_SUITE_P(PerInstance, VtsHalAutomotivePropertyConfigTest,
testing::Combine(testing::ValuesIn(getPropertyConfigTestParams()),
testing::ValuesIn(getDescriptors())),
[](const testing::TestParamInfo<
std::tuple<PropertyConfigTestParam, ServiceDescriptor>>& info) {
std::string name = "";
if (std::get<1>(info.param).isAidlService) {
name += "aidl_";
} else {
name += "hidl_";
}
name += std::get<1>(info.param).name;
name += "_" + toString(std::get<0>(info.param).propId);
return Sanitize(name);
});
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
ABinderProcess_setThreadPoolMaxThreadCount(1);
return RUN_ALL_TESTS();
}