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