blob: 8ada468d61c97cddcb4c5e789c57ac16c2f8bd60 [file]
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <protos/aconfig_storage_metadata.pb.h>
#include <android-base/logging.h>
#include "aconfigd.h"
#include "aconfigd_util.h"
#include "storage_files.h"
using namespace aconfig_storage;
namespace android {
namespace aconfigd {
/// constructor
StorageFiles::StorageFiles(const std::string& container, const StorageRecord& record)
: container_(container)
, storage_record_(record)
, package_map_(nullptr)
, flag_map_(nullptr)
, persist_flag_val_(nullptr)
, persist_flag_info_(nullptr) {
}
/// move constructor
StorageFiles::StorageFiles(StorageFiles&& rhs) {
if (this != &rhs) {
*this = std::move(rhs);
}
}
/// move assignment
StorageFiles& StorageFiles::operator=(StorageFiles&& rhs) {
if (this != &rhs) {
container_ = rhs.container_;
storage_record_ = std::move(rhs.storage_record_);
package_map_ = std::move(rhs.package_map_);
flag_map_ = std::move(rhs.flag_map_);
persist_flag_val_ = std::move(rhs.persist_flag_val_);
persist_flag_info_ = std::move(rhs.persist_flag_info_);
}
return *this;
}
/// map a storage file
base::Result<MappedStorageFile> StorageFiles::MapStorageFile(StorageFileType file_type) {
switch (file_type) {
case StorageFileType::package_map:
if (storage_record_.package_map.empty()) {
return Error() << "Missing package map file";
}
return map_storage_file(storage_record_.package_map);
break;
case StorageFileType::flag_map:
if (storage_record_.flag_map.empty()) {
return Error() << "Missing flag map file";
}
return map_storage_file(storage_record_.flag_map);
break;
default:
return base::Error() << "Unsupported storage file type for MappedStorageFile";
}
}
/// map a mutable storage file
base::Result<MutableMappedStorageFile> StorageFiles::MapMutableStorageFile(
StorageFileType file_type) {
switch (file_type) {
case StorageFileType::flag_val:
if (storage_record_.flag_val.empty()) {
return Error() << "Missing persist flag value file";
}
return map_mutable_storage_file(storage_record_.flag_val);
break;
case StorageFileType::flag_info:
if (storage_record_.flag_info.empty()) {
return Error() << "Missing persist flag info file";
}
return map_mutable_storage_file(storage_record_.flag_info);
break;
default:
return base::Error() << "Unsupported storage file type to map";
}
}
/// get package map
base::Result<const MappedStorageFile*> StorageFiles::GetPackageMap() {
if (!package_map_) {
auto package_map = MapStorageFile(StorageFileType::package_map);
if (!package_map.ok()) {
return base::Error() << "Failed to map package map file for " << container_
<< ": " << package_map.error();
}
package_map_.reset(new MappedStorageFile(*package_map));
}
return package_map_.get();
}
/// get flag map
base::Result<const MappedStorageFile*> StorageFiles::GetFlagMap() {
if (!flag_map_) {
auto flag_map = MapStorageFile(StorageFileType::flag_map);
if (!flag_map.ok()) {
return base::Error() << "Failed to map flag map file for " << container_
<< ": " << flag_map.error();
}
flag_map_.reset(new MappedStorageFile(*flag_map));
}
return flag_map_.get();
}
/// get persist flag val
base::Result<const MutableMappedStorageFile*> StorageFiles::GetPersistFlagVal() {
if (!persist_flag_val_) {
auto flag_val = MapMutableStorageFile(StorageFileType::flag_val);
if (!flag_val.ok()) {
return base::Error() << "Failed to map persist flag value file for " << container_
<< ": " << flag_val.error();
}
persist_flag_val_.reset(new MutableMappedStorageFile(*flag_val));
}
return persist_flag_val_.get();
}
/// get persist flag info
base::Result<const MutableMappedStorageFile*> StorageFiles::GetPersistFlagInfo() {
if (!persist_flag_info_) {
auto flag_info = MapMutableStorageFile(StorageFileType::flag_info);
if (!flag_info.ok()) {
return base::Error() << "Failed to map persist flag info file for " << container_
<< ": " << flag_info.error();
}
persist_flag_info_.reset(new MutableMappedStorageFile(*flag_info));
}
return persist_flag_info_.get();
}
/// check if flag is read only
base::Result<bool> StorageFiles::IsFlagReadOnly(const PackageFlagContext& context) {
if (!context.flag_exists) {
return base::Error() << "Flag does not exist";
}
auto flag_info_file = GetPersistFlagInfo();
if (!flag_info_file.ok()) {
return base::Error() << flag_info_file.error();
}
auto ro_info_file = MappedStorageFile();
ro_info_file.file_ptr = (*flag_info_file)->file_ptr;
ro_info_file.file_size = (*flag_info_file)->file_size;
auto attribute = get_flag_attribute(
ro_info_file, context.value_type, context.flag_index);
if (!attribute.ok()) {
return base::Error() << "Failed to get flag attribute";
}
return !(*attribute & FlagInfoBit::IsReadWrite);
}
/// set storage record
void StorageFiles::SetStorageRecord(const StorageRecord& record) {
storage_record_ = record;
package_map_.reset(nullptr);
flag_map_.reset(nullptr);
persist_flag_val_.reset(nullptr);
persist_flag_info_.reset(nullptr);
}
/// Find flag value type and global index
base::Result<StorageFiles::PackageFlagContext> StorageFiles::GetPackageFlagContext(
const std::string& package,
const std::string& flag) {
auto result = PackageFlagContext(package, flag);
// early return
if (package.empty()) {
result.package_exists = false;
result.flag_exists = false;
return result;
}
// find package context
auto package_map = GetPackageMap();
if (!package_map.ok()) {
return base::Error() << package_map.error();
}
auto package_context = get_package_read_context(**package_map, package);
if (!package_context.ok()) {
return base::Error() << "Failed to get package context for " << package
<< " in " << container_ << " :" << package_context.error();
}
if (!package_context->package_exists) {
result.flag_exists = false;
return result;
} else {
result.package_exists = true;
}
// early return
if (flag.empty()) {
return result;
}
uint32_t package_id = package_context->package_id;
uint32_t boolean_flag_start_index = package_context->boolean_start_index;
// find flag context
auto flag_map = GetFlagMap();
if (!flag_map.ok()) {
return base::Error() << flag_map.error();
}
auto flag_context = get_flag_read_context(**flag_map, package_id, flag);
if (!flag_context.ok()) {
return base::Error() << "Failed to get flag context of " << package << "/"
<< flag << " in " << container_ << " :"
<< flag_context.error();
}
if (!flag_context->flag_exists) {
result.flag_exists = false;
return result;
}
StoredFlagType stored_type = flag_context->flag_type;
uint16_t within_package_flag_index = flag_context->flag_index;
auto value_type = map_to_flag_value_type(stored_type);
if (!value_type.ok()) {
return base::Error() << "Failed to get flag value type :" << value_type.error();
}
result.flag_exists = true;
result.value_type = *value_type;
result.flag_index = boolean_flag_start_index + within_package_flag_index;
return result;
}
/// check if has package
base::Result<bool> StorageFiles::HasPackage(const std::string& package) {
auto type_and_index = GetPackageFlagContext(package, "");
if (!type_and_index.ok()) {
return base::Error() << type_and_index.error();
}
return type_and_index->package_exists;
}
/// check if has flag
base::Result<bool> StorageFiles::HasFlag(const std::string& package,
const std::string& flag) {
auto type_and_index = GetPackageFlagContext(package, flag);
if (!type_and_index.ok()) {
return base::Error() << type_and_index.error();
}
return type_and_index->flag_exists;
}
/// get persistent flag attribute
base::Result<uint8_t> StorageFiles::GetFlagAttribute(
const PackageFlagContext& context) {
if (!context.flag_exists) {
return base::Error() << "Flag does not exist";
}
auto flag_info_file = GetPersistFlagInfo();
if (!flag_info_file.ok()) {
return base::Error() << flag_info_file.error();
}
auto ro_info_file = MappedStorageFile();
ro_info_file.file_ptr = (*flag_info_file)->file_ptr;
ro_info_file.file_size = (*flag_info_file)->file_size;
auto attribute = get_flag_attribute(ro_info_file, context.value_type, context.flag_index);
if (!attribute.ok()) {
return base::Error() << "Failed to get flag info: " << attribute.error();
}
return *attribute;
}
/// get server or default flag value
base::Result<std::string> StorageFiles::GetServerFlagValue(
const PackageFlagContext& context) {
if (!context.flag_exists) {
return base::Error() << "Flag does not exist";
}
auto flag_value_file = GetPersistFlagVal();
if (!flag_value_file.ok()) {
return base::Error() << flag_value_file.error();
}
switch (context.value_type) {
case FlagValueType::Boolean: {
auto ro_value_file = MappedStorageFile();
ro_value_file.file_ptr = (*flag_value_file)->file_ptr;
ro_value_file.file_size = (*flag_value_file)->file_size;
auto value = get_boolean_flag_value(ro_value_file, context.flag_index);
if (!value.ok()) {
return base::Error() << "Failed to get flag value: " << value.error();
}
return *value ? "true" : "false";
break;
}
default:
return base::Error() << "Unsupported flag value type";
}
return base::Error() << "Failed to find flag in value file";
}
/// get local flag value, will error if local flag value does not exist
base::Result<std::string> StorageFiles::GetLocalFlagValue(
const PackageFlagContext& context) {
if (!context.flag_exists) {
return base::Error() << "Flag does not exist";
}
auto pb_file = storage_record_.local_overrides;
auto pb = ReadPbFromFile<LocalFlagOverrides>(pb_file);
if (!pb.ok()) {
return Error() << "Failed to read pb from " << pb_file << ": " << pb.error();
}
for (auto& entry : pb->overrides()) {
if (context.package == entry.package_name()
&& context.flag == entry.flag_name()) {
return entry.flag_value();
}
}
return base::Error() << "Failed to find flag local override value";
}
/// server flag override, update persistent flag value
base::Result<void> StorageFiles::SetServerFlagValue(const PackageFlagContext& context,
const std::string& flag_value) {
if (!context.flag_exists) {
return base::Error() << "Flag does not exist";
}
auto readonly = IsFlagReadOnly(context);
RETURN_IF_ERROR(readonly, "Failed to check if flag is readonly")
if (*readonly) {
return base::Error() << "Cannot update read only flag";
}
auto flag_value_file = GetPersistFlagVal();
RETURN_IF_ERROR(flag_value_file, "Cannot get persist flag value file");
switch (context.value_type) {
case FlagValueType::Boolean: {
if (flag_value != "true" && flag_value != "false") {
return base::Error() << "Invalid boolean flag value, it should be true|false";
}
auto update = set_boolean_flag_value(
**flag_value_file, context.flag_index, flag_value == "true");
RETURN_IF_ERROR(update, "Failed to update flag value");
update = SetHasServerOverride(context, true);
RETURN_IF_ERROR(update, "Failed to set flag has server override");
break;
}
default:
return base::Error() << "Unsupported flag value type";
}
return {};
}
/// local flag override, update local flag override pb filee
base::Result<void> StorageFiles::SetLocalFlagValue(const PackageFlagContext& context,
const std::string& flag_value) {
if (!context.flag_exists) {
return base::Error() << "Flag does not exist";
}
auto readonly = IsFlagReadOnly(context);
RETURN_IF_ERROR(readonly, "Failed to check if flag is readonly")
if (*readonly) {
return base::Error() << "Cannot update read only flag";
}
auto pb_file = storage_record_.local_overrides;
auto pb = ReadPbFromFile<LocalFlagOverrides>(pb_file);
if (!pb.ok()) {
return Error() << "Failed to read pb from " << pb_file << ": " << pb.error();
}
bool exist = false;
for (auto& entry : *(pb->mutable_overrides())) {
if (entry.package_name() == context.package
&& entry.flag_name() == context.flag) {
if (entry.flag_value() == flag_value) {
return {};
}
exist = true;
entry.set_flag_value(flag_value);
break;
}
}
if (!exist) {
auto new_override = pb->add_overrides();
new_override->set_package_name(context.package);
new_override->set_flag_name(context.flag);
new_override->set_flag_value(flag_value);
}
auto write = WritePbToFile<LocalFlagOverrides>(*pb, pb_file);
if (!write.ok()) {
return Error() << "Failed to write pb to " << pb_file << ": " << write.error();
}
auto update = SetHasLocalOverride(context, true);
RETURN_IF_ERROR(update, "Failed to set flag has local override");
return {};
}
/// set has server override in flag info
base::Result<void> StorageFiles::SetHasServerOverride(const PackageFlagContext& context,
bool has_server_override) {
if (!context.flag_exists) {
return base::Error() << "Flag does not exist";
}
auto flag_info_file = GetPersistFlagInfo();
if (!flag_info_file.ok()) {
return base::Error() << flag_info_file.error();
}
auto update_result = set_flag_has_server_override(
**flag_info_file, context.value_type, context.flag_index, has_server_override);
if (!update_result.ok()) {
return base::Error() << "Failed to update flag has server override: "
<< update_result.error();
}
return {};
}
/// set has local override in flag info
base::Result<void> StorageFiles::SetHasLocalOverride(const PackageFlagContext& context,
bool has_local_override) {
if (!context.flag_exists) {
return base::Error() << "Flag does not exist";
}
auto flag_info_file = GetPersistFlagInfo();
if (!flag_info_file.ok()) {
return base::Error() << flag_info_file.error();
}
auto update_result = set_flag_has_local_override(
**flag_info_file, context.value_type, context.flag_index, has_local_override);
if (!update_result.ok()) {
return base::Error() << "Failed to update flag has local override: "
<< update_result.error();
}
return {};
}
/// remove a single flag local override, return if removed
base::Result<bool> StorageFiles::RemoveLocalFlagValue(
const PackageFlagContext& context) {
auto pb_file = storage_record_.local_overrides;
auto pb = ReadPbFromFile<LocalFlagOverrides>(pb_file);
if (!pb.ok()) {
return Error() << "Failed to read pb from " << pb_file << ": " << pb.error();
}
auto remaining_overrides = LocalFlagOverrides();
for (auto entry : pb->overrides()) {
if (entry.package_name() == context.package
&& entry.flag_name() == context.flag) {
continue;
}
auto kept_override = remaining_overrides.add_overrides();
kept_override->set_package_name(entry.package_name());
kept_override->set_flag_name(entry.flag_name());
kept_override->set_flag_value(entry.flag_value());
}
if (remaining_overrides.overrides_size() != pb->overrides_size()) {
auto result = WritePbToFile<LocalFlagOverrides>(remaining_overrides, pb_file);
if (!result.ok()) {
return base::Error() << result.error();
}
auto update = SetHasLocalOverride(context, false);
RETURN_IF_ERROR(update, "Failed to unset flag has local override");
return true;
} else {
return false;
}
}
/// remove all local overrides
base::Result<void> StorageFiles::RemoveAllLocalFlagValue() {
auto pb_file = storage_record_.local_overrides;
auto overrides_pb = ReadPbFromFile<LocalFlagOverrides>(pb_file);
RETURN_IF_ERROR(overrides_pb, "Failed to read local overrides");
for (auto& entry : overrides_pb->overrides()) {
auto context = GetPackageFlagContext(entry.package_name(), entry.flag_name());
RETURN_IF_ERROR(context, "Failed to find package flag context for flag "
+ entry.package_name() + "/" + entry.flag_name());
auto update = SetHasLocalOverride(*context, false);
RETURN_IF_ERROR(update, "Failed to unset flag has local override");
}
if (overrides_pb->overrides_size()) {
auto result = WritePbToFile<LocalFlagOverrides>(
LocalFlagOverrides(), pb_file);
RETURN_IF_ERROR(result, "Failed to flush local overrides pb file");
}
return {};
}
/// apply local update to boot flag value copy
base::Result<void> StorageFiles::ApplyLocalOverride(
const std::string& flag_value_file) {
auto mutable_flag_value_file = map_mutable_storage_file(flag_value_file);
if (!mutable_flag_value_file.ok()) {
return base::Error() << "Failed to map flag value file for local override: "
<< mutable_flag_value_file.error();
}
auto pb_file = storage_record_.local_overrides;
auto pb = ReadPbFromFile<LocalFlagOverrides>(pb_file);
if (!pb.ok()) {
return Error() << "Failed to read pb from " << pb_file << ": " << pb.error();
}
auto applied_overrides = LocalFlagOverrides();
for (auto& entry : pb->overrides()) {
// find flag value type and index
auto context = GetPackageFlagContext(entry.package_name(), entry.flag_name());
if (!context.ok()) {
return base::Error() << "Failed to find flag: " << context.error();
}
if (!context->flag_exists) {
continue;
}
// apply a local override
switch (context->value_type) {
case FlagValueType::Boolean: {
// validate value
if (entry.flag_value() != "true" && entry.flag_value() != "false") {
return base::Error() << "Invalid boolean flag value, it should be true|false";
}
// update flag value
auto update_result = set_boolean_flag_value(
*mutable_flag_value_file, context->flag_index, entry.flag_value() == "true");
if (!update_result.ok()) {
return base::Error() << "Failed to update flag value: " << update_result.error();
}
break;
}
default:
return base::Error() << "Unsupported flag value type";
}
// mark it applied
auto new_applied = applied_overrides.add_overrides();
new_applied->set_package_name(entry.package_name());
new_applied->set_flag_name(entry.flag_name());
new_applied->set_flag_value(entry.flag_value());
}
if (pb->overrides_size() != applied_overrides.overrides_size()) {
auto result = WritePbToFile<LocalFlagOverrides>(applied_overrides, pb_file);
if (!result.ok()) {
return base::Error() << result.error();
}
}
return {};
}
} // namespace aconfigd
} // namespace android