Support set property in dump.
Support debug interface to set property value in FakeVehicleHardware.
Test: atest FakeVehicleHardwareTest.
Bug: 214613918
Change-Id: I64ef532274e20db1444e8583d71e0c0955c5b460
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
index e701078..3c38025 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
@@ -131,6 +131,7 @@
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 @@
}
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);
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
index 1f8e436..9660793 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -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 ::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 @@
} 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 @@
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 @@
}
}
+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
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
index f821dfe..0812c2a 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
@@ -62,6 +62,7 @@
using ::testing::WhenSortedBy;
constexpr int INVALID_PROP_ID = 0;
+constexpr char CAR_MAKE[] = "Default Car";
} // namespace
@@ -1282,6 +1283,185 @@
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