Merge "Add vendor override property to fake VHAL hardware."

This commit is contained in:
TreeHugger Robot
2021-10-27 17:48:39 +00:00
committed by Android (Google) Code Review
9 changed files with 268 additions and 13 deletions

View File

@@ -30,6 +30,12 @@ cc_library {
"VehicleHalDefaultConfig",
],
export_header_lib_headers: ["IVehicleHardware"],
static_libs: ["VehicleHalUtils"],
static_libs: [
"VehicleHalUtils",
"FakeVehicleHalValueGenerators",
],
shared_libs: [
"libjsoncpp",
],
export_static_lib_headers: ["VehicleHalUtils"],
}

View File

@@ -82,10 +82,8 @@ class FakeVehicleHardware final : public IVehicleHardware {
void registerOnPropertySetErrorEvent(OnPropertySetErrorCallback&& callback) override;
private:
void storePropInitialValue(const defaultconfig::ConfigDeclaration& config);
void init(std::shared_ptr<VehiclePropValuePool> valuePool);
void onValueChangeCallback(
const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value);
// Expose private methods to unit test.
friend class FakeVehicleHardwareTestHelper;
std::unique_ptr<VehiclePropertyStore> mServerSidePropStore;
// mValuePool is also used in mServerSidePropStore.
@@ -93,6 +91,18 @@ class FakeVehicleHardware final : public IVehicleHardware {
std::mutex mCallbackLock;
OnPropertyChangeCallback mOnPropertyChangeCallback GUARDED_BY(mCallbackLock);
OnPropertySetErrorCallback mOnPropertySetErrorCallback GUARDED_BY(mCallbackLock);
void init(std::shared_ptr<VehiclePropValuePool> valuePool);
// Stores the initial value to property store.
void storePropInitialValue(const defaultconfig::ConfigDeclaration& config);
// The callback that would be called when a vehicle property value change happens.
void onValueChangeCallback(
const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value);
// If property "persist.vendor.vhal_init_value_override" is set to true, override the properties
// using config files in 'overrideDir'.
void maybeOverrideProperties(const char* overrideDir);
// Override the properties using config files in 'overrideDir'.
void overrideProperties(const char* overrideDir);
};
} // namespace fake

View File

@@ -17,11 +17,17 @@
#include "FakeVehicleHardware.h"
#include <DefaultConfig.h>
#include <JsonFakeValueGenerator.h>
#include <VehicleHalTypes.h>
#include <VehicleUtils.h>
#include <android-base/properties.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
#include <dirent.h>
#include <sys/types.h>
#include <fstream>
#include <regex>
#include <vector>
namespace android {
@@ -30,6 +36,8 @@ namespace automotive {
namespace vehicle {
namespace fake {
namespace {
using ::aidl::android::hardware::automotive::vehicle::GetValueRequest;
using ::aidl::android::hardware::automotive::vehicle::GetValueResult;
using ::aidl::android::hardware::automotive::vehicle::RawPropValues;
@@ -40,6 +48,11 @@ using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
const char* VENDOR_OVERRIDE_DIR = "/vendor/etc/automotive/vhaloverride/";
const char* OVERRIDE_PROPERTY = "persist.vendor.vhal_init_value_override";
} // namespace
void FakeVehicleHardware::storePropInitialValue(const defaultconfig::ConfigDeclaration& config) {
const VehiclePropConfig& vehiclePropConfig = config.config;
int propId = vehiclePropConfig.prop;
@@ -99,6 +112,8 @@ void FakeVehicleHardware::init(std::shared_ptr<VehiclePropValuePool> valuePool)
storePropInitialValue(it);
}
maybeOverrideProperties(VENDOR_OVERRIDE_DIR);
mServerSidePropStore->setOnValueChangeCallback(
[this](const VehiclePropValue& value) { return onValueChangeCallback(value); });
}
@@ -201,6 +216,39 @@ void FakeVehicleHardware::onValueChangeCallback(const VehiclePropValue& value) {
}
}
void FakeVehicleHardware::maybeOverrideProperties(const char* overrideDir) {
if (android::base::GetBoolProperty(OVERRIDE_PROPERTY, false)) {
overrideProperties(overrideDir);
}
}
void FakeVehicleHardware::overrideProperties(const char* overrideDir) {
ALOGI("loading vendor override properties from %s", overrideDir);
if (auto dir = opendir(overrideDir); dir != NULL) {
std::regex regJson(".*[.]json", std::regex::icase);
while (auto f = readdir(dir)) {
if (!std::regex_match(f->d_name, regJson)) {
continue;
}
std::string file = overrideDir + std::string(f->d_name);
JsonFakeValueGenerator tmpGenerator(file);
std::vector<VehiclePropValue> propValues = tmpGenerator.getAllEvents();
for (const VehiclePropValue& prop : propValues) {
auto propToStore = mValuePool->obtain(prop);
propToStore->timestamp = elapsedRealtimeNano();
if (auto result = mServerSidePropStore->writeValue(std::move(propToStore),
/*updateStatus=*/true);
!result.ok()) {
ALOGW("failed to write vendor override properties: %d, error: %s", prop.prop,
result.error().message().c_str());
}
}
}
closedir(dir);
}
}
} // namespace fake
} // namespace vehicle
} // namespace automotive

View File

@@ -29,9 +29,21 @@ cc_test {
static_libs: [
"VehicleHalUtils",
"FakeVehicleHardware",
"FakeVehicleHalValueGenerators",
"libgtest",
"libgmock",
],
shared_libs: [
"libjsoncpp",
],
data: [
":FakeVehicleHardwareTestOverrideJson",
],
defaults: ["VehicleHalDefaults"],
test_suites: ["device-tests"],
}
filegroup {
name: "FakeVehicleHardwareTestOverrideJson",
srcs: ["override/*"],
}

View File

@@ -16,16 +16,21 @@
#include <DefaultConfig.h>
#include <FakeVehicleHardware.h>
#include <android-base/expected.h>
#include <android-base/file.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
#include <inttypes.h>
namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {
namespace fake {
namespace {
using ::aidl::android::hardware::automotive::vehicle::GetValueRequest;
@@ -38,6 +43,8 @@ using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
using ::android::base::expected;
using ::android::base::unexpected;
using ::testing::ContainerEq;
using ::testing::Eq;
using ::testing::WhenSortedBy;
@@ -46,6 +53,17 @@ constexpr int INVALID_PROP_ID = 0;
} // namespace
// A helper class to access private methods for FakeVehicleHardware.
class FakeVehicleHardwareTestHelper {
public:
FakeVehicleHardwareTestHelper(FakeVehicleHardware* hardware) { mHardware = hardware; }
void overrideProperties(const char* overrideDir) { mHardware->overrideProperties(overrideDir); }
private:
FakeVehicleHardware* mHardware;
};
class FakeVehicleHardwareTest : public ::testing::Test {
protected:
void SetUp() override {}
@@ -64,6 +82,63 @@ class FakeVehicleHardwareTest : public ::testing::Test {
requests);
}
StatusCode setValue(const VehiclePropValue& value) {
std::vector<SetValueRequest> requests = {
SetValueRequest{
.requestId = 0,
.value = value,
},
};
if (StatusCode status = setValues(requests); status != StatusCode::OK) {
return status;
}
const SetValueResult& result = getSetValueResults().back();
if (result.requestId != 0) {
ALOGE("request ID mismatch, got %" PRId64 ", expect 0", result.requestId);
return StatusCode::INTERNAL_ERROR;
}
return result.status;
}
expected<VehiclePropValue, StatusCode> getValue(const VehiclePropValue& value) {
std::vector<GetValueRequest> requests = {
GetValueRequest{
.requestId = 0,
.prop = value,
},
};
if (StatusCode status = getValues(requests); status != StatusCode::OK) {
return unexpected(status);
}
const GetValueResult& result = getGetValueResults().back();
if (result.requestId != 0) {
ALOGE("request ID mismatch, got %" PRId64 ", expect 0", result.requestId);
return unexpected(StatusCode::INTERNAL_ERROR);
}
if (result.status != StatusCode::OK) {
return unexpected(result.status);
}
if (!result.prop.has_value()) {
ALOGE("%s", "result property is empty");
return unexpected(StatusCode::INTERNAL_ERROR);
}
return result.prop.value();
}
template <class T>
int getStatus(expected<T, StatusCode> result) {
return toInt(result.error());
}
void onSetValues(const std::vector<SetValueResult> results) {
for (auto& result : results) {
mSetValueResults.push_back(result);
@@ -424,6 +499,86 @@ TEST_F(FakeVehicleHardwareTest, testSetStatusMustIgnore) {
ASSERT_EQ(getGetValueResults()[1].prop->status, VehiclePropertyStatus::AVAILABLE);
}
TEST_F(FakeVehicleHardwareTest, testVendorOverrideProperties) {
std::string overrideDir = android::base::GetExecutableDirectory() + "/override/";
// Set vendor override directory.
FakeVehicleHardwareTestHelper helper(getHardware());
helper.overrideProperties(overrideDir.c_str());
// This is the same as the prop in 'gear_selection.json'.
int gearProp = toInt(VehicleProperty::GEAR_SELECTION);
auto result = getValue(VehiclePropValue{
.prop = gearProp,
});
ASSERT_TRUE(result.ok()) << "expect to get the overridden property ok: " << getStatus(result);
ASSERT_EQ(static_cast<size_t>(1), result.value().value.int32Values.size());
ASSERT_EQ(8, result.value().value.int32Values[0]);
// If we set the value, it should update despite the override.
ASSERT_EQ(setValue(VehiclePropValue{
.prop = gearProp,
.value =
{
.int32Values = {5},
},
.timestamp = elapsedRealtimeNano(),
}),
StatusCode::OK)
<< "expect to set the overridden property ok";
result = getValue(VehiclePropValue{
.prop = gearProp,
});
ASSERT_TRUE(result.ok()) << "expect to get the overridden property after setting value ok";
ASSERT_EQ(static_cast<size_t>(1), result.value().value.int32Values.size());
ASSERT_EQ(5, result.value().value.int32Values[0]);
}
TEST_F(FakeVehicleHardwareTest, testVendorOverridePropertiesMultipleAreas) {
std::string overrideDir = android::base::GetExecutableDirectory() + "/override/";
// Set vendor override directory.
FakeVehicleHardwareTestHelper helper(getHardware());
helper.overrideProperties(overrideDir.c_str());
// This is the same as the prop in 'hvac_temperature_set.json'.
int hvacProp = toInt(VehicleProperty::HVAC_TEMPERATURE_SET);
auto result = getValue(VehiclePropValue{
.prop = hvacProp,
.areaId = HVAC_LEFT,
});
ASSERT_TRUE(result.ok()) << "expect to get the overridden property ok: " << getStatus(result);
ASSERT_EQ(static_cast<size_t>(1), result.value().value.floatValues.size());
ASSERT_EQ(30.0f, result.value().value.floatValues[0]);
// HVAC_RIGHT should not be affected and return the default value.
result = getValue(VehiclePropValue{
.prop = hvacProp,
.areaId = HVAC_RIGHT,
});
ASSERT_TRUE(result.ok()) << "expect to get the default property ok: " << getStatus(result);
ASSERT_EQ(static_cast<size_t>(1), result.value().value.floatValues.size());
ASSERT_EQ(20.0f, result.value().value.floatValues[0]);
}
TEST_F(FakeVehicleHardwareTest, testVendorOverridePropertiesDirDoesNotExist) {
// Set vendor override directory to a non-existing dir
FakeVehicleHardwareTestHelper helper(getHardware());
helper.overrideProperties("123");
auto result = getValue(VehiclePropValue{
.prop = toInt(VehicleProperty::GEAR_SELECTION),
});
ASSERT_TRUE(result.ok()) << "expect to get the default property ok: " << getStatus(result);
ASSERT_EQ(static_cast<size_t>(1), result.value().value.int32Values.size());
ASSERT_EQ(4, result.value().value.int32Values[0]);
}
} // namespace fake
} // namespace vehicle
} // namespace automotive

View File

@@ -0,0 +1,9 @@
[
{
"timestamp": 1000000,
"areaId": 0,
"value": 8,
// GEAR_SELECTION
"prop": 289408000
}
]

View File

@@ -0,0 +1,10 @@
[
{
"timestamp": 1000000,
// HVAC_LEFT
"areaId": 49,
"value": 30,
// HVAC_TEMPERATURE_SET
"prop": 358614275
}
]

View File

@@ -130,10 +130,9 @@ inline size_t getVehicleRawValueVectorSize(
const ::aidl::android::hardware::automotive::vehicle::RawPropValues& value,
::aidl::android::hardware::automotive::vehicle::VehiclePropertyType type) {
switch (type) {
case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::INT32: // fall
// through
case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::
BOOLEAN: // fall through
case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::INT32:
[[fallthrough]];
case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::BOOLEAN:
return std::min(value.int32Values.size(), static_cast<size_t>(1));
case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::FLOAT:
return std::min(value.floatValues.size(), static_cast<size_t>(1));
@@ -147,6 +146,10 @@ inline size_t getVehicleRawValueVectorSize(
return value.int64Values.size();
case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::BYTES:
return value.byteValues.size();
case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::STRING:
[[fallthrough]];
case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::MIXED:
return 0;
default:
ALOGE("getVehicleRawValueVectorSize: unknown type: %d", toInt(type));
return 0;

View File

@@ -52,17 +52,19 @@ VehiclePropValuePool::RecyclableType VehiclePropValuePool::obtain(VehiclePropert
}
VehiclePropValuePool::RecyclableType VehiclePropValuePool::obtain(const VehiclePropValue& src) {
VehiclePropertyType type = getPropType(src.prop);
int propId = src.prop;
VehiclePropertyType type = getPropType(propId);
size_t vectorSize = getVehicleRawValueVectorSize(src.value, type);
if (vectorSize == 0 && !isComplexType(type)) {
ALOGW("empty vehicle prop value, contains no content");
ALOGW("empty vehicle prop value, contains no content, prop: %d", propId);
// Return any empty VehiclePropValue.
return RecyclableType{new VehiclePropValue, mDisposableDeleter};
return RecyclableType{new VehiclePropValue{}, mDisposableDeleter};
}
auto dest = obtain(type, vectorSize);
dest->prop = src.prop;
dest->prop = propId;
dest->areaId = src.areaId;
dest->status = src.status;
dest->timestamp = src.timestamp;