Use read-write lock to guard property config.

Property config now may change dynamically due to late-init. We might
also support dynamic config later. This CL uses read write lock
to properly guard access to config map.

Flag: EXEMPT HAL change
Test: atest DefaultVehicleHalTest
Bug: 342470570
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:35401eb1ba251507ed68748b19b0c4a2fcc56c40)
Merged-In: I0df79040c363e66baab48631f457752532d2967d
Change-Id: I0df79040c363e66baab48631f457752532d2967d
This commit is contained in:
Yu Shan
2024-09-24 21:41:57 +00:00
committed by Android Build Cherrypicker Worker
parent 7956b83f9f
commit acd65a80a9
2 changed files with 158 additions and 87 deletions

View File

@@ -31,6 +31,7 @@
#include <android-base/thread_annotations.h>
#include <android/binder_auto_utils.h>
#include <functional>
#include <memory>
#include <mutex>
#include <shared_mutex>
@@ -138,12 +139,11 @@ class DefaultVehicleHal final : public aidlvhal::BnVehicle {
// Only used for testing.
int32_t mTestInterfaceVersion = 0;
// mConfigsByPropId and mConfigFile is lazy initialized.
mutable std::mutex mConfigInitLock;
mutable bool mConfigInit GUARDED_BY(mConfigInitLock) = false;
mutable std::atomic<bool> mConfigInit = false;
mutable std::shared_timed_mutex mConfigLock;
mutable std::unordered_map<int32_t, aidlvhal::VehiclePropConfig> mConfigsByPropId
GUARDED_BY(mConfigInitLock);
mutable std::unique_ptr<ndk::ScopedFileDescriptor> mConfigFile GUARDED_BY(mConfigInitLock);
GUARDED_BY(mConfigLock);
mutable std::unique_ptr<ndk::ScopedFileDescriptor> mConfigFile GUARDED_BY(mConfigLock);
std::mutex mLock;
std::unordered_map<const AIBinder*, std::unique_ptr<OnBinderDiedContext>> mOnBinderDiedContexts
@@ -175,7 +175,10 @@ class DefaultVehicleHal final : public aidlvhal::BnVehicle {
android::base::Result<std::vector<int64_t>> checkDuplicateRequests(
const std::vector<aidlvhal::SetValueRequest>& requests);
VhalResult<void> checkSubscribeOptions(const std::vector<aidlvhal::SubscribeOptions>& options);
VhalResult<void> checkSubscribeOptions(
const std::vector<aidlvhal::SubscribeOptions>& options,
const std::unordered_map<int32_t, aidlvhal::VehiclePropConfig>& configsByPropId)
REQUIRES_SHARED(mConfigLock);
VhalResult<void> checkPermissionHelper(const aidlvhal::VehiclePropValue& value,
aidlvhal::VehiclePropertyAccess accessToTest) const;
@@ -184,7 +187,7 @@ class DefaultVehicleHal final : public aidlvhal::BnVehicle {
VhalResult<void> checkWritePermission(const aidlvhal::VehiclePropValue& value) const;
android::base::Result<const aidlvhal::VehiclePropConfig*> getConfig(int32_t propId) const;
android::base::Result<aidlvhal::VehiclePropConfig> getConfig(int32_t propId) const;
void onBinderDiedWithContext(const AIBinder* clientId);
@@ -196,7 +199,7 @@ class DefaultVehicleHal final : public aidlvhal::BnVehicle {
bool checkDumpPermission();
bool getAllPropConfigsFromHardwareLocked() const REQUIRES(mConfigInitLock);
bool getAllPropConfigsFromHardwareLocked() const EXCLUDES(mConfigLock);
// The looping handler function to process all onBinderDied or onBinderUnlinked events in
// mBinderEvents.
@@ -209,10 +212,12 @@ class DefaultVehicleHal final : public aidlvhal::BnVehicle {
int32_t getVhalInterfaceVersion() const;
// Gets mConfigsByPropId, lazy init it if necessary.
const std::unordered_map<int32_t, aidlvhal::VehiclePropConfig>& getConfigsByPropId() const;
// Gets mConfigFile, lazy init it if necessary.
const ndk::ScopedFileDescriptor* getConfigFile() const;
// Gets mConfigsByPropId, lazy init it if necessary. Note that the reference is only valid in
// the scope of the callback and it is guaranteed that read lock is obtained during the
// callback.
void getConfigsByPropId(
std::function<void(const std::unordered_map<int32_t, aidlvhal::VehiclePropConfig>&)>
callback) const EXCLUDES(mConfigLock);
// Puts the property change events into a queue so that they can handled in batch.
static void batchPropertyChangeEvent(
@@ -239,6 +244,12 @@ class DefaultVehicleHal final : public aidlvhal::BnVehicle {
static void onBinderUnlinked(void* cookie);
static void parseSubscribeOptions(
const std::vector<aidlvhal::SubscribeOptions>& options,
const std::unordered_map<int32_t, aidlvhal::VehiclePropConfig>& configsByPropId,
std::vector<aidlvhal::SubscribeOptions>& onChangeSubscriptions,
std::vector<aidlvhal::SubscribeOptions>& continuousSubscriptions);
// Test-only
// Set the default timeout for pending requests.
void setTimeout(int64_t timeoutInNano);

View File

@@ -95,6 +95,18 @@ float getDefaultSampleRateHz(float sampleRateHz, float minSampleRateHz, float ma
return sampleRateHz;
}
class SCOPED_CAPABILITY SharedScopedLockAssertion {
public:
SharedScopedLockAssertion(std::shared_timed_mutex& mutex) ACQUIRE_SHARED(mutex) {}
~SharedScopedLockAssertion() RELEASE() {}
};
class SCOPED_CAPABILITY UniqueScopedLockAssertion {
public:
UniqueScopedLockAssertion(std::shared_timed_mutex& mutex) ACQUIRE(mutex) {}
~UniqueScopedLockAssertion() RELEASE() {}
};
} // namespace
DefaultVehicleHal::DefaultVehicleHal(std::unique_ptr<IVehicleHardware> vehicleHardware)
@@ -355,68 +367,82 @@ bool DefaultVehicleHal::getAllPropConfigsFromHardwareLocked() const {
}
filteredConfigs.push_back(std::move(config));
}
for (auto& config : filteredConfigs) {
mConfigsByPropId[config.prop] = config;
}
VehiclePropConfigs vehiclePropConfigs;
vehiclePropConfigs.payloads = std::move(filteredConfigs);
auto result = LargeParcelableBase::parcelableToStableLargeParcelable(vehiclePropConfigs);
if (!result.ok()) {
ALOGE("failed to convert configs to shared memory file, error: %s, code: %d",
result.error().message().c_str(), static_cast<int>(result.error().code()));
mConfigFile = nullptr;
return false;
{
std::unique_lock<std::shared_timed_mutex> configWriteLock(mConfigLock);
UniqueScopedLockAssertion lockAssertion(mConfigLock);
for (auto& config : filteredConfigs) {
mConfigsByPropId[config.prop] = config;
}
VehiclePropConfigs vehiclePropConfigs;
vehiclePropConfigs.payloads = std::move(filteredConfigs);
auto result = LargeParcelableBase::parcelableToStableLargeParcelable(vehiclePropConfigs);
if (!result.ok()) {
ALOGE("failed to convert configs to shared memory file, error: %s, code: %d",
result.error().message().c_str(), static_cast<int>(result.error().code()));
mConfigFile = nullptr;
return false;
}
if (result.value() != nullptr) {
mConfigFile = std::move(result.value());
}
}
if (result.value() != nullptr) {
mConfigFile = std::move(result.value());
}
mConfigInit = true;
return true;
}
const ScopedFileDescriptor* DefaultVehicleHal::getConfigFile() const {
std::scoped_lock lockGuard(mConfigInitLock);
void DefaultVehicleHal::getConfigsByPropId(
std::function<void(const std::unordered_map<int32_t, VehiclePropConfig>&)> callback) const {
if (!mConfigInit) {
CHECK(getAllPropConfigsFromHardwareLocked())
<< "Failed to get property configs from hardware";
mConfigInit = true;
}
return mConfigFile.get();
}
const std::unordered_map<int32_t, VehiclePropConfig>& DefaultVehicleHal::getConfigsByPropId()
const {
std::scoped_lock lockGuard(mConfigInitLock);
if (!mConfigInit) {
CHECK(getAllPropConfigsFromHardwareLocked())
<< "Failed to get property configs from hardware";
mConfigInit = true;
}
return mConfigsByPropId;
std::shared_lock<std::shared_timed_mutex> configReadLock(mConfigLock);
SharedScopedLockAssertion lockAssertion(mConfigLock);
callback(mConfigsByPropId);
}
ScopedAStatus DefaultVehicleHal::getAllPropConfigs(VehiclePropConfigs* output) {
const ScopedFileDescriptor* configFile = getConfigFile();
const auto& configsByPropId = getConfigsByPropId();
if (configFile != nullptr) {
if (!mConfigInit) {
CHECK(getAllPropConfigsFromHardwareLocked())
<< "Failed to get property configs from hardware";
}
std::shared_lock<std::shared_timed_mutex> configReadLock(mConfigLock);
SharedScopedLockAssertion lockAssertion(mConfigLock);
if (mConfigFile != nullptr) {
output->payloads.clear();
output->sharedMemoryFd.set(dup(configFile->get()));
output->sharedMemoryFd.set(dup(mConfigFile->get()));
return ScopedAStatus::ok();
}
output->payloads.reserve(configsByPropId.size());
for (const auto& [_, config] : configsByPropId) {
output->payloads.reserve(mConfigsByPropId.size());
for (const auto& [_, config] : mConfigsByPropId) {
output->payloads.push_back(config);
}
return ScopedAStatus::ok();
}
Result<const VehiclePropConfig*> DefaultVehicleHal::getConfig(int32_t propId) const {
const auto& configsByPropId = getConfigsByPropId();
auto it = configsByPropId.find(propId);
if (it == configsByPropId.end()) {
return Error() << "no config for property, ID: " << propId;
}
return &(it->second);
Result<VehiclePropConfig> DefaultVehicleHal::getConfig(int32_t propId) const {
Result<VehiclePropConfig> result;
getConfigsByPropId([this, &result, propId](const auto& configsByPropId) {
SharedScopedLockAssertion lockAssertion(mConfigLock);
auto it = configsByPropId.find(propId);
if (it == configsByPropId.end()) {
result = Error() << "no config for property, ID: " << propId;
return;
}
// Copy the VehiclePropConfig
result = it->second;
});
return result;
}
Result<void> DefaultVehicleHal::checkProperty(const VehiclePropValue& propValue) {
@@ -425,15 +451,15 @@ Result<void> DefaultVehicleHal::checkProperty(const VehiclePropValue& propValue)
if (!result.ok()) {
return result.error();
}
const VehiclePropConfig* config = result.value();
const VehicleAreaConfig* areaConfig = getAreaConfig(propValue, *config);
const VehiclePropConfig& config = result.value();
const VehicleAreaConfig* areaConfig = getAreaConfig(propValue, config);
if (!isGlobalProp(propId) && areaConfig == nullptr) {
// Ignore areaId for global property. For non global property, check whether areaId is
// allowed. areaId must appear in areaConfig.
return Error() << "invalid area ID: " << propValue.areaId << " for prop ID: " << propId
<< ", not listed in config";
}
if (auto result = checkPropValue(propValue, config); !result.ok()) {
if (auto result = checkPropValue(propValue, &config); !result.ok()) {
return Error() << "invalid property value: " << propValue.toString()
<< ", error: " << getErrorMsg(result);
}
@@ -659,17 +685,27 @@ ScopedAStatus DefaultVehicleHal::setValues(const CallbackType& callback,
ScopedAStatus DefaultVehicleHal::getPropConfigs(const std::vector<int32_t>& props,
VehiclePropConfigs* output) {
std::vector<VehiclePropConfig> configs;
const auto& configsByPropId = getConfigsByPropId();
for (int32_t prop : props) {
auto it = configsByPropId.find(prop);
if (it != configsByPropId.end()) {
configs.push_back(it->second);
} else {
return ScopedAStatus::fromServiceSpecificErrorWithMessage(
toInt(StatusCode::INVALID_ARG),
StringPrintf("no config for property, ID: %" PRId32, prop).c_str());
ScopedAStatus status = ScopedAStatus::ok();
getConfigsByPropId([this, &configs, &status, &props](const auto& configsByPropId) {
SharedScopedLockAssertion lockAssertion(mConfigLock);
for (int32_t prop : props) {
auto it = configsByPropId.find(prop);
if (it != configsByPropId.end()) {
configs.push_back(it->second);
} else {
status = ScopedAStatus::fromServiceSpecificErrorWithMessage(
toInt(StatusCode::INVALID_ARG),
StringPrintf("no config for property, ID: %" PRId32, prop).c_str());
return;
}
}
});
if (!status.isOk()) {
return status;
}
return vectorToStableLargeParcelable(std::move(configs), output);
}
@@ -691,8 +727,8 @@ bool areaConfigsHaveRequiredAccess(const std::vector<VehicleAreaConfig>& areaCon
}
VhalResult<void> DefaultVehicleHal::checkSubscribeOptions(
const std::vector<SubscribeOptions>& options) {
const auto& configsByPropId = getConfigsByPropId();
const std::vector<SubscribeOptions>& options,
const std::unordered_map<int32_t, VehiclePropConfig>& configsByPropId) {
for (const auto& option : options) {
int32_t propId = option.propId;
auto it = configsByPropId.find(propId);
@@ -757,23 +793,15 @@ VhalResult<void> DefaultVehicleHal::checkSubscribeOptions(
}
}
}
return {};
}
ScopedAStatus DefaultVehicleHal::subscribe(const CallbackType& callback,
const std::vector<SubscribeOptions>& options,
[[maybe_unused]] int32_t maxSharedMemoryFileCount) {
// TODO(b/205189110): Use shared memory file count.
if (callback == nullptr) {
return ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
}
if (auto result = checkSubscribeOptions(options); !result.ok()) {
ALOGE("subscribe: invalid subscribe options: %s", getErrorMsg(result).c_str());
return toScopedAStatus(result);
}
std::vector<SubscribeOptions> onChangeSubscriptions;
std::vector<SubscribeOptions> continuousSubscriptions;
const auto& configsByPropId = getConfigsByPropId();
void DefaultVehicleHal::parseSubscribeOptions(
const std::vector<SubscribeOptions>& options,
const std::unordered_map<int32_t, VehiclePropConfig>& configsByPropId,
std::vector<SubscribeOptions>& onChangeSubscriptions,
std::vector<SubscribeOptions>& continuousSubscriptions) {
for (const auto& option : options) {
int32_t propId = option.propId;
// We have already validate config exists.
@@ -831,6 +859,34 @@ ScopedAStatus DefaultVehicleHal::subscribe(const CallbackType& callback,
onChangeSubscriptions.push_back(std::move(optionCopy));
}
}
}
ScopedAStatus DefaultVehicleHal::subscribe(const CallbackType& callback,
const std::vector<SubscribeOptions>& options,
[[maybe_unused]] int32_t maxSharedMemoryFileCount) {
// TODO(b/205189110): Use shared memory file count.
if (callback == nullptr) {
return ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
}
std::vector<SubscribeOptions> onChangeSubscriptions;
std::vector<SubscribeOptions> continuousSubscriptions;
ScopedAStatus returnStatus = ScopedAStatus::ok();
getConfigsByPropId([this, &returnStatus, &options, &onChangeSubscriptions,
&continuousSubscriptions](const auto& configsByPropId) {
SharedScopedLockAssertion lockAssertion(mConfigLock);
if (auto result = checkSubscribeOptions(options, configsByPropId); !result.ok()) {
ALOGE("subscribe: invalid subscribe options: %s", getErrorMsg(result).c_str());
returnStatus = toScopedAStatus(result);
return;
}
parseSubscribeOptions(options, configsByPropId, onChangeSubscriptions,
continuousSubscriptions);
});
if (!returnStatus.isOk()) {
return returnStatus;
}
{
// Lock to make sure onBinderDied would not be called concurrently.
@@ -891,13 +947,13 @@ VhalResult<void> DefaultVehicleHal::checkPermissionHelper(
return StatusError(StatusCode::INVALID_ARG) << getErrorMsg(result);
}
const VehiclePropConfig* config = result.value();
const VehicleAreaConfig* areaConfig = getAreaConfig(value, *config);
const VehiclePropConfig& config = result.value();
const VehicleAreaConfig* areaConfig = getAreaConfig(value, config);
if (areaConfig == nullptr && !isGlobalProp(propId)) {
return StatusError(StatusCode::INVALID_ARG) << "no config for area ID: " << value.areaId;
}
if (!hasRequiredAccess(config->access, accessToTest) &&
if (!hasRequiredAccess(config.access, accessToTest) &&
(areaConfig == nullptr || !hasRequiredAccess(areaConfig->access, accessToTest))) {
return StatusError(StatusCode::ACCESS_DENIED)
<< StringPrintf("Property %" PRId32 " does not have the following access: %" PRId32,
@@ -966,7 +1022,6 @@ binder_status_t DefaultVehicleHal::dump(int fd, const char** args, uint32_t numA
}
DumpResult result = mVehicleHardware->dump(options);
if (result.refreshPropertyConfigs) {
std::scoped_lock lockGuard(mConfigInitLock);
getAllPropConfigsFromHardwareLocked();
}
dprintf(fd, "%s", (result.buffer + "\n").c_str());
@@ -974,11 +1029,16 @@ binder_status_t DefaultVehicleHal::dump(int fd, const char** args, uint32_t numA
return STATUS_OK;
}
dprintf(fd, "Vehicle HAL State: \n");
const auto& configsByPropId = getConfigsByPropId();
std::unordered_map<int32_t, VehiclePropConfig> configsByPropIdCopy;
getConfigsByPropId([this, &configsByPropIdCopy](const auto& configsByPropId) {
SharedScopedLockAssertion lockAssertion(mConfigLock);
configsByPropIdCopy = configsByPropId;
});
{
std::scoped_lock<std::mutex> lockGuard(mLock);
dprintf(fd, "Interface version: %" PRId32 "\n", getVhalInterfaceVersion());
dprintf(fd, "Containing %zu property configs\n", configsByPropId.size());
dprintf(fd, "Containing %zu property configs\n", configsByPropIdCopy.size());
dprintf(fd, "Currently have %zu getValues clients\n", mGetValuesClients.size());
dprintf(fd, "Currently have %zu setValues clients\n", mSetValuesClients.size());
dprintf(fd, "Currently have %zu subscribe clients\n", countSubscribeClients());