Merge "Introduce getPropertyConfig to IVehicleHardware" into main

This commit is contained in:
Treehugger Robot
2024-07-20 02:26:42 +00:00
committed by Android (Google) Code Review
8 changed files with 195 additions and 43 deletions

View File

@@ -83,6 +83,17 @@ std::vector<aidlvhal::VehiclePropConfig> GRPCVehicleHardware::getAllPropertyConf
return configs;
}
std::optional<aidlvhal::VehiclePropConfig> GRPCVehicleHardware::getPropertyConfig(
int32_t propId) const {
// TODO(b/354055835): Use GRPC call to get one config instead of getting all the configs.
for (const auto& config : getAllPropertyConfigs()) {
if (config.prop == propId) {
return config;
}
}
return std::nullopt;
}
aidlvhal::StatusCode GRPCVehicleHardware::setValues(
std::shared_ptr<const SetValuesCallback> callback,
const std::vector<aidlvhal::SetValueRequest>& requests) {

View File

@@ -50,6 +50,10 @@ class GRPCVehicleHardware : public IVehicleHardware {
// Get all the property configs.
std::vector<aidlvhal::VehiclePropConfig> getAllPropertyConfigs() const override;
// Get the config for the specified propId.
std::optional<aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
getPropertyConfig(int32_t propId) const override;
// Set property values asynchronously. Server could return before the property set requests
// are sent to vehicle bus or before property set confirmation is received. The callback is
// safe to be called after the function returns and is safe to be called in a different thread.

View File

@@ -20,6 +20,7 @@
#include <VehicleHalTypes.h>
#include <memory>
#include <optional>
#include <vector>
namespace android {
@@ -46,33 +47,53 @@ struct SetValueErrorEvent {
int32_t areaId;
};
namespace aidlvhal = ::aidl::android::hardware::automotive::vehicle;
// An abstract interface to access vehicle hardware.
// For virtualized VHAL, GrpcVehicleHardware would communicate with a VehicleHardware
// implementation in another VM through GRPC. For non-virtualzied VHAL, VHAL directly communicates
// with a VehicleHardware through this interface.
class IVehicleHardware {
public:
using SetValuesCallback = std::function<void(
std::vector<aidl::android::hardware::automotive::vehicle::SetValueResult>)>;
using GetValuesCallback = std::function<void(
std::vector<aidl::android::hardware::automotive::vehicle::GetValueResult>)>;
using PropertyChangeCallback = std::function<void(
std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue>)>;
using SetValuesCallback = std::function<void(std::vector<aidlvhal::SetValueResult>)>;
using GetValuesCallback = std::function<void(std::vector<aidlvhal::GetValueResult>)>;
using PropertyChangeCallback = std::function<void(std::vector<aidlvhal::VehiclePropValue>)>;
using PropertySetErrorCallback = std::function<void(std::vector<SetValueErrorEvent>)>;
virtual ~IVehicleHardware() = default;
// Get all the property configs.
virtual std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
getAllPropertyConfigs() const = 0;
virtual std::vector<aidlvhal::VehiclePropConfig> getAllPropertyConfigs() const = 0;
// Get the property configs for the specified propId. This is used for early-boot
// native VHAL clients to access certain property configs when not all property configs are
// available. For example, a config discovery process might be required to determine the
// property config for HVAC. However, for early boot properties, e.g. VHAL_HEARTBEAT, it
// could return before the config discovery process.
//
// Currently Android system may try to access the following properties during early boot:
// STORAGE_ENCRYPTION_BINDING_SEED, WATCHDOG_ALIVE, WATCHDOG_TERMINATE_PROCESS, VHAL_HEARTBEAT,
// CURRENT_POWER_POLICY, POWER_POLICY_REQ, POWER_POLICY_GROUP_REQ. They should return
// quickly otherwise the whole bootup process might be blocked.
virtual std::optional<aidlvhal::VehiclePropConfig> getPropertyConfig(int32_t propId) const {
// The default implementation is to use getAllPropertyConfigs(). This should be
// overridden if getAllPropertyConfigs() takes a while to return for initial boot or
// relies on ethernet or other communication channel that is not available during early
// boot.
for (const auto& config : getAllPropertyConfigs()) {
if (config.prop == propId) {
return config;
}
}
return std::nullopt;
}
// Set property values asynchronously. Server could return before the property set requests
// are sent to vehicle bus or before property set confirmation is received. The callback is
// safe to be called after the function returns and is safe to be called in a different thread.
virtual aidl::android::hardware::automotive::vehicle::StatusCode setValues(
virtual aidlvhal::StatusCode setValues(
std::shared_ptr<const SetValuesCallback> callback,
const std::vector<aidl::android::hardware::automotive::vehicle::SetValueRequest>&
requests) = 0;
const std::vector<aidlvhal::SetValueRequest>& requests) = 0;
// Get property values asynchronously. Server could return before the property values are ready.
// The callback is safe to be called after the function returns and is safe to be called in a
@@ -86,7 +107,7 @@ class IVehicleHardware {
virtual DumpResult dump(const std::vector<std::string>& options) = 0;
// Check whether the system is healthy, return {@code StatusCode::OK} for healthy.
virtual aidl::android::hardware::automotive::vehicle::StatusCode checkHealth() = 0;
virtual aidlvhal::StatusCode checkHealth() = 0;
// Register a callback that would be called when there is a property change event from vehicle.
// This function must only be called once during initialization.
@@ -179,16 +200,14 @@ class IVehicleHardware {
// 5. The second subscriber is removed, 'unsubscribe' is called.
// The impl can optionally disable the polling for vehicle speed.
//
virtual aidl::android::hardware::automotive::vehicle::StatusCode subscribe(
[[maybe_unused]] aidl::android::hardware::automotive::vehicle::SubscribeOptions
options) {
return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
virtual aidlvhal::StatusCode subscribe([[maybe_unused]] aidlvhal::SubscribeOptions options) {
return aidlvhal::StatusCode::OK;
}
// A [propId, areaId] is unsubscribed. This applies for both continuous or on-change property.
virtual aidl::android::hardware::automotive::vehicle::StatusCode unsubscribe(
[[maybe_unused]] int32_t propId, [[maybe_unused]] int32_t areaId) {
return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
virtual aidlvhal::StatusCode unsubscribe([[maybe_unused]] int32_t propId,
[[maybe_unused]] int32_t areaId) {
return aidlvhal::StatusCode::OK;
}
// This function is deprecated, subscribe/unsubscribe should be used instead.
@@ -216,10 +235,10 @@ class IVehicleHardware {
//
// If the impl is always polling at {@code maxSampleRate} as specified in config, then this
// function can be a no-op.
virtual aidl::android::hardware::automotive::vehicle::StatusCode updateSampleRate(
[[maybe_unused]] int32_t propId, [[maybe_unused]] int32_t areaId,
[[maybe_unused]] float sampleRate) {
return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
virtual aidlvhal::StatusCode updateSampleRate([[maybe_unused]] int32_t propId,
[[maybe_unused]] int32_t areaId,
[[maybe_unused]] float sampleRate) {
return aidlvhal::StatusCode::OK;
}
};

View File

@@ -199,6 +199,8 @@ class DefaultVehicleHal final : public aidlvhal::BnVehicle {
bool checkDumpPermission();
bool isConfigSupportedForCurrentVhalVersion(const aidlvhal::VehiclePropConfig& config) const;
bool getAllPropConfigsFromHardwareLocked() const EXCLUDES(mConfigLock);
// The looping handler function to process all onBinderDied or onBinderUnlinked events in

View File

@@ -340,32 +340,37 @@ int32_t DefaultVehicleHal::getVhalInterfaceVersion() const {
return myVersion;
}
bool DefaultVehicleHal::isConfigSupportedForCurrentVhalVersion(
const VehiclePropConfig& config) const {
int32_t myVersion = getVhalInterfaceVersion();
if (!isSystemProp(config.prop)) {
return true;
}
VehicleProperty property = static_cast<VehicleProperty>(config.prop);
std::string propertyName = aidl::android::hardware::automotive::vehicle::toString(property);
auto it = VersionForVehicleProperty.find(property);
if (it == VersionForVehicleProperty.end()) {
ALOGE("The property: %s is not a supported system property, ignore", propertyName.c_str());
return false;
}
int requiredVersion = it->second;
if (myVersion < requiredVersion) {
ALOGE("The property: %s is not supported for current client VHAL version, "
"require %d, current version: %d, ignore",
propertyName.c_str(), requiredVersion, myVersion);
return false;
}
return true;
}
bool DefaultVehicleHal::getAllPropConfigsFromHardwareLocked() const {
ALOGD("Get all property configs from hardware");
auto configs = mVehicleHardware->getAllPropertyConfigs();
std::vector<VehiclePropConfig> filteredConfigs;
int32_t myVersion = getVhalInterfaceVersion();
for (auto& config : configs) {
if (!isSystemProp(config.prop)) {
for (const auto& config : configs) {
if (isConfigSupportedForCurrentVhalVersion(config)) {
filteredConfigs.push_back(std::move(config));
continue;
}
VehicleProperty property = static_cast<VehicleProperty>(config.prop);
std::string propertyName = aidl::android::hardware::automotive::vehicle::toString(property);
auto it = VersionForVehicleProperty.find(property);
if (it == VersionForVehicleProperty.end()) {
ALOGE("The property: %s is not a supported system property, ignore",
propertyName.c_str());
continue;
}
int requiredVersion = it->second;
if (myVersion < requiredVersion) {
ALOGE("The property: %s is not supported for current client VHAL version, "
"require %d, current version: %d, ignore",
propertyName.c_str(), requiredVersion, myVersion);
continue;
}
filteredConfigs.push_back(std::move(config));
}
{
@@ -431,6 +436,19 @@ ScopedAStatus DefaultVehicleHal::getAllPropConfigs(VehiclePropConfigs* output) {
Result<VehiclePropConfig> DefaultVehicleHal::getConfig(int32_t propId) const {
Result<VehiclePropConfig> result;
if (!mConfigInit) {
std::optional<VehiclePropConfig> config = mVehicleHardware->getPropertyConfig(propId);
if (!config.has_value()) {
return Error() << "no config for property, ID: " << propId;
}
if (!isConfigSupportedForCurrentVhalVersion(config.value())) {
return Error() << "property not supported for current VHAL interface, ID: " << propId;
}
return config.value();
}
getConfigsByPropId([this, &result, propId](const auto& configsByPropId) {
SharedScopedLockAssertion lockAssertion(mConfigLock);
@@ -685,6 +703,22 @@ ScopedAStatus DefaultVehicleHal::setValues(const CallbackType& callback,
ScopedAStatus DefaultVehicleHal::getPropConfigs(const std::vector<int32_t>& props,
VehiclePropConfigs* output) {
std::vector<VehiclePropConfig> configs;
if (!mConfigInit) {
for (int32_t prop : props) {
auto maybeConfig = mVehicleHardware->getPropertyConfig(prop);
if (!maybeConfig.has_value() ||
!isConfigSupportedForCurrentVhalVersion(maybeConfig.value())) {
return ScopedAStatus::fromServiceSpecificErrorWithMessage(
toInt(StatusCode::INVALID_ARG),
StringPrintf("no config for property, ID: %" PRId32, prop).c_str());
}
configs.push_back(maybeConfig.value());
}
return vectorToStableLargeParcelable(std::move(configs), output);
}
ScopedAStatus status = ScopedAStatus::ok();
getConfigsByPropId([this, &configs, &status, &props](const auto& configsByPropId) {
SharedScopedLockAssertion lockAssertion(mConfigLock);

View File

@@ -650,6 +650,8 @@ TEST_F(DefaultVehicleHalTest, testGetPropConfigs) {
auto hardware = std::make_unique<MockVehicleHardware>();
hardware->setPropertyConfigs(testConfigs);
// Store the pointer for testing. We are sure it is valid.
MockVehicleHardware* hardwarePtr = hardware.get();
auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
@@ -658,6 +660,7 @@ TEST_F(DefaultVehicleHalTest, testGetPropConfigs) {
ASSERT_TRUE(status.isOk()) << "getPropConfigs failed: " << status.getMessage();
ASSERT_EQ(output.payloads, testConfigs);
ASSERT_FALSE(hardwarePtr->getAllPropertyConfigsCalled());
}
TEST_F(DefaultVehicleHalTest, testGetPropConfigsInvalidArg) {
@@ -704,6 +707,34 @@ TEST_F(DefaultVehicleHalTest, testGetValuesSmall) {
ASSERT_TRUE(maybeGetValueResults.has_value()) << "no results in callback";
EXPECT_EQ(maybeGetValueResults.value().payloads, expectedResults) << "results mismatch";
EXPECT_EQ(countClients(), static_cast<size_t>(1));
ASSERT_FALSE(getHardware()->getAllPropertyConfigsCalled());
}
TEST_F(DefaultVehicleHalTest, testGetValuesSmall_AfterGetAllPropConfigs) {
GetValueRequests requests;
std::vector<GetValueResult> expectedResults;
std::vector<GetValueRequest> expectedHardwareRequests;
// If we already called getAllPropConfigs, the configs will be cached.
VehiclePropConfigs output;
getClient()->getAllPropConfigs(&output);
ASSERT_TRUE(getValuesTestCases(10, requests, expectedResults, expectedHardwareRequests).ok());
getHardware()->addGetValueResponses(expectedResults);
auto status = getClient()->getValues(getCallbackClient(), requests);
ASSERT_TRUE(status.isOk()) << "getValues failed: " << status.getMessage();
EXPECT_EQ(getHardware()->nextGetValueRequests(), expectedHardwareRequests)
<< "requests to hardware mismatch";
auto maybeGetValueResults = getCallback()->nextGetValueResults();
ASSERT_TRUE(maybeGetValueResults.has_value()) << "no results in callback";
EXPECT_EQ(maybeGetValueResults.value().payloads, expectedResults) << "results mismatch";
EXPECT_EQ(countClients(), static_cast<size_t>(1));
ASSERT_TRUE(getHardware()->getAllPropertyConfigsCalled());
}
TEST_F(DefaultVehicleHalTest, testGetValuesLarge) {
@@ -1016,6 +1047,34 @@ TEST_F(DefaultVehicleHalTest, testSetValuesSmall) {
ASSERT_TRUE(maybeSetValueResults.has_value()) << "no results in callback";
ASSERT_EQ(maybeSetValueResults.value().payloads, expectedResults) << "results mismatch";
EXPECT_EQ(countClients(), static_cast<size_t>(1));
ASSERT_FALSE(getHardware()->getAllPropertyConfigsCalled());
}
TEST_F(DefaultVehicleHalTest, testSetValuesSmall_AfterGetAllPropConfigs) {
SetValueRequests requests;
std::vector<SetValueResult> expectedResults;
std::vector<SetValueRequest> expectedHardwareRequests;
// If we already called getAllPropConfigs, the configs will be cached.
VehiclePropConfigs output;
getClient()->getAllPropConfigs(&output);
ASSERT_TRUE(setValuesTestCases(10, requests, expectedResults, expectedHardwareRequests).ok());
getHardware()->addSetValueResponses(expectedResults);
auto status = getClient()->setValues(getCallbackClient(), requests);
ASSERT_TRUE(status.isOk()) << "setValues failed: " << status.getMessage();
EXPECT_EQ(getHardware()->nextSetValueRequests(), expectedHardwareRequests)
<< "requests to hardware mismatch";
auto maybeSetValueResults = getCallback()->nextSetValueResults();
ASSERT_TRUE(maybeSetValueResults.has_value()) << "no results in callback";
ASSERT_EQ(maybeSetValueResults.value().payloads, expectedResults) << "results mismatch";
EXPECT_EQ(countClients(), static_cast<size_t>(1));
ASSERT_TRUE(getHardware()->getAllPropertyConfigsCalled());
}
TEST_F(DefaultVehicleHalTest, testSetValuesLarge) {

View File

@@ -45,9 +45,20 @@ MockVehicleHardware::~MockVehicleHardware() {
std::vector<VehiclePropConfig> MockVehicleHardware::getAllPropertyConfigs() const {
std::scoped_lock<std::mutex> lockGuard(mLock);
mGetAllPropertyConfigsCalled = true;
return mPropertyConfigs;
}
std::optional<VehiclePropConfig> MockVehicleHardware::getPropertyConfig(int32_t propId) const {
std::scoped_lock<std::mutex> lockGuard(mLock);
for (const auto& config : mPropertyConfigs) {
if (config.prop == propId) {
return config;
}
}
return std::nullopt;
}
StatusCode MockVehicleHardware::setValues(std::shared_ptr<const SetValuesCallback> callback,
const std::vector<SetValueRequest>& requests) {
std::scoped_lock<std::mutex> lockGuard(mLock);
@@ -336,6 +347,11 @@ void MockVehicleHardware::sendOnPropertySetErrorEvent(
(*mPropertySetErrorCallback)(errorEvents);
}
bool MockVehicleHardware::getAllPropertyConfigsCalled() {
std::scoped_lock<std::mutex> lockGuard(mLock);
return mGetAllPropertyConfigsCalled;
}
} // namespace vehicle
} // namespace automotive
} // namespace hardware

View File

@@ -47,6 +47,8 @@ class MockVehicleHardware final : public IVehicleHardware {
std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
getAllPropertyConfigs() const override;
std::optional<aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
getPropertyConfig(int32_t propId) const override;
aidl::android::hardware::automotive::vehicle::StatusCode setValues(
std::shared_ptr<const SetValuesCallback> callback,
const std::vector<aidl::android::hardware::automotive::vehicle::SetValueRequest>&
@@ -98,6 +100,9 @@ class MockVehicleHardware final : public IVehicleHardware {
std::vector<aidl::android::hardware::automotive::vehicle::SubscribeOptions>
getSubscribeOptions();
void clearSubscribeOptions();
// Whether getAllPropertyConfigs() has been called, which blocks all all property configs
// being ready.
bool getAllPropertyConfigsCalled();
private:
mutable std::mutex mLock;
@@ -143,6 +148,8 @@ class MockVehicleHardware final : public IVehicleHardware {
DumpResult mDumpResult;
mutable bool mGetAllPropertyConfigsCalled GUARDED_BY(mLock) = false;
// RecurrentTimer is thread-safe.
std::shared_ptr<RecurrentTimer> mRecurrentTimer;
std::unordered_map<int32_t, std::unordered_map<int32_t, std::shared_ptr<std::function<void()>>>>