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

Commit 73446f49 authored by Yu Shan's avatar Yu Shan
Browse files

Support set property in dump.

Support debug interface to set property value in FakeVehicleHardware.

Test: atest FakeVehicleHardwareTest.
Bug: 214613918
Change-Id: I64ef532274e20db1444e8583d71e0c0955c5b460
parent 93d36287
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -131,6 +131,7 @@ class FakeVehicleHardware final : public IVehicleHardware {
    std::string dumpHelp();
    std::string dumpListProperties();
    std::string dumpSpecificProperty(const std::vector<std::string>& options);
    std::string dumpSetProperties(const std::vector<std::string>& options);

    template <typename T>
    ::android::base::Result<T> safelyParseInt(int index, const std::string& s) {
@@ -141,6 +142,12 @@ class FakeVehicleHardware final : public IVehicleHardware {
        }
        return out;
    }
    ::android::base::Result<float> safelyParseFloat(int index, const std::string& s);
    std::vector<std::string> getOptionValues(const std::vector<std::string>& options,
                                             size_t* index);
    ::android::base::Result<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>
    parseSetPropOptions(const std::vector<std::string>& options);
    ::android::base::Result<std::vector<uint8_t>> parseHexString(const std::string& s);

    ::android::base::Result<void> checkArgumentsSize(const std::vector<std::string>& options,
                                                     size_t minSize);
+204 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include <PropertyUtils.h>
#include <VehicleHalTypes.h>
#include <VehicleUtils.h>
#include <android-base/parsedouble.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <utils/Log.h>
@@ -62,12 +63,29 @@ using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;

using ::android::base::EqualsIgnoreCase;
using ::android::base::Error;
using ::android::base::ParseFloat;
using ::android::base::Result;
using ::android::base::StartsWith;
using ::android::base::StringPrintf;

const char* VENDOR_OVERRIDE_DIR = "/vendor/etc/automotive/vhaloverride/";
const char* OVERRIDE_PROPERTY = "persist.vendor.vhal_init_value_override";

// A list of supported options for "--set" command.
const std::unordered_set<std::string> SET_PROP_OPTIONS = {
        // integer.
        "-i",
        // 64bit integer.
        "-i64",
        // float.
        "-f",
        // string.
        "-s",
        // bytes in hex format, e.g. 0xDEADBEEF.
        "-b",
        // Area id in integer.
        "-a"};

}  // namespace

void FakeVehicleHardware::storePropInitialValue(const defaultconfig::ConfigDeclaration& config) {
@@ -505,7 +523,7 @@ DumpResult FakeVehicleHardware::dump(const std::vector<std::string>& options) {
    } else if (EqualsIgnoreCase(option, "--get")) {
        result.buffer = dumpSpecificProperty(options);
    } else if (EqualsIgnoreCase(option, "--set")) {
        // TODO(b/214613918): Support debug set values.
        result.buffer = dumpSetProperties(options);
    } else {
        result.buffer = StringPrintf("Invalid option: %s\n", option.c_str());
    }
@@ -627,6 +645,148 @@ std::string FakeVehicleHardware::dumpSpecificProperty(const std::vector<std::str
    return msg;
}

std::vector<std::string> FakeVehicleHardware::getOptionValues(
        const std::vector<std::string>& options, size_t* index) {
    std::vector<std::string> values;
    while (*index < options.size()) {
        std::string option = options[*index];
        if (SET_PROP_OPTIONS.find(option) != SET_PROP_OPTIONS.end()) {
            return std::move(values);
        }
        values.push_back(option);
        (*index)++;
    }
    return std::move(values);
}

Result<VehiclePropValue> FakeVehicleHardware::parseSetPropOptions(
        const std::vector<std::string>& options) {
    // Options format:
    // --set PROP [-f f1 f2...] [-i i1 i2...] [-i64 i1 i2...] [-s s1 s2...] [-b b1 b2...] [-a a]
    size_t optionIndex = 1;
    auto result = safelyParseInt<int32_t>(optionIndex, options[optionIndex]);
    if (!result.ok()) {
        return Error() << StringPrintf("Property value: \"%s\" is not a valid int: %s\n",
                                       options[optionIndex].c_str(), getErrorMsg(result).c_str());
    }
    VehiclePropValue prop = {};
    prop.prop = result.value();
    prop.status = VehiclePropertyStatus::AVAILABLE;
    optionIndex++;
    std::unordered_set<std::string> parsedOptions;

    while (optionIndex < options.size()) {
        std::string type = options[optionIndex];
        optionIndex++;
        size_t currentIndex = optionIndex;
        std::vector<std::string> values = getOptionValues(options, &optionIndex);
        if (parsedOptions.find(type) != parsedOptions.end()) {
            return Error() << StringPrintf("Duplicate \"%s\" options\n", type.c_str());
        }
        parsedOptions.insert(type);
        if (EqualsIgnoreCase(type, "-i")) {
            if (values.size() == 0) {
                return Error() << "No values specified when using \"-i\"\n";
            }
            prop.value.int32Values.resize(values.size());
            for (size_t i = 0; i < values.size(); i++) {
                auto int32Result = safelyParseInt<int32_t>(currentIndex + i, values[i]);
                if (!int32Result.ok()) {
                    return Error()
                           << StringPrintf("Value: \"%s\" is not a valid int: %s\n",
                                           values[i].c_str(), getErrorMsg(int32Result).c_str());
                }
                prop.value.int32Values[i] = int32Result.value();
            }
        } else if (EqualsIgnoreCase(type, "-i64")) {
            if (values.size() == 0) {
                return Error() << "No values specified when using \"-i64\"\n";
            }
            prop.value.int64Values.resize(values.size());
            for (size_t i = 0; i < values.size(); i++) {
                auto int64Result = safelyParseInt<int64_t>(currentIndex + i, values[i]);
                if (!int64Result.ok()) {
                    return Error()
                           << StringPrintf("Value: \"%s\" is not a valid int64: %s\n",
                                           values[i].c_str(), getErrorMsg(int64Result).c_str());
                }
                prop.value.int64Values[i] = int64Result.value();
            }
        } else if (EqualsIgnoreCase(type, "-f")) {
            if (values.size() == 0) {
                return Error() << "No values specified when using \"-f\"\n";
            }
            prop.value.floatValues.resize(values.size());
            for (size_t i = 0; i < values.size(); i++) {
                auto floatResult = safelyParseFloat(currentIndex + i, values[i]);
                if (!floatResult.ok()) {
                    return Error()
                           << StringPrintf("Value: \"%s\" is not a valid float: %s\n",
                                           values[i].c_str(), getErrorMsg(floatResult).c_str());
                }
                prop.value.floatValues[i] = floatResult.value();
            }
        } else if (EqualsIgnoreCase(type, "-s")) {
            if (values.size() != 1) {
                return Error() << "Expect exact one value when using \"-s\"\n";
            }
            prop.value.stringValue = values[0];
        } else if (EqualsIgnoreCase(type, "-b")) {
            if (values.size() != 1) {
                return Error() << "Expect exact one value when using \"-b\"\n";
            }
            auto bytesResult = parseHexString(values[0]);
            if (!bytesResult.ok()) {
                return Error() << StringPrintf("value: \"%s\" is not a valid hex string: %s\n",
                                               values[0].c_str(), getErrorMsg(bytesResult).c_str());
            }
            prop.value.byteValues = std::move(bytesResult.value());
        } else if (EqualsIgnoreCase(type, "-a")) {
            if (values.size() != 1) {
                return Error() << "Expect exact one value when using \"-a\"\n";
            }
            auto int32Result = safelyParseInt<int32_t>(currentIndex, values[0]);
            if (!int32Result.ok()) {
                return Error() << StringPrintf("Area ID: \"%s\" is not a valid int: %s\n",
                                               values[0].c_str(), getErrorMsg(int32Result).c_str());
            }
            prop.areaId = int32Result.value();
        } else {
            return Error() << StringPrintf("Unknown option: %s\n", type.c_str());
        }
    }

    return prop;
}

std::string FakeVehicleHardware::dumpSetProperties(const std::vector<std::string>& options) {
    if (auto result = checkArgumentsSize(options, 3); !result.ok()) {
        return getErrorMsg(result);
    }

    auto parseResult = parseSetPropOptions(options);
    if (!parseResult.ok()) {
        return getErrorMsg(parseResult);
    }
    VehiclePropValue prop = std::move(parseResult.value());
    ALOGD("Dump: Setting property: %s", prop.toString().c_str());

    bool isSpecialValue = false;
    auto setResult = maybeSetSpecialValue(prop, &isSpecialValue);

    if (!isSpecialValue) {
        auto updatedValue = mValuePool->obtain(prop);
        updatedValue->timestamp = elapsedRealtimeNano();
        setResult = mServerSidePropStore->writeValue(std::move(updatedValue));
    }

    if (setResult.ok()) {
        return StringPrintf("Set property: %s\n", prop.toString().c_str());
    }
    return StringPrintf("failed to set property: %s, error: %s\n", prop.toString().c_str(),
                        getErrorMsg(setResult).c_str());
}

StatusCode FakeVehicleHardware::checkHealth() {
    // Always return OK for checkHealth.
    return StatusCode::OK;
@@ -689,6 +849,49 @@ void FakeVehicleHardware::overrideProperties(const char* overrideDir) {
    }
}

Result<float> FakeVehicleHardware::safelyParseFloat(int index, const std::string& s) {
    float out;
    if (!ParseFloat(s, &out)) {
        return Error() << StringPrintf("non-float argument at index %d: %s\n", index, s.c_str());
    }
    return out;
}

Result<std::vector<uint8_t>> FakeVehicleHardware::parseHexString(const std::string& s) {
    std::vector<uint8_t> bytes;
    if (s.size() % 2 != 0) {
        return Error() << StringPrintf("invalid hex string: %s, should have even size\n",
                                       s.c_str());
    }
    if (!StartsWith(s, "0x")) {
        return Error() << StringPrintf("hex string should start with \"0x\", got %s\n", s.c_str());
    }
    std::string subs = s.substr(2);
    std::transform(subs.begin(), subs.end(), subs.begin(),
                   [](unsigned char c) { return std::tolower(c); });

    bool highDigit = true;
    for (size_t i = 0; i < subs.size(); i++) {
        char c = subs[i];
        uint8_t v;
        if (c >= '0' && c <= '9') {
            v = c - '0';
        } else if (c >= 'a' && c <= 'f') {
            v = c - 'a' + 10;
        } else {
            return Error() << StringPrintf("invalid character %c in hex string %s\n", c,
                                           subs.c_str());
        }
        if (highDigit) {
            bytes.push_back(v * 16);
        } else {
            bytes[bytes.size() - 1] += v;
        }
        highDigit = !highDigit;
    }
    return bytes;
}

}  // namespace fake
}  // namespace vehicle
}  // namespace automotive
+180 −0
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ using ::testing::IsSubsetOf;
using ::testing::WhenSortedBy;

constexpr int INVALID_PROP_ID = 0;
constexpr char CAR_MAKE[] = "Default Car";

}  // namespace

@@ -1282,6 +1283,185 @@ TEST_F(FakeVehicleHardwareTest, testDumpInvalidOptions) {
    ASSERT_THAT(result.buffer, ContainsRegex("Invalid option: --invalid"));
}

struct SetPropTestCase {
    std::string test_name;
    std::vector<std::string> options;
    bool success;
    std::string errorMsg = "";
};

class FakeVehicleHardwareSetPropTest : public FakeVehicleHardwareTest,
                                       public testing::WithParamInterface<SetPropTestCase> {};

TEST_P(FakeVehicleHardwareSetPropTest, cmdSetOneProperty) {
    const SetPropTestCase& tc = GetParam();

    DumpResult result = getHardware()->dump(tc.options);
    ASSERT_FALSE(result.callerShouldDumpState);
    ASSERT_NE(result.buffer, "");
    if (tc.success) {
        ASSERT_THAT(result.buffer, ContainsRegex("Set property:"));
    } else {
        ASSERT_THAT(result.buffer, ContainsRegex(tc.errorMsg));
    }
}

std::vector<SetPropTestCase> GenSetPropParams() {
    std::string infoMakeProperty = std::to_string(toInt(VehicleProperty::INFO_MAKE));
    return {
            {"success_set_string", {"--set", infoMakeProperty, "-s", CAR_MAKE}, true},
            {"success_set_bytes", {"--set", infoMakeProperty, "-b", "0xdeadbeef"}, true},
            {"success_set_bytes_caps", {"--set", infoMakeProperty, "-b", "0xDEADBEEF"}, true},
            {"success_set_int", {"--set", infoMakeProperty, "-i", "2147483647"}, true},
            {"success_set_ints",
             {"--set", infoMakeProperty, "-i", "2147483647", "0", "-2147483648"},
             true},
            {"success_set_int64",
             {"--set", infoMakeProperty, "-i64", "-9223372036854775808"},
             true},
            {"success_set_int64s",
             {"--set", infoMakeProperty, "-i64", "-9223372036854775808", "0",
              "9223372036854775807"},
             true},
            {"success_set_float", {"--set", infoMakeProperty, "-f", "1.175494351E-38"}, true},
            {"success_set_floats",
             {"--set", infoMakeProperty, "-f", "-3.402823466E+38", "0", "3.402823466E+38"},
             true},
            {"success_set_area", {"--set", infoMakeProperty, "-a", "2147483647"}, true},
            {"fail_no_options", {"--set", infoMakeProperty}, false, "Invalid number of arguments"},
            {"fail_less_than_4_options",
             {"--set", infoMakeProperty, "-i"},
             false,
             "No values specified"},
            {"fail_unknown_options", {"--set", infoMakeProperty, "-abcd"}, false, "Unknown option"},
            {"fail_invalid_property",
             {"--set", "not valid", "-s", CAR_MAKE},
             false,
             "not a valid int"},
            {"fail_duplicate_string",
             {"--set", infoMakeProperty, "-s", CAR_MAKE, "-s", CAR_MAKE},
             false,
             "Duplicate \"-s\" options"},
            {"fail_multiple_strings",
             {"--set", infoMakeProperty, "-s", CAR_MAKE, CAR_MAKE},
             false,
             "Expect exact one value"},
            {"fail_no_string_value",
             {"--set", infoMakeProperty, "-s", "-a", "1234"},
             false,
             "Expect exact one value"},
            {"fail_duplicate_bytes",
             {"--set", infoMakeProperty, "-b", "0xdeadbeef", "-b", "0xdeadbeef"},
             false,
             "Duplicate \"-b\" options"},
            {"fail_multiple_bytes",
             {"--set", infoMakeProperty, "-b", "0xdeadbeef", "0xdeadbeef"},
             false,
             "Expect exact one value"},
            {"fail_invalid_bytes",
             {"--set", infoMakeProperty, "-b", "0xgood"},
             false,
             "not a valid hex string"},
            {"fail_invalid_bytes_no_prefix",
             {"--set", infoMakeProperty, "-b", "deadbeef"},
             false,
             "not a valid hex string"},
            {"fail_invalid_int",
             {"--set", infoMakeProperty, "-i", "abc"},
             false,
             "not a valid int"},
            {"fail_int_out_of_range",
             {"--set", infoMakeProperty, "-i", "2147483648"},
             false,
             "not a valid int"},
            {"fail_no_int_value",
             {"--set", infoMakeProperty, "-i", "-s", CAR_MAKE},
             false,
             "No values specified"},
            {"fail_invalid_int64",
             {"--set", infoMakeProperty, "-i64", "abc"},
             false,
             "not a valid int64"},
            {"fail_int64_out_of_range",
             {"--set", infoMakeProperty, "-i64", "-9223372036854775809"},
             false,
             "not a valid int64"},
            {"fail_no_int64_value",
             {"--set", infoMakeProperty, "-i64", "-s", CAR_MAKE},
             false,
             "No values specified"},
            {"fail_invalid_float",
             {"--set", infoMakeProperty, "-f", "abc"},
             false,
             "not a valid float"},
            {"fail_float_out_of_range",
             {"--set", infoMakeProperty, "-f", "-3.402823466E+39"},
             false,
             "not a valid float"},
            {"fail_no_float_value",
             {"--set", infoMakeProperty, "-f", "-s", CAR_MAKE},
             false,
             "No values specified"},
            {"fail_multiple_areas",
             {"--set", infoMakeProperty, "-a", "2147483648", "0"},
             false,
             "Expect exact one value"},
            {"fail_invalid_area",
             {"--set", infoMakeProperty, "-a", "abc"},
             false,
             "not a valid int"},
            {"fail_area_out_of_range",
             {"--set", infoMakeProperty, "-a", "2147483648"},
             false,
             "not a valid int"},
            {"fail_no_area_value",
             {"--set", infoMakeProperty, "-a", "-s", CAR_MAKE},
             false,
             "Expect exact one value"},
    };
}

INSTANTIATE_TEST_SUITE_P(
        FakeVehicleHardwareSetPropTests, FakeVehicleHardwareSetPropTest,
        testing::ValuesIn(GenSetPropParams()),
        [](const testing::TestParamInfo<FakeVehicleHardwareSetPropTest::ParamType>& info) {
            return info.param.test_name;
        });

TEST_F(FakeVehicleHardwareTest, SetComplexPropTest) {
    std::string infoMakeProperty = std::to_string(toInt(VehicleProperty::INFO_MAKE));
    getHardware()->dump({"--set", infoMakeProperty,      "-s",   CAR_MAKE,
                         "-b",    "0xdeadbeef",          "-i",   "2147483647",
                         "0",     "-2147483648",         "-i64", "-9223372036854775808",
                         "0",     "9223372036854775807", "-f",   "-3.402823466E+38",
                         "0",     "3.402823466E+38",     "-a",   "123"});
    VehiclePropValue requestProp;
    requestProp.prop = toInt(VehicleProperty::INFO_MAKE);
    requestProp.areaId = 123;
    auto result = getValue(requestProp);
    ASSERT_TRUE(result.ok());
    VehiclePropValue value = result.value();
    ASSERT_EQ(value.prop, toInt(VehicleProperty::INFO_MAKE));
    ASSERT_EQ(value.areaId, 123);
    ASSERT_STREQ(CAR_MAKE, value.value.stringValue.c_str());
    uint8_t bytes[] = {0xde, 0xad, 0xbe, 0xef};
    ASSERT_FALSE(memcmp(bytes, value.value.byteValues.data(), sizeof(bytes)));
    ASSERT_EQ(3u, value.value.int32Values.size());
    ASSERT_EQ(2147483647, value.value.int32Values[0]);
    ASSERT_EQ(0, value.value.int32Values[1]);
    ASSERT_EQ(-2147483648, value.value.int32Values[2]);
    ASSERT_EQ(3u, value.value.int64Values.size());
    // -9223372036854775808 is not a valid literal since '-' and '9223372036854775808' would be two
    // tokens and the later does not fit in unsigned long long.
    ASSERT_EQ(-9223372036854775807 - 1, value.value.int64Values[0]);
    ASSERT_EQ(0, value.value.int64Values[1]);
    ASSERT_EQ(9223372036854775807, value.value.int64Values[2]);
    ASSERT_EQ(3u, value.value.floatValues.size());
    ASSERT_EQ(-3.402823466E+38f, value.value.floatValues[0]);
    ASSERT_EQ(0.0f, value.value.floatValues[1]);
    ASSERT_EQ(3.402823466E+38f, value.value.floatValues[2]);
}

}  // namespace fake
}  // namespace vehicle
}  // namespace automotive