storage daemon: add flag value override capability
Bug: b/312444587
Test: atest aconfigd_test
Change-Id: Ib0f67ae498ad1dbfbacebdbb0e3adbf24746bcdd
diff --git a/aconfigd/Android.bp b/aconfigd/Android.bp
index b35cd81..521053b 100644
--- a/aconfigd/Android.bp
+++ b/aconfigd/Android.bp
@@ -8,6 +8,7 @@
],
static_libs: [
"libaconfig_storage_read_api_cc",
+ "libaconfig_storage_write_api_cc",
"libaconfig_storage_protos_cc",
"libprotobuf-cpp-lite",
"libbase",
@@ -25,6 +26,8 @@
],
static_libs: [
"libgmock",
+ "libaconfig_storage_read_api_cc",
+ "libaconfig_storage_write_api_cc",
"libaconfig_storage_protos_cc",
"libprotobuf-cpp-lite",
"libbase",
diff --git a/aconfigd/aconfigd.cpp b/aconfigd/aconfigd.cpp
index 9135a2c..a03fa7b 100644
--- a/aconfigd/aconfigd.cpp
+++ b/aconfigd/aconfigd.cpp
@@ -24,6 +24,7 @@
#include <cutils/sockets.h>
#include <aconfig_storage/aconfig_storage_read_api.hpp>
+#include <aconfig_storage/aconfig_storage_write_api.hpp>
#include <protos/aconfig_storage_metadata.pb.h>
#include <aconfigd.pb.h>
@@ -69,9 +70,12 @@
/// A map from container name to the respective storage file locations
using StorageRecords = std::unordered_map<std::string, StorageRecord>;
-/// In memort storage file records. Parsed from the pb.
+/// In memory storage file records. Parsed from the pb.
static StorageRecords persist_storage_records;
+/// In memory cache for package to container mapping
+static std::unordered_map<std::string, std::string> container_map;
+
namespace {
/// Read persistent aconfig storage records pb file
@@ -227,6 +231,144 @@
return false;
}
+/// Find the container name given flag package name
+Result<std::string> FindContainer(const std::string& package) {
+ if (container_map.count(package)) {
+ return container_map[package];
+ }
+
+ auto records_pb = ReadStorageRecordsPb(kAvailableStorageRecordsFileName);
+ if (!records_pb.ok()) {
+ return Error() << "Unable to read available storage records: "
+ << records_pb.error();
+ }
+
+ for (auto& entry : records_pb->files()) {
+ auto mapped_file = aconfig_storage::get_mapped_file(
+ entry.container(), aconfig_storage::StorageFileType::package_map);
+ if (!mapped_file.ok()) {
+ return Error() << "Failed to map file for container " << entry.container()
+ << ": " << mapped_file.error();
+ }
+
+ auto offset = aconfig_storage::get_package_offset(*mapped_file, package);
+ if (!offset.ok()) {
+ return Error() << "Failed to get offset for package " << package
+ << " from package map of " << entry.container() << " :"
+ << offset.error();
+ }
+
+ if (offset->package_exists) {
+ container_map[package] = entry.container();
+ return entry.container();
+ }
+ }
+
+ return Error() << "package not found";
+}
+
+/// Find boolean flag offset in flag value file
+Result<uint32_t> FindBooleanFlagOffset(const std::string& container,
+ const std::string& package,
+ const std::string& flag) {
+
+ auto package_map = aconfig_storage::get_mapped_file(
+ container, aconfig_storage::StorageFileType::package_map);
+ if (!package_map.ok()) {
+ return Error() << "Failed to map package map file for " << container
+ << ": " << package_map.error();
+ }
+
+ auto pkg_offset = aconfig_storage::get_package_offset(*package_map, package);
+ if (!pkg_offset.ok()) {
+ return Error() << "Failed to get package offset of " << package
+ << " in " << container << " :" << pkg_offset.error();
+ }
+
+ if (!pkg_offset->package_exists) {
+ return Error() << package << " is not found in " << container;
+ }
+
+ uint32_t package_id = pkg_offset->package_id;
+ uint32_t package_offset = pkg_offset->boolean_offset;
+
+ auto flag_map = aconfig_storage::get_mapped_file(
+ container, aconfig_storage::StorageFileType::flag_map);
+ if (!flag_map.ok()) {
+ return Error() << "Failed to map flag map file for " << container
+ << ": " << flag_map.error();
+ }
+
+ auto flg_offset = aconfig_storage::get_flag_offset(*flag_map, package_id, flag);
+ if (!flg_offset.ok()) {
+ return Error() << "Failed to get flag offset of " << flag
+ << " in " << container << " :" << flg_offset.error();
+ }
+
+ if (!flg_offset->flag_exists) {
+ return Error() << flag << " is not found in " << container;
+ }
+
+ uint16_t flag_offset = flg_offset->flag_offset;
+ return package_offset + flag_offset;
+}
+
+/// Add a new storage
+Result<void> add_new_storage(const std::string& container,
+ const std::string& package_map,
+ const std::string& flag_map,
+ const std::string& flag_val) {
+ auto updated_result = HandleContainerUpdate(
+ container, package_map, flag_map, flag_val);
+ if (!updated_result.ok()) {
+ return Error() << "Failed to update container " << container
+ << ":" << updated_result.error();
+ }
+
+ auto copy_result = CreateBootSnapshotForContainer(container);
+ if (!copy_result.ok()) {
+ return Error() << "Failed to make a boot copy: " << copy_result.error();
+ }
+
+ return {};
+}
+
+/// Update persistent boolean flag value
+Result<void> update_boolean_flag_value(const std::string& package_name,
+ const std::string& flag_name,
+ const std::string& flag_value) {
+ auto container_result = FindContainer(package_name);
+ if (!container_result.ok()) {
+ return Error() << "Failed for find container for package " << package_name
+ << ": " << container_result.error();
+ }
+ auto container = *container_result;
+
+ auto offset_result = FindBooleanFlagOffset(container, package_name, flag_name);
+ if (!offset_result.ok()) {
+ return Error() << "Failed to obtain " << package_name << "."
+ << flag_name << " flag value offset: " << offset_result.error();
+ }
+
+ auto mapped_file = aconfig_storage::get_mapped_flag_value_file(container);
+ if (!mapped_file.ok()) {
+ return Error() << "Failed to map flag value file for " << container
+ << ": " << mapped_file.error();
+ }
+
+ if (flag_value != "true" && flag_value != "false") {
+ return Error() << "Invalid boolean flag value, it should be true|false";
+ }
+
+ auto update_result = aconfig_storage::set_boolean_flag_value(
+ *mapped_file, *offset_result, flag_value == "true");
+ if (!update_result.ok()) {
+ return Error() << "Failed to update flag value: " << update_result.error();
+ }
+
+ return {};
+}
+
} // namespace
/// Initialize in memory aconfig storage records
@@ -294,24 +436,20 @@
switch (message.msg_case()) {
case StorageMessage::kNewStorageMessage: {
+ LOG(INFO) << "received a new storage request";
auto msg = message.new_storage_message();
-
- auto updated_result = HandleContainerUpdate(
- msg.container(), msg.package_map(), msg.flag_map(), msg.flag_value());
- if (!updated_result.ok()) {
- return Error() << "Failed to update container " << msg.container()
- << ":" << updated_result.error();
- }
-
- auto copy_result = CreateBootSnapshotForContainer(msg.container());
- if (!copy_result.ok()) {
- return Error() << "Failed to make a boot copy: " << copy_result.error();
- }
+ return add_new_storage(msg.container(),
+ msg.package_map(),
+ msg.flag_map(),
+ msg.flag_value());
break;
}
case StorageMessage::kFlagOverrideMessage: {
- // TODO
- // Update flag value based
+ LOG(INFO) << "received a flag override request";
+ auto msg = message.flag_override_message();
+ return update_boolean_flag_value(msg.package_name(),
+ msg.flag_name(),
+ msg.flag_value());
break;
}
default:
diff --git a/aconfigd/aconfigd_test.cpp b/aconfigd/aconfigd_test.cpp
index 17496e9..101a3ef 100644
--- a/aconfigd/aconfigd_test.cpp
+++ b/aconfigd/aconfigd_test.cpp
@@ -23,6 +23,8 @@
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
+#include "aconfig_storage/aconfig_storage_read_api.hpp"
+#include "aconfig_storage/aconfig_storage_write_api.hpp"
#include <protos/aconfig_storage_metadata.pb.h>
#include <aconfigd.pb.h>
#include "aconfigd.h"
@@ -60,19 +62,79 @@
return sock_fd;
}
-TEST(aconfigd_socket, new_storage_message) {
+base::Result<void> send_new_storage_message() {
auto sock_fd = connect_aconfigd_socket();
- ASSERT_TRUE(sock_fd.ok()) << strerror(errno);
+ if (!sock_fd.ok()) {
+ return Error() << sock_fd.error();
+ }
auto message = StorageMessage{};
auto msg = message.mutable_new_storage_message();
auto test_dir = base::GetExecutableDirectory();
- msg->set_container("test");
+ msg->set_container("mockup");
msg->set_package_map(test_dir + "/tests/package.map");
msg->set_flag_map(test_dir + "/tests/flag.map");
msg->set_flag_value(test_dir + "/tests/flag.val");
auto message_string = std::string();
+ if (!message.SerializeToString(&message_string)) {
+ return Error() << "failed to serialize pb to string";
+ }
+
+ auto result = TEMP_FAILURE_RETRY(
+ send(*sock_fd, message_string.c_str(), message_string.size(), 0));
+ if (result != static_cast<long>(message_string.size())) {
+ return ErrnoError() << "send() failed";
+ }
+
+ char buffer[1280] = {};
+ auto num_bytes = TEMP_FAILURE_RETRY(recv(*sock_fd, buffer, sizeof(buffer), 0));
+ if (num_bytes < 0) {
+ return ErrnoError() << "recv() failed";
+ }
+
+ auto return_msg = std::string(buffer, num_bytes);
+ if (!return_msg.empty()) {
+ return Error() << "failed to add mockup storage: " << return_msg;
+ }
+
+ return {};
+}
+
+TEST(aconfigd_socket, new_storage_message) {
+ auto new_storage_result = send_new_storage_message();
+ ASSERT_TRUE(new_storage_result.ok()) << new_storage_result.error();
+
+ auto pb_file = "/metadata/aconfig/boot/available_storage_file_records.pb";
+ auto records_pb = storage_records_pb();
+ auto content = std::string();
+ ASSERT_TRUE(base::ReadFileToString(pb_file, &content)) << strerror(errno);
+ ASSERT_TRUE(records_pb.ParseFromString(content)) << strerror(errno);
+
+ bool found = false;
+ for (auto& entry : records_pb.files()) {
+ if (entry.container() == "mockup") {
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+}
+
+TEST(aconfigd_socket, flag_override_message) {
+ auto new_storage_result = send_new_storage_message();
+ ASSERT_TRUE(new_storage_result.ok()) << new_storage_result.error();
+
+ auto sock_fd = connect_aconfigd_socket();
+ ASSERT_TRUE(sock_fd.ok()) << strerror(errno);
+
+ auto message = StorageMessage{};
+ auto msg = message.mutable_flag_override_message();
+ msg->set_package_name("com.android.aconfig.storage.test_1");
+ msg->set_flag_name("enabled_rw");
+ msg->set_flag_value("true");
+
+ auto message_string = std::string();
ASSERT_TRUE(message.SerializeToString(&message_string));
auto result = TEMP_FAILURE_RETRY(
@@ -85,21 +147,6 @@
ASSERT_TRUE(num_bytes >= 0) << strerror(errno);
auto received_msg = std::string(buffer, num_bytes);
ASSERT_EQ(received_msg, "") << "expect empty error message returned from socket";
-
- auto pb_file = "/metadata/aconfig/boot/available_storage_file_records.pb";
- auto records_pb = storage_records_pb();
- auto content = std::string();
- ASSERT_TRUE(base::ReadFileToString(pb_file, &content)) << strerror(errno);
- ASSERT_TRUE(records_pb.ParseFromString(content)) << strerror(errno);
-
- bool found = false;
- for (auto& entry : records_pb.files()) {
- if (entry.container() == "test") {
- found = true;
- break;
- }
- }
- ASSERT_TRUE(found);
}
} // namespace aconfigd
diff --git a/aconfigd/tests/flag.map b/aconfigd/tests/flag.map
index c690023..c1dab4a 100644
--- a/aconfigd/tests/flag.map
+++ b/aconfigd/tests/flag.map
Binary files differ
diff --git a/aconfigd/tests/flag.val b/aconfigd/tests/flag.val
index 273d196..3c82f31 100644
--- a/aconfigd/tests/flag.val
+++ b/aconfigd/tests/flag.val
Binary files differ
diff --git a/aconfigd/tests/package.map b/aconfigd/tests/package.map
index dc6b4c3..6c46a03 100644
--- a/aconfigd/tests/package.map
+++ b/aconfigd/tests/package.map
Binary files differ