Store frro configs in idmap file

This also includes the change to use the configs to decide which (if
any) frros to use at runtime

Bug: 243066074
Test: Manual, updated and created automated tests
Change-Id: I3f1d23e2958ad170799880b9f5eb5bd8ceb1fa67
diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
index 3bbe9d9..4b271a1 100644
--- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
+++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
@@ -70,12 +70,35 @@
   }
 
   static constexpr uint16_t kValueSize = 8U;
+  std::vector<std::pair<ConfigDescription, TargetValue>> target_values;
+  target_values.reserve(data.GetHeader()->GetTargetInlineEntryValueCount());
   for (const auto& target_entry : data.GetTargetInlineEntries()) {
     Write32(target_entry.target_id);
+    Write32(target_values.size());
+    Write32(target_entry.values.size());
+    target_values.insert(
+        target_values.end(), target_entry.values.begin(), target_entry.values.end());
+  }
+
+  std::vector<ConfigDescription> configs;
+  configs.reserve(data.GetHeader()->GetConfigCount());
+  for (const auto& target_entry_value : target_values) {
+    auto config_it = find(configs.begin(), configs.end(), target_entry_value.first);
+    if (config_it != configs.end()) {
+      Write32(config_it - configs.begin());
+    } else {
+      Write32(configs.size());
+      configs.push_back(target_entry_value.first);
+    }
     Write16(kValueSize);
     Write8(0U);  // padding
-    Write8(target_entry.value.data_type);
-    Write32(target_entry.value.data_value);
+    Write8(target_entry_value.second.data_type);
+    Write32(target_entry_value.second.data_value);
+  }
+
+  for( auto& cd : configs) {
+    cd.swapHtoD();
+    stream_.write(reinterpret_cast<char*>(&cd), sizeof(cd));
   }
 
   for (const auto& overlay_entry : data.GetOverlayEntries()) {
@@ -89,6 +112,8 @@
 void BinaryStreamVisitor::visit(const IdmapData::Header& header) {
   Write32(header.GetTargetEntryCount());
   Write32(header.GetTargetInlineEntryCount());
+  Write32(header.GetTargetInlineEntryValueCount());
+  Write32(header.GetConfigCount());
   Write32(header.GetOverlayEntryCount());
   Write32(header.GetStringPoolIndexOffset());
 }
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 06650f6..4efaeea 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -186,6 +186,8 @@
   std::unique_ptr<IdmapData::Header> idmap_data_header(new IdmapData::Header());
   if (!Read32(stream, &idmap_data_header->target_entry_count) ||
       !Read32(stream, &idmap_data_header->target_entry_inline_count) ||
+      !Read32(stream, &idmap_data_header->target_entry_inline_value_count) ||
+      !Read32(stream, &idmap_data_header->config_count) ||
       !Read32(stream, &idmap_data_header->overlay_entry_count) ||
       !Read32(stream, &idmap_data_header->string_pool_index_offset)) {
     return nullptr;
@@ -207,20 +209,59 @@
     if (!Read32(stream, &target_entry.target_id) || !Read32(stream, &target_entry.overlay_id)) {
       return nullptr;
     }
-    data->target_entries_.push_back(target_entry);
+    data->target_entries_.emplace_back(target_entry);
   }
 
   // Read the mapping of target resource id to inline overlay values.
-  uint8_t unused1;
-  uint16_t unused2;
+  std::vector<std::tuple<TargetInlineEntry, uint32_t, uint32_t>> target_inline_entries;
   for (size_t i = 0; i < data->header_->GetTargetInlineEntryCount(); i++) {
     TargetInlineEntry target_entry{};
-    if (!Read32(stream, &target_entry.target_id) || !Read16(stream, &unused2) ||
-        !Read8(stream, &unused1) || !Read8(stream, &target_entry.value.data_type) ||
-        !Read32(stream, &target_entry.value.data_value)) {
+    uint32_t entry_offset;
+    uint32_t entry_count;
+    if (!Read32(stream, &target_entry.target_id) || !Read32(stream, &entry_offset)
+        || !Read32(stream, &entry_count)) {
       return nullptr;
     }
-    data->target_inline_entries_.push_back(target_entry);
+    target_inline_entries.emplace_back(std::make_tuple(target_entry, entry_offset, entry_count));
+  }
+
+  // Read the inline overlay resource values
+  std::vector<std::pair<uint32_t, TargetValue>> target_values;
+  uint8_t unused1;
+  uint16_t unused2;
+  for (size_t i = 0; i < data->header_->GetTargetInlineEntryValueCount(); i++) {
+    uint32_t config_index;
+    if (!Read32(stream, &config_index)) {
+      return nullptr;
+    }
+    TargetValue value;
+    if (!Read16(stream, &unused2)
+        || !Read8(stream, &unused1)
+        || !Read8(stream, &value.data_type)
+        || !Read32(stream, &value.data_value)) {
+      return nullptr;
+    }
+    target_values.emplace_back(std::make_pair(config_index, value));
+  }
+
+  // Read the configurations
+  std::vector<ConfigDescription> configurations;
+  for (size_t i = 0; i < data->header_->GetConfigCount(); i++) {
+    ConfigDescription cd;
+    if (!stream.read(reinterpret_cast<char*>(&cd), sizeof(ConfigDescription))) {
+      return nullptr;
+    }
+    configurations.emplace_back(cd);
+  }
+
+  // Construct complete target inline entries
+  for (auto [target_entry, entry_offset, entry_count] : target_inline_entries) {
+    for(size_t i = 0; i < entry_count; i++) {
+      const auto& target_value = target_values[entry_offset + i];
+      const auto& config = configurations[target_value.first];
+      target_entry.values[config] = target_value.second;
+    }
+    data->target_inline_entries_.emplace_back(target_entry);
   }
 
   // Read the mapping of overlay resource id to target resource id.
@@ -278,12 +319,21 @@
 
   std::unique_ptr<IdmapData> data(new IdmapData());
   data->string_pool_data_ = resource_mapping.GetStringPoolData().to_string();
+  uint32_t inline_value_count = 0;
+  std::set<std::string> config_set;
   for (const auto& mapping : resource_mapping.GetTargetToOverlayMap()) {
     if (auto overlay_resource = std::get_if<ResourceId>(&mapping.second)) {
       data->target_entries_.push_back({mapping.first, *overlay_resource});
     } else {
-      data->target_inline_entries_.push_back(
-          {mapping.first, std::get<TargetValue>(mapping.second)});
+      std::map<ConfigDescription, TargetValue> values;
+      for (const auto& [config, value] : std::get<ConfigMap>(mapping.second)) {
+        config_set.insert(config);
+        ConfigDescription cd;
+        ConfigDescription::Parse(config, &cd);
+        values[cd] = value;
+        inline_value_count++;
+      }
+      data->target_inline_entries_.push_back({mapping.first, values});
     }
   }
 
@@ -295,6 +345,8 @@
   data_header->target_entry_count = static_cast<uint32_t>(data->target_entries_.size());
   data_header->target_entry_inline_count =
       static_cast<uint32_t>(data->target_inline_entries_.size());
+  data_header->target_entry_inline_value_count = inline_value_count;
+  data_header->config_count = config_set.size();
   data_header->overlay_entry_count = static_cast<uint32_t>(data->overlay_entries_.size());
   data_header->string_pool_index_offset = resource_mapping.GetStringPoolOffset();
   data->header_ = std::move(data_header);
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
index d10a278..a44fa75 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -94,14 +94,17 @@
   }
 
   for (auto& target_entry : data.GetTargetInlineEntries()) {
-    stream_ << TAB << base::StringPrintf("0x%08x -> ", target_entry.target_id)
-            << utils::DataTypeToString(target_entry.value.data_type);
+    for(auto iter = target_entry.values.begin(); iter != target_entry.values.end(); ++iter) {
+      auto value = iter->second;
+      stream_ << TAB << base::StringPrintf("0x%08x -> ", target_entry.target_id)
+              << utils::DataTypeToString(value.data_type);
 
-    if (target_entry.value.data_type == Res_value::TYPE_STRING) {
-      auto str = string_pool.stringAt(target_entry.value.data_value - string_pool_offset);
-      stream_ << " \"" << str.value_or(StringPiece16(u"")) << "\"";
-    } else {
-      stream_ << " " << base::StringPrintf("0x%08x", target_entry.value.data_value);
+      if (value.data_type == Res_value::TYPE_STRING) {
+        auto str = string_pool.stringAt(value.data_value - string_pool_offset);
+        stream_ << " \"" << str.value_or(StringPiece16(u"")) << "\"";
+      } else {
+        stream_ << " " << base::StringPrintf("0x%08x", value.data_value);
+      }
     }
 
     std::string target_name = kUnknownResourceName;
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index 779538c..3531cd7 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -89,22 +89,30 @@
       print(target_entry.target_id, "target id");
     }
 
+
     pad(sizeof(Res_value::size) + sizeof(Res_value::res0));
 
-    print(target_entry.value.data_type, "type: %s",
-          utils::DataTypeToString(target_entry.value.data_type).data());
+    for (auto& target_entry_value : target_entry.values) {
+      auto value = target_entry_value.second;
 
-    Result<std::string> overlay_name(Error(""));
-    if (overlay_ != nullptr &&
-        (target_entry.value.data_value == Res_value::TYPE_REFERENCE ||
-         target_entry.value.data_value == Res_value::TYPE_DYNAMIC_REFERENCE)) {
-      overlay_name = overlay_->GetResourceName(target_entry.value.data_value);
-    }
+      print(target_entry_value.first.to_string(), false, "config: %s",
+          target_entry_value.first.toString().string());
 
-    if (overlay_name) {
-      print(target_entry.value.data_value, "data: %s", overlay_name->c_str());
-    } else {
-      print(target_entry.value.data_value, "data");
+      print(value.data_type, "type: %s",
+            utils::DataTypeToString(value.data_type).data());
+
+      Result<std::string> overlay_name(Error(""));
+      if (overlay_ != nullptr &&
+          (value.data_value == Res_value::TYPE_REFERENCE ||
+           value.data_value == Res_value::TYPE_DYNAMIC_REFERENCE)) {
+        overlay_name = overlay_->GetResourceName(value.data_value);
+      }
+
+      if (overlay_name) {
+        print(value.data_value, "data: %s", overlay_name->c_str());
+      } else {
+        print(value.data_value, "data");
+      }
     }
   }
 
@@ -138,6 +146,8 @@
 void RawPrintVisitor::visit(const IdmapData::Header& header) {
   print(header.GetTargetEntryCount(), "target entry count");
   print(header.GetTargetInlineEntryCount(), "target inline entry count");
+  print(header.GetTargetInlineEntryValueCount(), "target inline entry value count");
+  print(header.GetConfigCount(), "config count");
   print(header.GetOverlayEntryCount(), "overlay entry count");
   print(header.GetStringPoolIndexOffset(), "string pool index offset");
 }
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
index 8ebe5aa4..bb31c11 100644
--- a/cmds/idmap2/libidmap2/ResourceMapping.cpp
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -161,14 +161,13 @@
 Result<Unit> ResourceMapping::AddMapping(
     ResourceId target_resource,
     const std::variant<OverlayData::ResourceIdValue, TargetValueWithConfig>& value) {
-  if (target_map_.find(target_resource) != target_map_.end()) {
-    return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
-  }
-
   // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
   // runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
 
   if (auto overlay_resource = std::get_if<OverlayData::ResourceIdValue>(&value)) {
+    if (target_map_.find(target_resource) != target_map_.end()) {
+      return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
+    }
     target_map_.insert(std::make_pair(target_resource, overlay_resource->overlay_id));
     if (overlay_resource->rewrite_id) {
       // An overlay resource can override multiple target resources at once. Rewrite the overlay
@@ -176,8 +175,18 @@
       overlay_map_.insert(std::make_pair(overlay_resource->overlay_id, target_resource));
     }
   } else {
-    auto overlay_value = std::get<TargetValueWithConfig>(value);
-    target_map_.insert(std::make_pair(target_resource, overlay_value.value));
+    auto[iter, inserted] = target_map_.try_emplace(target_resource, ConfigMap());
+    auto& resource_value = iter->second;
+    if (!inserted && std::holds_alternative<ResourceId>(resource_value)) {
+      return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
+    }
+    auto& config_map = std::get<ConfigMap>(resource_value);
+    const auto& config_value = std::get<TargetValueWithConfig>(value);
+    if (config_map.find(config_value.config) != config_map.end()) {
+      return Error(R"(target resource id "0x%08x" mapped to multiple values with the same config)",
+                   target_resource);
+    }
+    config_map.insert(config_value.to_pair());
   }
 
   return Unit{};