Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6b58b66d authored by Yu Shan's avatar Yu Shan Committed by Android (Google) Code Review
Browse files

Merge changes Ib0fa499e,I5f602d59 into main

* changes:
  Allow areaId JSON fields to inherit from property fields.
  Allow setting supported values for reference VHAL.
parents 39bb3cb7 68faf519
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -39,6 +39,9 @@ struct ConfigDeclaration {
    std::unordered_map<int32_t, aidl::android::hardware::automotive::vehicle::RawPropValues>
            initialAreaValues;

    // The optional supported values for each areaId.
    std::unordered_map<int32_t, std::vector<float>> supportedValuesForAreaId;

    inline bool operator==(const ConfigDeclaration& other) const {
        return (config == other.config && initialValue == other.initialValue &&
                initialAreaValues == other.initialAreaValues);
+25 −1
Original line number Diff line number Diff line
@@ -108,11 +108,23 @@ class JsonConfigParser {
    // @param fieldIsOptional Whether the field is optional.
    // @param outPtr The pointer to output to if the field exists and parsing succeeded.
    // @param errors The error array to append error to if errors are found.
    // @return true if the field is optional and does not exist or parsed successfully.
    // @param found if not nullptr, this will be set to true if the field is found.
    // @return true if parsed successfully or the field is optional and is not found.
    template <class T>
    bool tryParseJsonValueToVariable(const Json::Value& parentJsonNode,
                                     const std::string& fieldName, bool fieldIsOptional, T* outPtr,
                                     std::vector<std::string>* errors, bool* found = nullptr);

    // Tries to parse a JSON value to a specific type.
    //
    // This is similar to the previous version except that it tries to find the field in multiple
    // parent nodes and will return early if the field is found in one parent node. This is useful
    // when we allow the field to either come from vehicleArea fields or vehicleProperty fields.
    template <class T>
    bool tryParseJsonValueToVariable(std::vector<const Json::Value*> parentJsonNodePtrs,
                                     const std::string& fieldName, bool fieldIsOptional, T* outPtr,
                                     std::vector<std::string>* errors);

    // Tries to parse a JSON value to an array of specific type.
    //
    // If fieldIsOptional is True, then if the field specified by "fieldName" does not exist,
@@ -126,8 +138,20 @@ class JsonConfigParser {
    // @return true if the field is optional and does not exist or parsed successfully.
    template <class T>
    bool tryParseJsonArrayToVariable(const Json::Value& parentJsonNode,
                                     const std::string& fieldName, bool fieldIsOptional,
                                     std::vector<T>* outPtr, std::vector<std::string>* errors,
                                     bool* found = nullptr);

    // Tries to parse a JSON value to an array of specific type.
    //
    // This is similar to the previous version except that it tries to find the field in multiple
    // parent nodes and will return early if the field is found in one parent node. This is useful
    // when we allow the field to either come from vehicleArea fields or vehicleProperty fields.
    template <class T>
    bool tryParseJsonArrayToVariable(std::vector<const Json::Value*> parentJsonNodePtrs,
                                     const std::string& fieldName, bool fieldIsOptional,
                                     std::vector<T>* outPtr, std::vector<std::string>* errors);

    // Parses a JSON field to VehiclePropertyAccess or VehiclePropertyChangeMode.
    template <class T>
    void parseAccessChangeMode(const Json::Value& parentJsonNode, const std::string& fieldName,
+127 −22
Original line number Diff line number Diff line
@@ -95,6 +95,12 @@ using ::aidl::android::hardware::automotive::vehicle::WindshieldWipersSwitch;
using ::android::base::Error;
using ::android::base::Result;

int32_t COMPATIBLE_API_VERSIONS[] = {
        // The base version.
        1,
        // V2 supports inherit areaId fields from parent property fields.
        2};

// Defines a map from constant names to constant values, the values defined here corresponds to
// the "Constants::XXXX" used in JSON config file.
const std::unordered_map<std::string, int> CONSTANTS_BY_NAME = {
@@ -148,6 +154,17 @@ const std::unordered_map<std::string, int> CONSTANTS_BY_NAME = {
         toInt(VehicleAreaMirror::DRIVER_LEFT) | toInt(VehicleAreaMirror::DRIVER_RIGHT)},
};

std::string nodesToStr(const std::vector<const Json::Value*>& nodePtrs) {
    std::string nodesStr = "";
    for (const Json::Value* nodePtr : nodePtrs) {
        if (nodesStr != "") {
            nodesStr += ", ";
        }
        nodesStr += nodePtr->toStyledString();
    }
    return nodesStr;
}

// A class to parse constant values for type T where T is defined as an enum in NDK AIDL backend.
template <class T>
class ConstantParser final : public ConstantParserInterface {
@@ -446,11 +463,34 @@ Result<int> JsonValueParser::parseConstantValue(
    return result;
}

template <class T>
bool JsonConfigParser::tryParseJsonValueToVariable(
        std::vector<const Json::Value*> parentJsonNodePtrs, const std::string& fieldName,
        bool fieldIsOptional, T* outPtr, std::vector<std::string>* errors) {
    bool found = false;
    for (const Json::Value* parentJsonNodePtr : parentJsonNodePtrs) {
        bool result = tryParseJsonValueToVariable(*parentJsonNodePtr, fieldName,
                                                  /*fieldIsOptional=*/true, outPtr, errors, &found);
        if (!result) {
            return result;
        }
        if (found) {
            return true;
        }
    }
    if (!fieldIsOptional && !found) {
        errors->push_back("Missing required field: " + fieldName +
                          " in nodes: " + nodesToStr(parentJsonNodePtrs));
        return false;
    }
    return true;
}

template <class T>
bool JsonConfigParser::tryParseJsonValueToVariable(const Json::Value& parentJsonNode,
                                                   const std::string& fieldName,
                                                   bool fieldIsOptional, T* outPtr,
                                                   std::vector<std::string>* errors) {
                                                   std::vector<std::string>* errors, bool* found) {
    if (!parentJsonNode.isObject()) {
        errors->push_back("Node: " + parentJsonNode.toStyledString() + " is not an object");
        return false;
@@ -469,6 +509,32 @@ bool JsonConfigParser::tryParseJsonValueToVariable(const Json::Value& parentJson
        return false;
    }
    *outPtr = std::move(result.value());
    if (found != nullptr) {
        *found = true;
    }
    return true;
}

template <class T>
bool JsonConfigParser::tryParseJsonArrayToVariable(
        std::vector<const Json::Value*> parentJsonNodePtrs, const std::string& fieldName,
        bool fieldIsOptional, std::vector<T>* outPtr, std::vector<std::string>* errors) {
    bool found = false;
    for (const Json::Value* parentJsonNodePtr : parentJsonNodePtrs) {
        bool result = tryParseJsonArrayToVariable(*parentJsonNodePtr, fieldName,
                                                  /*fieldIsOptional=*/true, outPtr, errors, &found);
        if (!result) {
            return result;
        }
        if (found) {
            return true;
        }
    }
    if (!fieldIsOptional && !found) {
        errors->push_back("Missing required field: " + fieldName +
                          " in nodes: " + nodesToStr(parentJsonNodePtrs));
        return false;
    }
    return true;
}

@@ -476,7 +542,7 @@ template <class T>
bool JsonConfigParser::tryParseJsonArrayToVariable(const Json::Value& parentJsonNode,
                                                   const std::string& fieldName,
                                                   bool fieldIsOptional, std::vector<T>* outPtr,
                                                   std::vector<std::string>* errors) {
                                                   std::vector<std::string>* errors, bool* found) {
    if (!parentJsonNode.isObject()) {
        errors->push_back("Node: " + parentJsonNode.toStyledString() + " is not an object");
        return false;
@@ -495,6 +561,9 @@ bool JsonConfigParser::tryParseJsonArrayToVariable(const Json::Value& parentJson
        return false;
    }
    *outPtr = std::move(result.value());
    if (found != nullptr) {
        *found = true;
    }
    return true;
}

@@ -574,44 +643,60 @@ void JsonConfigParser::parseAreas(const Json::Value& parentJsonNode, const std::
        }
        VehicleAreaConfig areaConfig = {};
        areaConfig.areaId = areaId;
        // We have already parsed the access in parentJsonNode into config, so we do not have to
        // parse parentNode again here.
        parseAccessChangeMode(jsonAreaConfig, "access", propStr, &(config->config.access),
                              &areaConfig.access, errors);
        tryParseJsonValueToVariable(jsonAreaConfig, "minInt32Value", /*optional=*/true,
                                    &areaConfig.minInt32Value, errors);
        tryParseJsonValueToVariable(jsonAreaConfig, "maxInt32Value", /*optional=*/true,
                                    &areaConfig.maxInt32Value, errors);
        tryParseJsonValueToVariable(jsonAreaConfig, "minInt64Value", /*optional=*/true,
                                    &areaConfig.minInt64Value, errors);
        tryParseJsonValueToVariable(jsonAreaConfig, "maxInt64Value", /*optional=*/true,
                                    &areaConfig.maxInt64Value, errors);
        tryParseJsonValueToVariable(jsonAreaConfig, "minFloatValue", /*optional=*/true,
                                    &areaConfig.minFloatValue, errors);
        tryParseJsonValueToVariable(jsonAreaConfig, "maxFloatValue", /*optional=*/true,
                                    &areaConfig.maxFloatValue, errors);
        // All the following fields may come from area config or from parent node (property config).
        tryParseJsonValueToVariable({&jsonAreaConfig, &parentJsonNode}, "minInt32Value",
                                    /*optional=*/true, &areaConfig.minInt32Value, errors);
        tryParseJsonValueToVariable({&jsonAreaConfig, &parentJsonNode}, "maxInt32Value",
                                    /*optional=*/true, &areaConfig.maxInt32Value, errors);
        tryParseJsonValueToVariable({&jsonAreaConfig, &parentJsonNode}, "minInt64Value",
                                    /*optional=*/true, &areaConfig.minInt64Value, errors);
        tryParseJsonValueToVariable({&jsonAreaConfig, &parentJsonNode}, "maxInt64Value",
                                    /*optional=*/true, &areaConfig.maxInt64Value, errors);
        tryParseJsonValueToVariable({&jsonAreaConfig, &parentJsonNode}, "minFloatValue",
                                    /*optional=*/true, &areaConfig.minFloatValue, errors);
        tryParseJsonValueToVariable({&jsonAreaConfig, &parentJsonNode}, "maxFloatValue",
                                    /*optional=*/true, &areaConfig.maxFloatValue, errors);

        // By default we support variable update rate for all properties except it is explicitly
        // disabled.
        areaConfig.supportVariableUpdateRate = true;
        tryParseJsonValueToVariable(jsonAreaConfig, "supportVariableUpdateRate", /*optional=*/true,
                                    &areaConfig.supportVariableUpdateRate, errors);
        tryParseJsonValueToVariable({&jsonAreaConfig, &parentJsonNode}, "supportVariableUpdateRate",
                                    /*optional=*/true, &areaConfig.supportVariableUpdateRate,
                                    errors);

        std::vector<int64_t> supportedEnumValues;
        tryParseJsonArrayToVariable(jsonAreaConfig, "supportedEnumValues", /*optional=*/true,
                                    &supportedEnumValues, errors);
        tryParseJsonArrayToVariable({&jsonAreaConfig, &parentJsonNode}, "supportedEnumValues",
                                    /*optional=*/true, &supportedEnumValues, errors);
        if (!supportedEnumValues.empty()) {
            areaConfig.supportedEnumValues = std::move(supportedEnumValues);
        }

        std::vector<float> supportedValues;
        tryParseJsonArrayToVariable({&jsonAreaConfig, &parentJsonNode}, "supportedValues",
                                    /*optional=*/true, &supportedValues, errors);
        if (!supportedValues.empty()) {
            config->supportedValuesForAreaId[areaId] = std::move(supportedValues);
        }

        const Json::Value* jsonHasSupportedValueInfo = nullptr;
        if (jsonAreaConfig.isMember("hasSupportedValueInfo")) {
            jsonHasSupportedValueInfo = &jsonAreaConfig["hasSupportedValueInfo"];
        } else if (parentJsonNode.isMember("hasSupportedValueInfo")) {
            jsonHasSupportedValueInfo = &parentJsonNode["hasSupportedValueInfo"];
        }
        if (jsonHasSupportedValueInfo != nullptr) {
            HasSupportedValueInfo hasSupportedValueInfo = HasSupportedValueInfo{};
            const Json::Value& jsonHasSupportedValueInfo = jsonAreaConfig["hasSupportedValueInfo"];
            tryParseJsonValueToVariable(jsonHasSupportedValueInfo, "hasMinSupportedValue",
            tryParseJsonValueToVariable(*jsonHasSupportedValueInfo, "hasMinSupportedValue",
                                        /*optional=*/true,
                                        &hasSupportedValueInfo.hasMinSupportedValue, errors);
            tryParseJsonValueToVariable(jsonHasSupportedValueInfo, "hasMaxSupportedValue",
            tryParseJsonValueToVariable(*jsonHasSupportedValueInfo, "hasMaxSupportedValue",
                                        /*optional=*/true,
                                        &hasSupportedValueInfo.hasMaxSupportedValue, errors);
            tryParseJsonValueToVariable(jsonHasSupportedValueInfo, "hasSupportedValuesList",
            tryParseJsonValueToVariable(*jsonHasSupportedValueInfo, "hasSupportedValuesList",
                                        /*optional=*/true,
                                        &hasSupportedValueInfo.hasSupportedValuesList, errors);
            areaConfig.hasSupportedValueInfo = std::move(hasSupportedValueInfo);
@@ -622,6 +707,11 @@ void JsonConfigParser::parseAreas(const Json::Value& parentJsonNode, const std::
        RawPropValues areaValue = {};
        if (parsePropValues(jsonAreaConfig, "defaultValue", &areaValue, errors)) {
            config->initialAreaValues[areaId] = std::move(areaValue);
        } else {
            if (config->initialValue != RawPropValues{}) {
                // Skip empty initial values.
                config->initialAreaValues[areaId] = config->initialValue;
            }
        }
    }
}
@@ -701,6 +791,21 @@ Result<std::unordered_map<int32_t, ConfigDeclaration>> JsonConfigParser::parseJs
    if (!root.isObject()) {
        return Error() << "root element must be an object";
    }
    // Default API version is 1.
    int32_t apiVersion = 1;
    if (root.isMember("apiVersion")) {
        apiVersion = static_cast<int32_t>(root["apiVersion"].asInt());
    }
    bool compatible = false;
    for (int32_t compatibleApiVersion : COMPATIBLE_API_VERSIONS) {
        if (compatibleApiVersion == apiVersion) {
            compatible = true;
            break;
        }
    }
    if (!compatible) {
        return Error() << "The JSON file is not compatible with the JSON loader version";
    }
    if (!root.isMember("properties") || !root["properties"].isArray()) {
        return Error() << "Missing 'properties' field in root or the field is not an array";
    }
+169 −0
Original line number Diff line number Diff line
@@ -314,6 +314,33 @@ TEST_F(JsonConfigLoaderUnitTest, testAccessOverride) {
    ASSERT_EQ(propConfig.changeMode, VehiclePropertyChangeMode::STATIC);
}

TEST_F(JsonConfigLoaderUnitTest, testAccessAreaOverride) {
    std::istringstream iss(R"(
    {
        "properties": [{
            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
            "areas": [
                {
                    "areaId": 0,
                    "access": "VehiclePropertyAccess::WRITE"
                }
            ]
        }]
    }
    )");

    auto result = mLoader.loadPropConfig(iss);

    ASSERT_TRUE(result.ok()) << result.error().message();
    auto configs = result.value();
    ASSERT_EQ(configs.size(), 1u);

    const VehiclePropConfig& propConfig = configs.begin()->second.config;
    ASSERT_EQ(propConfig.access, VehiclePropertyAccess::READ);
    ASSERT_EQ(propConfig.areaConfigs[0].access, VehiclePropertyAccess::WRITE);
    ASSERT_EQ(propConfig.changeMode, VehiclePropertyChangeMode::STATIC);
}

TEST_F(JsonConfigLoaderUnitTest, testChangeModeOverride) {
    std::istringstream iss(R"(
    {
@@ -564,6 +591,148 @@ TEST_F(JsonConfigLoaderUnitTest, testAreas_Simple) {
    ASSERT_EQ(areaConfig.areaId, HVAC_ALL);
}

TEST_F(JsonConfigLoaderUnitTest, testAreas_InheritFromProperty) {
    std::istringstream iss(R"(
    {
        "properties": [{
            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
            "minInt32Value": 1,
            "maxInt32Value": 7,
            "minInt64Value": 2,
            "maxInt64Value": 6,
            "minFloatValue": 1.1,
            "maxFloatValue": 2.2,
            "supportVariableUpdateRate": true,
            "supportedEnumValues": [1, 2, 3],
            "hasSupportedValueInfo": {
                "hasMinSupportedValue": true,
                "hasMaxSupportedValue": true,
                "hasSupportedValuesList": true
            },
            "defaultValue": {
                "int32Values": [
                    1
                ]
            },
            "areas": [{
                "areaId": "Constants::HVAC_ALL"
            }]
        }]
    }
    )");

    auto result = mLoader.loadPropConfig(iss);

    ASSERT_RESULT_OK(result);

    auto configs = result.value();
    ASSERT_EQ(configs.size(), 1u);

    const auto& configDecl = configs.begin()->second;
    const VehiclePropConfig& config = configDecl.config;
    EXPECT_EQ(config.access, VehiclePropertyAccess::READ);
    ASSERT_EQ(config.areaConfigs.size(), 1u);
    const VehicleAreaConfig& areaConfig = config.areaConfigs[0];
    EXPECT_EQ(areaConfig.minInt32Value, 1);
    EXPECT_EQ(areaConfig.maxInt32Value, 7);
    EXPECT_EQ(areaConfig.minInt64Value, 2);
    EXPECT_EQ(areaConfig.maxInt64Value, 6);
    EXPECT_EQ(areaConfig.minFloatValue, 1.1f);
    EXPECT_EQ(areaConfig.maxFloatValue, 2.2f);
    EXPECT_EQ(areaConfig.access, VehiclePropertyAccess::READ);
    EXPECT_EQ(areaConfig.areaId, HVAC_ALL);
    EXPECT_EQ(areaConfig.supportVariableUpdateRate, true);
    ASSERT_TRUE(areaConfig.supportedEnumValues.has_value());
    EXPECT_EQ(areaConfig.supportedEnumValues.value(), std::vector<int64_t>({1, 2, 3}));
    ASSERT_TRUE(areaConfig.hasSupportedValueInfo.has_value());
    EXPECT_TRUE(areaConfig.hasSupportedValueInfo->hasMinSupportedValue);
    EXPECT_TRUE(areaConfig.hasSupportedValueInfo->hasMaxSupportedValue);
    EXPECT_TRUE(areaConfig.hasSupportedValueInfo->hasSupportedValuesList);
    ASSERT_FALSE(configDecl.initialAreaValues.find(HVAC_ALL) == configDecl.initialAreaValues.end());
    EXPECT_EQ(configDecl.initialAreaValues.find(HVAC_ALL)->second,
              RawPropValues{.int32Values = {1}});
}

TEST_F(JsonConfigLoaderUnitTest, testAreas_InheritFromProperty_override) {
    std::istringstream iss(R"(
    {
        "properties": [{
            "property": "VehicleProperty::INFO_FUEL_CAPACITY",
            "minInt32Value": 100,
            "maxInt32Value": 100,
            "minInt64Value": 100,
            "maxInt64Value": 100,
            "minFloatValue": 100.1,
            "maxFloatValue": 100.2,
            "supportVariableUpdateRate": false,
            "supportedEnumValues": [3, 2, 1],
            "hasSupportedValueInfo": {
                "hasMinSupportedValue": false,
                "hasMaxSupportedValue": false,
                "hasSupportedValuesList": false
            },
            "defaultValue": {
                "int32Values": [
                    2
                ]
            },
            "areas": [{
                "areaId": "Constants::HVAC_ALL",
                "minInt32Value": 1,
                "maxInt32Value": 7,
                "minInt64Value": 2,
                "maxInt64Value": 6,
                "minFloatValue": 1.1,
                "maxFloatValue": 2.2,
                "supportVariableUpdateRate": true,
                "supportedEnumValues": [1, 2, 3],
                "hasSupportedValueInfo": {
                    "hasMinSupportedValue": true,
                    "hasMaxSupportedValue": true,
                    "hasSupportedValuesList": true
                },
                "defaultValue": {
                    "int32Values": [
                        1
                    ]
                }
            }]
        }]
    }
    )");

    auto result = mLoader.loadPropConfig(iss);

    ASSERT_RESULT_OK(result);

    auto configs = result.value();
    ASSERT_EQ(configs.size(), 1u);

    const auto& configDecl = configs.begin()->second;
    const VehiclePropConfig& config = configDecl.config;
    EXPECT_EQ(config.access, VehiclePropertyAccess::READ);
    ASSERT_EQ(config.areaConfigs.size(), 1u);
    const VehicleAreaConfig& areaConfig = config.areaConfigs[0];
    EXPECT_EQ(areaConfig.minInt32Value, 1);
    EXPECT_EQ(areaConfig.maxInt32Value, 7);
    EXPECT_EQ(areaConfig.minInt64Value, 2);
    EXPECT_EQ(areaConfig.maxInt64Value, 6);
    EXPECT_EQ(areaConfig.minFloatValue, 1.1f);
    EXPECT_EQ(areaConfig.maxFloatValue, 2.2f);
    EXPECT_EQ(areaConfig.access, VehiclePropertyAccess::READ);
    EXPECT_EQ(areaConfig.areaId, HVAC_ALL);
    EXPECT_EQ(areaConfig.supportVariableUpdateRate, true);
    ASSERT_TRUE(areaConfig.supportedEnumValues.has_value());
    EXPECT_EQ(areaConfig.supportedEnumValues.value(), std::vector<int64_t>({1, 2, 3}));
    ASSERT_TRUE(areaConfig.hasSupportedValueInfo.has_value());
    EXPECT_TRUE(areaConfig.hasSupportedValueInfo->hasMinSupportedValue);
    EXPECT_TRUE(areaConfig.hasSupportedValueInfo->hasMaxSupportedValue);
    EXPECT_TRUE(areaConfig.hasSupportedValueInfo->hasSupportedValuesList);
    ASSERT_FALSE(configDecl.initialAreaValues.find(HVAC_ALL) == configDecl.initialAreaValues.end());
    EXPECT_EQ(configDecl.initialAreaValues.find(HVAC_ALL)->second,
              RawPropValues{.int32Values = {1}});
}

TEST_F(JsonConfigLoaderUnitTest, testAreas_DefaultValueForEachArea) {
    std::istringstream iss(R"(
    {
+1004 −1846

File changed.

Preview size limit exceeded, changes collapsed.

Loading