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

Commit 68faf519 authored by Yu Shan's avatar Yu Shan
Browse files

Allow areaId JSON fields to inherit from property fields.

Update json config parser to V2, which allows areaId fields, e.g.
minInt32Value to inherit from parent property fields.

This CL also updates the test JSON file to move the common fields
up one level to avoid duplication.

We increase the JSON file version because the new version is not
compatible with an older parser.

Test: atest FakeVehicleHardwareTest JsonConfigLoaderTest
Flag: EXEMPT refactor
Bug: 395147447
Change-Id: Ib0fa499e31888cfee197f6c4e44f73f509300a4f
parent f8b99c20
Loading
Loading
Loading
Loading
+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,
+122 −24
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,51 +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, "supportedValues", /*optional=*/true,
                                    &supportedValues, errors);
        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);
@@ -629,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;
            }
        }
    }
}
@@ -708,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"(
    {
+975 −2119

File changed.

Preview size limit exceeded, changes collapsed.

+27 −36
Original line number Diff line number Diff line
{
    "apiVersion": 2,
    "properties": [
        {
            "property": "TestVendorProperty::MIXED_TYPE_PROPERTY_FOR_TEST",
@@ -75,13 +76,7 @@
                            1.0
                        ]
                    },
                    "areaId": "Constants::HVAC_LEFT",
                    "minFloatValue": -10.0,
                    "maxFloatValue": 10.0,
                    "hasSupportedValueInfo": {
                        "hasMinSupportedValue": true,
                        "hasMaxSupportedValue": true
                    }
                    "areaId": "Constants::HVAC_LEFT"
                },
                {
                    "defaultValue": {
@@ -89,17 +84,17 @@
                            2.0
                        ]
                    },
                    "areaId": "Constants::HVAC_RIGHT",
                    "minFloatValue": -10.0,
                    "maxFloatValue": 10.0,
                    "hasSupportedValueInfo": {
                        "hasMinSupportedValue": true,
                        "hasMaxSupportedValue": true
                    }
                    "areaId": "Constants::HVAC_RIGHT"
                }
            ],
            "access": "VehiclePropertyAccess::READ_WRITE",
            "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
            "changeMode": "VehiclePropertyChangeMode::ON_CHANGE",
            "hasSupportedValueInfo": {
                "hasMinSupportedValue": true,
                "hasMaxSupportedValue": true
            },
            "minFloatValue": -10.0,
            "maxFloatValue": 10.0
        },
        {
            "property": "TestVendorProperty::VENDOR_EXTENSION_INT_PROPERTY",
@@ -110,19 +105,7 @@
                            2
                        ]
                    },
                    "areaId": "VehicleAreaWindow::FRONT_WINDSHIELD",
                    "minInt32Value": -100,
                    "maxInt32Value": 100,
                    "hasSupportedValueInfo": {
                        "hasMinSupportedValue": true,
                        "hasMaxSupportedValue": true,
                        "hasSupportedValuesList": true
                    },
                    "supportedValues": [
                        1,
                        2,
                        3
                    ]
                    "areaId": "VehicleAreaWindow::FRONT_WINDSHIELD"
                },
                {
                    "defaultValue": {
@@ -130,9 +113,7 @@
                            0
                        ]
                    },
                    "areaId": "VehicleAreaWindow::REAR_WINDSHIELD",
                    "minInt32Value": -100,
                    "maxInt32Value": 100
                    "areaId": "VehicleAreaWindow::REAR_WINDSHIELD"
                },
                {
                    "defaultValue": {
@@ -140,13 +121,23 @@
                            -1
                        ]
                    },
                    "areaId": "VehicleAreaWindow::ROOF_TOP_1",
                    "minInt32Value": -100,
                    "maxInt32Value": 100
                    "areaId": "VehicleAreaWindow::ROOF_TOP_1"
                }
            ],
            "access": "VehiclePropertyAccess::READ_WRITE",
            "changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
            "changeMode": "VehiclePropertyChangeMode::ON_CHANGE",
            "hasSupportedValueInfo": {
                "hasMinSupportedValue": true,
                "hasMaxSupportedValue": true,
                "hasSupportedValuesList": true
            },
            "minInt32Value": -100,
            "maxInt32Value": 100,
            "supportedValues": [
                1,
                2,
                3
            ]
        },
        {
            "property": "TestVendorProperty::VENDOR_EXTENSION_STRING_PROPERTY",