/* * Copyright (C) 2021 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. */ #include "VehicleUtils.h" #include namespace android { namespace hardware { namespace automotive { namespace vehicle { using ::aidl::android::hardware::automotive::vehicle::StatusCode; using ::aidl::android::hardware::automotive::vehicle::toString; using ::aidl::android::hardware::automotive::vehicle::VehicleArea; using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig; using ::aidl::android::hardware::automotive::vehicle::VehicleAreaDoor; using ::aidl::android::hardware::automotive::vehicle::VehicleAreaMirror; using ::aidl::android::hardware::automotive::vehicle::VehicleAreaSeat; using ::aidl::android::hardware::automotive::vehicle::VehicleAreaWheel; using ::aidl::android::hardware::automotive::vehicle::VehicleAreaWindow; using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig; using ::aidl::android::hardware::automotive::vehicle::VehicleProperty; using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyGroup; using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType; using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; using ::android::base::Error; using ::android::base::Result; using ::ndk::ScopedAStatus; namespace { class PropertyIdByNameSingleton { public: static PropertyIdByNameSingleton& getInstance() { static PropertyIdByNameSingleton instance; return instance; } Result getPropertyId(const std::string& name) const { auto it = mPropertyIdByName.find(name); if (it == mPropertyIdByName.end()) { return Error(); } return it->second; } PropertyIdByNameSingleton(PropertyIdByNameSingleton const&) = delete; void operator=(PropertyIdByNameSingleton const&) = delete; private: std::unordered_map mPropertyIdByName; PropertyIdByNameSingleton() { constexpr auto values = ndk::internal::enum_values; for (unsigned int i = 0; i < values.size(); i++) { mPropertyIdByName.emplace(toString(values[i]), toInt(values[i])); } } }; class AreaByNameSingleton { public: static AreaByNameSingleton& getInstance() { static AreaByNameSingleton instance; return instance; } Result getArea(const std::string& name, int32_t propId) const { VehicleArea areaType = getPropArea(propId); auto mapIt = mAreaByNameByAreaType.find(areaType); if (mapIt == mAreaByNameByAreaType.end()) { return Error() << "Invalid area type for property ID: " << propIdToString(propId); } const auto& areaByName = mapIt->second; auto it = areaByName.find(name); if (it == areaByName.end()) { return Error() << "Invalid area name for property " << propIdToString(propId) << ": " << name; } return it->second; } AreaByNameSingleton(AreaByNameSingleton const&) = delete; void operator=(AreaByNameSingleton const&) = delete; private: std::unordered_map> mAreaByNameByAreaType; AreaByNameSingleton() { populateMap(VehicleArea::WINDOW, ndk::internal::enum_values); populateMap(VehicleArea::MIRROR, ndk::internal::enum_values); populateMap(VehicleArea::SEAT, ndk::internal::enum_values); populateMap(VehicleArea::DOOR, ndk::internal::enum_values); populateMap(VehicleArea::WHEEL, ndk::internal::enum_values); } template void populateMap(VehicleArea areaType, std::array values) { for (unsigned int i = 0; i < values.size(); i++) { mAreaByNameByAreaType[areaType].emplace(toString(values[i]), toInt(values[i])); } } }; } // namespace Result checkPropValue(const VehiclePropValue& value, const VehiclePropConfig* config) { int32_t property = value.prop; VehiclePropertyType type = getPropType(property); switch (type) { case VehiclePropertyType::BOOLEAN: [[fallthrough]]; case VehiclePropertyType::INT32: if (value.value.int32Values.size() != 1) { return Error() << "expect 1 int32Values for INT32 type"; } break; case VehiclePropertyType::INT32_VEC: if (value.value.int32Values.size() < 1) { return Error() << "expect >=1 int32Values for INT32_VEC type"; } break; case VehiclePropertyType::INT64: if (value.value.int64Values.size() != 1) { return Error() << "expect 1 int64Values for INT64 type"; } break; case VehiclePropertyType::INT64_VEC: if (value.value.int64Values.size() < 1) { return Error() << "expect >=1 int64Values for INT64_VEC type"; } break; case VehiclePropertyType::FLOAT: if (value.value.floatValues.size() != 1) { return Error() << "expect 1 floatValues for FLOAT type"; } break; case VehiclePropertyType::FLOAT_VEC: if (value.value.floatValues.size() < 1) { return Error() << "expect >=1 floatValues for FLOAT_VEC type"; } break; case VehiclePropertyType::BYTES: // We allow setting an empty bytes array. break; case VehiclePropertyType::STRING: // We allow setting an empty string. break; case VehiclePropertyType::MIXED: if (getPropGroup(property) == VehiclePropertyGroup::VENDOR) { // We only checks vendor mixed properties. return checkVendorMixedPropValue(value, config); } break; default: return Error() << "unknown property type: " << toInt(type); } return {}; } Result checkVendorMixedPropValue(const VehiclePropValue& value, const VehiclePropConfig* config) { auto configArray = config->configArray; // configArray[0], 1 indicates the property has a String value, we allow the string value to // be empty. size_t int32Count = 0; // configArray[1], 1 indicates the property has a Boolean value. if (configArray[1] == 1) { int32Count++; } // configArray[2], 1 indicates the property has an Integer value. if (configArray[2] == 1) { int32Count++; } // configArray[3], the number indicates the size of Integer[] in the property. int32Count += static_cast(configArray[3]); size_t int32Size = value.value.int32Values.size(); if (int32Size != int32Count) { return Error() << "invalid mixed property, got " << int32Size << " int32Values, expect " << int32Count; } size_t int64Count = 0; // configArray[4], 1 indicates the property has a Long value. if (configArray[4] == 1) { int64Count++; } // configArray[5], the number indicates the size of Long[] in the property. int64Count += static_cast(configArray[5]); size_t int64Size = value.value.int64Values.size(); if (int64Size != int64Count) { return Error() << "invalid mixed property, got " << int64Size << " int64Values, expect " << int64Count; } size_t floatCount = 0; // configArray[6], 1 indicates the property has a Float value. if (configArray[6] == 1) { floatCount++; } // configArray[7], the number indicates the size of Float[] in the property. floatCount += static_cast(configArray[7]); size_t floatSize = value.value.floatValues.size(); if (floatSize != floatCount) { return Error() << "invalid mixed property, got " << floatSize << " floatValues, expect " << floatCount; } // configArray[8], the number indicates the size of byte[] in the property. size_t byteSize = value.value.byteValues.size(); size_t byteCount = static_cast(configArray[8]); if (byteCount != 0 && byteSize != byteCount) { return Error() << "invalid mixed property, got " << byteSize << " byteValues, expect " << byteCount; } return {}; } Result checkValueRange(const VehiclePropValue& value, const VehicleAreaConfig* areaConfig) { if (areaConfig == nullptr) { return {}; } int32_t property = value.prop; VehiclePropertyType type = getPropType(property); switch (type) { case VehiclePropertyType::INT32: [[fallthrough]]; case VehiclePropertyType::INT32_VEC: if (areaConfig->minInt32Value == 0 && areaConfig->maxInt32Value == 0) { break; } for (int32_t int32Value : value.value.int32Values) { if (int32Value < areaConfig->minInt32Value || int32Value > areaConfig->maxInt32Value) { return Error() << "int32Value: " << int32Value << " out of range, min: " << areaConfig->minInt32Value << " max: " << areaConfig->maxInt32Value; } } break; case VehiclePropertyType::INT64: [[fallthrough]]; case VehiclePropertyType::INT64_VEC: if (areaConfig->minInt64Value == 0 && areaConfig->maxInt64Value == 0) { break; } for (int64_t int64Value : value.value.int64Values) { if (int64Value < areaConfig->minInt64Value || int64Value > areaConfig->maxInt64Value) { return Error() << "int64Value: " << int64Value << " out of range, min: " << areaConfig->minInt64Value << " max: " << areaConfig->maxInt64Value; } } break; case VehiclePropertyType::FLOAT: [[fallthrough]]; case VehiclePropertyType::FLOAT_VEC: if (areaConfig->minFloatValue == 0.f && areaConfig->maxFloatValue == 0.f) { break; } for (float floatValue : value.value.floatValues) { if (floatValue < areaConfig->minFloatValue || floatValue > areaConfig->maxFloatValue) { return Error() << "floatValue: " << floatValue << " out of range, min: " << areaConfig->minFloatValue << " max: " << areaConfig->maxFloatValue; } } break; default: // We don't check the rest of property types. Additional logic needs to be added if // required in VehicleHardware, e.g. you might want to check the range for mixed // property. break; } return {}; } StatusCode VhalError::value() const { return mCode; } std::string VhalError::print() const { return aidl::android::hardware::automotive::vehicle::toString(mCode); } Result stringToPropId(const std::string& propName) { return PropertyIdByNameSingleton::getInstance().getPropertyId(propName); } Result stringToArea(const std::string& areaName, int32_t propId) { return AreaByNameSingleton::getInstance().getArea(areaName, propId); } } // namespace vehicle } // namespace automotive } // namespace hardware } // namespace android