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/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index 58aff42..03e714a 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -21,42 +21,51 @@
  * header                     := magic version target_crc overlay_crc fulfilled_policies
  *                               enforce_overlayable target_path overlay_path overlay_name
  *                               debug_info
- * data                       := data_header target_entry* target_inline_entry* overlay_entry*
- *                               string_pool
- * data_header                := target_entry_count target_inline_entry_count overlay_entry_count
+ * data                       := data_header target_entry* target_inline_entry*
+                                 target_inline_entry_value* config* overlay_entry* string_pool
+ * data_header                := target_entry_count target_inline_entry_count
+                                 target_inline_entry_value_count config_count overlay_entry_count
  *                               string_pool_index
  * target_entry               := target_id overlay_id
- * target_inline_entry        := target_id Res_value::size padding(1) Res_value::type
+ * target_inline_entry        := target_id start_value_index value_count
+ * target_inline_entry_value  := config_index Res_value::size padding(1) Res_value::type
+ *                               Res_value::value
+ * config                     := target_id Res_value::size padding(1) Res_value::type
  *                               Res_value::value
  * overlay_entry              := overlay_id target_id
  *
- * debug_info                 := string
- * enforce_overlayable        := <uint32_t>
- * fulfilled_policies         := <uint32_t>
- * magic                      := <uint32_t>
- * overlay_crc                := <uint32_t>
- * overlay_entry_count        := <uint32_t>
- * overlay_id                 := <uint32_t>
- * overlay_package_id         := <uint8_t>
- * overlay_name               := string
- * overlay_path               := string
- * padding(n)                 := <uint8_t>[n]
- * Res_value::size            := <uint16_t>
- * Res_value::type            := <uint8_t>
- * Res_value::value           := <uint32_t>
- * string                     := <uint32_t> <uint8_t>+ padding(n)
- * string_pool                := string
- * string_pool_index          := <uint32_t>
- * string_pool_length         := <uint32_t>
- * target_crc                 := <uint32_t>
- * target_entry_count         := <uint32_t>
- * target_inline_entry_count  := <uint32_t>
- * target_id                  := <uint32_t>
- * target_package_id          := <uint8_t>
- * target_path                := string
- * value_type                 := <uint8_t>
- * value_data                 := <uint32_t>
- * version                    := <uint32_t>
+ * debug_info                       := string
+ * enforce_overlayable              := <uint32_t>
+ * fulfilled_policies               := <uint32_t>
+ * magic                            := <uint32_t>
+ * overlay_crc                      := <uint32_t>
+ * overlay_entry_count              := <uint32_t>
+ * overlay_id                       := <uint32_t>
+ * overlay_package_id               := <uint8_t>
+ * overlay_name                     := string
+ * overlay_path                     := string
+ * padding(n)                       := <uint8_t>[n]
+ * Res_value::size                  := <uint16_t>
+ * Res_value::type                  := <uint8_t>
+ * Res_value::value                 := <uint32_t>
+ * string                           := <uint32_t> <uint8_t>+ padding(n)
+ * string_pool                      := string
+ * string_pool_index                := <uint32_t>
+ * string_pool_length               := <uint32_t>
+ * target_crc                       := <uint32_t>
+ * target_entry_count               := <uint32_t>
+ * target_inline_entry_count        := <uint32_t>
+ * target_inline_entry_value_count  := <uint32_t>
+ * config_count                     := <uint32_t>
+ * config_index                     := <uint32_t>
+ * start_value_index                := <uint32_t>
+ * value_count                      := <uint32_t>
+ * target_id                        := <uint32_t>
+ * target_package_id                := <uint8_t>
+ * target_path                      := string
+ * value_type                       := <uint8_t>
+ * value_data                       := <uint32_t>
+ * version                          := <uint32_t>
  */
 
 #ifndef IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
@@ -70,6 +79,7 @@
 #include "android-base/macros.h"
 #include "androidfw/ResourceTypes.h"
 #include "androidfw/StringPiece.h"
+#include "androidfw/ConfigDescription.h"
 #include "idmap2/ResourceContainer.h"
 #include "idmap2/ResourceMapping.h"
 
@@ -165,19 +175,27 @@
    public:
     static std::unique_ptr<const Header> FromBinaryStream(std::istream& stream);
 
-    inline uint32_t GetTargetEntryCount() const {
+    [[nodiscard]] inline uint32_t GetTargetEntryCount() const {
       return target_entry_count;
     }
 
-    inline uint32_t GetTargetInlineEntryCount() const {
+    [[nodiscard]] inline uint32_t GetTargetInlineEntryCount() const {
       return target_entry_inline_count;
     }
 
-    inline uint32_t GetOverlayEntryCount() const {
+    [[nodiscard]] inline uint32_t GetTargetInlineEntryValueCount() const {
+      return target_entry_inline_value_count;
+    }
+
+    [[nodiscard]] inline uint32_t GetConfigCount() const {
+      return config_count;
+    }
+
+    [[nodiscard]] inline uint32_t GetOverlayEntryCount() const {
       return overlay_entry_count;
     }
 
-    inline uint32_t GetStringPoolIndexOffset() const {
+    [[nodiscard]] inline uint32_t GetStringPoolIndexOffset() const {
       return string_pool_index_offset;
     }
 
@@ -186,6 +204,8 @@
    private:
     uint32_t target_entry_count;
     uint32_t target_entry_inline_count;
+    uint32_t target_entry_inline_value_count;
+    uint32_t config_count;
     uint32_t overlay_entry_count;
     uint32_t string_pool_index_offset;
     Header() = default;
@@ -202,7 +222,7 @@
 
   struct TargetInlineEntry {
     ResourceId target_id;
-    TargetValue value;
+    std::map<ConfigDescription, TargetValue> values;
   };
 
   struct OverlayEntry {
@@ -227,11 +247,11 @@
     return target_inline_entries_;
   }
 
-  const std::vector<OverlayEntry>& GetOverlayEntries() const {
+  [[nodiscard]] const std::vector<OverlayEntry>& GetOverlayEntries() const {
     return overlay_entries_;
   }
 
-  const std::string& GetStringPoolData() const {
+  [[nodiscard]] const std::string& GetStringPoolData() const {
     return string_pool_data_;
   }
 
diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h
index 21862a36..4bad2fa 100644
--- a/cmds/idmap2/include/idmap2/ResourceMapping.h
+++ b/cmds/idmap2/include/idmap2/ResourceMapping.h
@@ -33,7 +33,8 @@
 using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
 
 namespace android::idmap2 {
-using TargetResourceMap = std::map<ResourceId, std::variant<ResourceId, TargetValue>>;
+using ConfigMap = std::unordered_map<std::string, TargetValue>;
+using TargetResourceMap = std::map<ResourceId, std::variant<ResourceId, ConfigMap>>;
 using OverlayResourceMap = std::map<ResourceId, ResourceId>;
 
 class ResourceMapping {
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index 8ec7496..af4dd89 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -46,6 +46,10 @@
 struct TargetValueWithConfig {
   TargetValue value;
   std::string config;
+
+  [[nodiscard]] std::pair<std::string, TargetValue> to_pair() const {
+    return std::make_pair(config, value);
+  }
 };
 
 namespace utils {
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{};
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
index bf63327..f1eeab9 100644
--- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -84,8 +84,10 @@
   const auto& target_inline_entries2 = data2->GetTargetInlineEntries();
   ASSERT_EQ(target_inline_entries1.size(), target_inline_entries2.size());
   ASSERT_EQ(target_inline_entries1[0].target_id, target_inline_entries2[0].target_id);
-  ASSERT_EQ(target_inline_entries1[0].value.data_type, target_inline_entries2[0].value.data_type);
-  ASSERT_EQ(target_inline_entries1[0].value.data_value, target_inline_entries2[0].value.data_value);
+  ASSERT_EQ(target_inline_entries1[0].values.begin()->second.data_type,
+      target_inline_entries2[0].values.begin()->second.data_type);
+  ASSERT_EQ(target_inline_entries1[0].values.begin()->second.data_value,
+      target_inline_entries2[0].values.begin()->second.data_value);
 
   const auto& overlay_entries1 = data1->GetOverlayEntries();
   const auto& overlay_entries2 = data2->GetOverlayEntries();
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index ee9a424..7b7dc17 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -47,10 +47,11 @@
   ASSERT_EQ((entry).target_id, (target_resid));                 \
   ASSERT_EQ((entry).overlay_id, (overlay_resid))
 
-#define ASSERT_TARGET_INLINE_ENTRY(entry, target_resid, expected_type, expected_value) \
-  ASSERT_EQ((entry).target_id, target_resid);                                          \
-  ASSERT_EQ((entry).value.data_type, (expected_type));                                 \
-  ASSERT_EQ((entry).value.data_value, (expected_value))
+#define ASSERT_TARGET_INLINE_ENTRY(entry, target_resid, ex_config, expected_type, expected_value) \
+  ASSERT_EQ((entry).target_id, target_resid);                                                     \
+  ASSERT_EQ((entry).values.begin()->first.to_string(), (ex_config));                              \
+  ASSERT_EQ((entry).values.begin()->second.data_type, (expected_type));                           \
+  ASSERT_EQ((entry).values.begin()->second.data_value, (expected_value))
 
 #define ASSERT_OVERLAY_ENTRY(entry, overlay_resid, target_resid) \
   ASSERT_EQ((entry).overlay_id, (overlay_resid));                \
@@ -67,7 +68,7 @@
   std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
   ASSERT_THAT(header, NotNull());
   ASSERT_EQ(header->GetMagic(), 0x504d4449U);
-  ASSERT_EQ(header->GetVersion(), 0x08U);
+  ASSERT_EQ(header->GetVersion(), 0x09U);
   ASSERT_EQ(header->GetTargetCrc(), 0x1234U);
   ASSERT_EQ(header->GetOverlayCrc(), 0x5678U);
   ASSERT_EQ(header->GetFulfilledPolicies(), 0x11);
@@ -122,8 +123,8 @@
 
   const auto& target_inline_entries = data->GetTargetInlineEntries();
   ASSERT_EQ(target_inline_entries.size(), 1U);
-  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000, Res_value::TYPE_INT_HEX,
-                             0x12345678);
+  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000,  "land-xxhdpi-v7",
+                             Res_value::TYPE_INT_HEX, 0x12345678);
 
   const auto& overlay_entries = data->GetOverlayEntries();
   ASSERT_EQ(target_entries.size(), 3U);
@@ -142,7 +143,7 @@
 
   ASSERT_THAT(idmap->GetHeader(), NotNull());
   ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
-  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x08U);
+  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x09U);
   ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U);
   ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U);
   ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), kIdmapRawDataPolicies);
@@ -165,8 +166,8 @@
 
   const auto& target_inline_entries = data->GetTargetInlineEntries();
   ASSERT_EQ(target_inline_entries.size(), 1U);
-  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000, Res_value::TYPE_INT_HEX,
-                             0x12345678);
+  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000,  "land-xxhdpi-v7",
+                             Res_value::TYPE_INT_HEX, 0x12345678);
 
   const auto& overlay_entries = data->GetOverlayEntries();
   ASSERT_EQ(target_entries.size(), 3U);
@@ -203,7 +204,7 @@
 
   ASSERT_THAT(idmap->GetHeader(), NotNull());
   ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
-  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x08U);
+  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x09U);
   ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC);
   ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC);
   ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), PolicyFlags::PUBLIC);
@@ -261,9 +262,9 @@
 
   auto frro = FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "test.target")
                   .SetOverlayable("TestResources")
-                  .SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U, "")
-                  .SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000, "")
-                  .SetResourceValue("string/str2", Res_value::TYPE_STRING, "foobar", "")
+                  .SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U, "land-xxhdpi-v7")
+                  .SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000, "land")
+                  .SetResourceValue("string/str2", Res_value::TYPE_STRING, "foobar", "xxhdpi-v7")
                   .Build();
 
   ASSERT_TRUE(frro);
@@ -295,11 +296,11 @@
 
   const auto& target_inline_entries = data->GetTargetInlineEntries();
   ASSERT_EQ(target_inline_entries.size(), 3U);
-  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1,
+  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1, "land-xxhdpi-v7",
                              Res_value::TYPE_INT_DEC, 2U);
-  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1,
+  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1, "land",
                              Res_value::TYPE_REFERENCE, 0x7f010000);
-  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[2], R::target::string::str2,
+  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[2], R::target::string::str2, "xxhdpi-v7",
                              Res_value::TYPE_STRING,
                              (uint32_t) (string_pool.indexOfString(u"foobar", 6)).value_or(-1));
 }
@@ -442,9 +443,9 @@
   constexpr size_t overlay_string_pool_size = 10U;
   const auto& target_inline_entries = data->GetTargetInlineEntries();
   ASSERT_EQ(target_inline_entries.size(), 2U);
-  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1,
+  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1, std::string(),
                              Res_value::TYPE_INT_DEC, 73U);  // -> 73
-  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1,
+  ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1, std::string(),
                              Res_value::TYPE_STRING,
                              overlay_string_pool_size + 0U);  // -> "Hello World"
 
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index a6371cb..7112eeb 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -64,7 +64,7 @@
   (*idmap)->accept(&visitor);
 
   ASSERT_CONTAINS_REGEX(ADDRESS "504d4449  magic\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "00000008  version\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "00000009  version\n", stream.str());
   ASSERT_CONTAINS_REGEX(
       StringPrintf(ADDRESS "%s  target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING),
       stream.str());
@@ -75,6 +75,8 @@
   ASSERT_CONTAINS_REGEX(ADDRESS "00000001  enforce overlayable\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000004  target entry count", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000000  target inline entry count", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "00000000  target inline entry value count", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "00000000  config count", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000004  overlay entry count", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "0000000a  string pool index offset", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "7f010000  target id: integer/int1", stream.str());
@@ -111,7 +113,7 @@
   (*idmap)->accept(&visitor);
 
   ASSERT_CONTAINS_REGEX(ADDRESS "504d4449  magic\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "00000008  version\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "00000009  version\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00001234  target crc\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00005678  overlay crc\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000011  fulfilled policies: public|signature\n", stream.str());
@@ -124,17 +126,25 @@
   ASSERT_CONTAINS_REGEX(ADDRESS "........  overlay name: OverlayName\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000003  target entry count\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000001  target inline entry count\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "00000001  target inline entry value count", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "00000001  config count", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000003  overlay entry count\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000000  string pool index offset\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "7f020000  target id\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "7f020000  overlay id\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "7f020000  target id\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "7f030000  target id\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "7f030000  overlay id\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "7f030002  target id\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "7f030001  overlay id\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "7f040000  target id\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "0000000e  config: land-xxhdpi-v7 size\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "........  config: land-xxhdpi-v7\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "      11  type: integer\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "12345678  data\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "7f020000  overlay id\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "7f030002  target id\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000004  string pool size\n", stream.str());
-  ASSERT_CONTAINS_REGEX("000000a4: ........  string pool\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "........  string pool\n", stream.str());
 }
 
 }  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
index ca9a444..016d427 100644
--- a/cmds/idmap2/tests/ResourceMappingTests.cpp
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -111,19 +111,20 @@
     return Error("Failed to find mapping for target resource");
   }
 
-  auto actual_overlay_value = std::get_if<TargetValue>(&entry_map->second);
-  if (actual_overlay_value == nullptr) {
+  auto config_map = std::get_if<ConfigMap>(&entry_map->second);
+  if (config_map == nullptr || config_map->empty()) {
     return Error("Target resource is not mapped to an inline value");
   }
+  auto actual_overlay_value = config_map->begin()->second;
 
-  if (actual_overlay_value->data_type != type) {
+  if (actual_overlay_value.data_type != type) {
     return Error(R"(Expected type: "0x%02x" Actual type: "0x%02x")", type,
-                 actual_overlay_value->data_type);
+                 actual_overlay_value.data_type);
   }
 
-  if (actual_overlay_value->data_value != value) {
+  if (actual_overlay_value.data_value != value) {
     return Error(R"(Expected value: "0x%08x" Actual value: "0x%08x")", type,
-                 actual_overlay_value->data_value);
+                 actual_overlay_value.data_value);
   }
 
   return Result<Unit>({});
diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h
index 6b5f3a8..45740e2 100644
--- a/cmds/idmap2/tests/TestHelpers.h
+++ b/cmds/idmap2/tests/TestHelpers.h
@@ -30,7 +30,7 @@
     0x49, 0x44, 0x4d, 0x50,
 
     // 0x4: version
-    0x08, 0x00, 0x00, 0x00,
+    0x09, 0x00, 0x00, 0x00,
 
     // 0x8: target crc
     0x34, 0x12, 0x00, 0x00,
@@ -76,66 +76,123 @@
     // 0x58: target_inline_entry_count
     0x01, 0x00, 0x00, 0x00,
 
-    // 0x5c: overlay_entry_count
+    // 0x5c: target_inline_entry_value_count
+    0x01, 0x00, 0x00, 0x00,
+
+    // 0x60: config_count
+    0x01, 0x00, 0x00, 0x00,
+
+    // 0x64: overlay_entry_count
     0x03, 0x00, 0x00, 0x00,
 
-    // 0x60: string_pool_offset
+    // 0x68: string_pool_offset
     0x00, 0x00, 0x00, 0x00,
 
     // TARGET ENTRIES
-    // 0x64: target id (0x7f020000)
+    // 0x6c: target id (0x7f020000)
     0x00, 0x00, 0x02, 0x7f,
 
-    // 0x68: overlay_id (0x7f020000)
+    // 0x70: overlay_id (0x7f020000)
     0x00, 0x00, 0x02, 0x7f,
 
-    // 0x6c: target id (0x7f030000)
+    // 0x74: target id (0x7f030000)
     0x00, 0x00, 0x03, 0x7f,
 
-    // 0x70: overlay_id (0x7f030000)
+    // 0x78: overlay_id (0x7f030000)
     0x00, 0x00, 0x03, 0x7f,
 
-    // 0x74: target id (0x7f030002)
+    // 0x7c: target id (0x7f030002)
     0x02, 0x00, 0x03, 0x7f,
 
-    // 0x78: overlay_id (0x7f030001)
+    // 0x80: overlay_id (0x7f030001)
     0x01, 0x00, 0x03, 0x7f,
 
     // INLINE TARGET ENTRIES
 
-    // 0x7c: target_id
+    // 0x84: target_id
     0x00, 0x00, 0x04, 0x7f,
 
-    // 0x80: Res_value::size (value ignored by idmap)
+    // 0x88: start value index
+    0x00, 0x00, 0x00, 0x00,
+
+    // 0x8c: value count
+    0x01, 0x00, 0x00, 0x00,
+
+    // INLINE TARGET ENTRY VALUES
+
+    // 0x90: config index
+    0x00, 0x00, 0x00, 0x00,
+
+    // 0x94: Res_value::size (value ignored by idmap)
     0x08, 0x00,
 
-    // 0x82: Res_value::res0 (value ignored by idmap)
+    // 0x98: Res_value::res0 (value ignored by idmap)
     0x00,
 
-    // 0x83: Res_value::dataType (TYPE_INT_HEX)
+    // 0x9c: Res_value::dataType (TYPE_INT_HEX)
     0x11,
 
-    // 0x84: Res_value::data
+    // 0xa0: Res_value::data
     0x78, 0x56, 0x34, 0x12,
 
+    // CONFIGURATIONS
+
+    // 0xa4: ConfigDescription
+    // size
+    0x40, 0x00, 0x00, 0x00,
+    // 0xa8: imsi
+    0x00, 0x00, 0x00, 0x00,
+    // 0xac: locale
+    0x00, 0x00, 0x00, 0x00,
+    // 0xb0: screenType
+    0x02, 0x00, 0xe0, 0x01,
+    // 0xb4: input
+    0x00, 0x00, 0x00, 0x00,
+    // 0xb8: screenSize
+    0x00, 0x00, 0x00, 0x00,
+    // 0xbc: version
+    0x07, 0x00, 0x00, 0x00,
+    // 0xc0: screenConfig
+    0x00, 0x00, 0x00, 0x00,
+    // 0xc4: screenSizeDp
+    0x00, 0x00, 0x00, 0x00,
+    // 0xc8: localeScript
+    0x00, 0x00, 0x00, 0x00,
+    // 0xcc: localVariant(1)
+    0x00, 0x00, 0x00, 0x00,
+    // 0xd0: localVariant(2)
+    0x00, 0x00, 0x00, 0x00,
+    // 0xd4: screenConfig2
+    0x00, 0x00, 0x00, 0x00,
+    // 0xd8: localeScriptWasComputed
+    0x00,
+    // 0xd9: localeNumberingSystem(1)
+    0x00, 0x00, 0x00, 0x00,
+    // 0xdd: localeNumberingSystem(2)
+    0x00, 0x00, 0x00, 0x00,
+
+    // 0xe1: padding
+    0x00, 0x00, 0x00,
+
+
     // OVERLAY ENTRIES
-    // 0x88: 0x7f020000 -> 0x7f020000
+    // 0xe4: 0x7f020000 -> 0x7f020000
     0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f,
 
-    // 0x90: 0x7f030000 -> 0x7f030000
+    // 0xec: 0x7f030000 -> 0x7f030000
     0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f,
 
-    // 0x98: 0x7f030001 -> 0x7f030002
+    // 0xf4: 0x7f030001 -> 0x7f030002
     0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f,
 
-    // 0xa0: string pool
+    // 0xfc: string pool
     // string length,
     0x04, 0x00, 0x00, 0x00,
 
-    // 0xa4 string contents "test"
+    // 0x100 string contents "test"
     0x74, 0x65, 0x73, 0x74};
 
-const unsigned int kIdmapRawDataLen = 0xa8;
+const unsigned int kIdmapRawDataLen = 0x104;
 const unsigned int kIdmapRawDataOffset = 0x54;
 const unsigned int kIdmapRawDataTargetCrc = 0x1234;
 const unsigned int kIdmapRawOverlayCrc = 0x5678;
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 39c7d19..235700b 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -601,10 +601,6 @@
     return base::unexpected(result.error());
   }
 
-  if (type_idx == 0x1c) {
-    LOG(ERROR) << base::StringPrintf("foobar first result %s", result->package_name->c_str());
-  }
-
   bool overlaid = false;
   if (!stop_at_first_match && !ignore_configuration && !apk_assets_[result->cookie]->IsLoader()) {
     for (const auto& id_map : package_group.overlays_) {
@@ -615,7 +611,21 @@
       }
       if (overlay_entry.IsInlineValue()) {
         // The target resource is overlaid by an inline value not represented by a resource.
-        result->entry = overlay_entry.GetInlineValue();
+        ConfigDescription best_frro_config;
+        Res_value best_frro_value;
+        bool frro_found = false;
+        for( const auto& [config, value] : overlay_entry.GetInlineValue()) {
+          if ((!frro_found || config.isBetterThan(best_frro_config, desired_config))
+              && config.match(*desired_config)) {
+            frro_found = true;
+            best_frro_config = config;
+            best_frro_value = value;
+          }
+        }
+        if (!frro_found) {
+          continue;
+        }
+        result->entry = best_frro_value;
         result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
         result->cookie = id_map.cookie;
 
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index efd1f6a..e122d48 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -56,6 +56,8 @@
 struct Idmap_data_header {
   uint32_t target_entry_count;
   uint32_t target_inline_entry_count;
+  uint32_t target_inline_entry_value_count;
+  uint32_t configuration_count;
   uint32_t overlay_entry_count;
 
   uint32_t string_pool_index_offset;
@@ -68,6 +70,12 @@
 
 struct Idmap_target_entry_inline {
   uint32_t target_id;
+  uint32_t start_value_index;
+  uint32_t value_count;
+};
+
+struct Idmap_target_entry_inline_value {
+  uint32_t config_index;
   Res_value value;
 };
 
@@ -138,11 +146,15 @@
 IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
                          const Idmap_target_entry* entries,
                          const Idmap_target_entry_inline* inline_entries,
+                         const Idmap_target_entry_inline_value* inline_entry_values,
+                         const ConfigDescription* configs,
                          uint8_t target_assigned_package_id,
                          const OverlayDynamicRefTable* overlay_ref_table)
     : data_header_(data_header),
       entries_(entries),
       inline_entries_(inline_entries),
+      inline_entry_values_(inline_entry_values),
+      configurations_(configs),
       target_assigned_package_id_(target_assigned_package_id),
       overlay_ref_table_(overlay_ref_table) { }
 
@@ -183,7 +195,13 @@
 
   if (inline_entry != end_inline_entry &&
       (0x00FFFFFFU & dtohl(inline_entry->target_id)) == target_res_id) {
-    return Result(inline_entry->value);
+    std::map<ConfigDescription, Res_value> values_map;
+    for (int i = 0; i < inline_entry->value_count; i++) {
+      const auto& value = inline_entry_values_[inline_entry->start_value_index + i];
+      const auto& config = configurations_[value.config_index];
+      values_map[config] = value.value;
+    }
+    return Result(values_map);
   }
   return {};
 }
@@ -237,6 +255,8 @@
                          const Idmap_data_header* data_header,
                          const Idmap_target_entry* target_entries,
                          const Idmap_target_entry_inline* target_inline_entries,
+                         const Idmap_target_entry_inline_value* inline_entry_values,
+                         const ConfigDescription* configs,
                          const Idmap_overlay_entry* overlay_entries,
                          std::unique_ptr<ResStringPool>&& string_pool,
                          std::string_view overlay_apk_path,
@@ -245,6 +265,8 @@
        data_header_(data_header),
        target_entries_(target_entries),
        target_inline_entries_(target_inline_entries),
+       inline_entry_values_(inline_entry_values),
+       configurations_(configs),
        overlay_entries_(overlay_entries),
        string_pool_(std::move(string_pool)),
        idmap_path_(std::move(idmap_path)),
@@ -303,6 +325,21 @@
   if (target_inline_entries == nullptr) {
     return {};
   }
+
+  auto target_inline_entry_values = ReadType<Idmap_target_entry_inline_value>(
+      &data_ptr, &data_size, "target inline values",
+      dtohl(data_header->target_inline_entry_value_count));
+  if (target_inline_entry_values == nullptr) {
+    return {};
+  }
+
+  auto configurations = ReadType<ConfigDescription>(
+      &data_ptr, &data_size, "configurations",
+      dtohl(data_header->configuration_count));
+  if (configurations == nullptr) {
+    return {};
+  }
+
   auto overlay_entries = ReadType<Idmap_overlay_entry>(&data_ptr, &data_size, "target inline",
                                                        dtohl(data_header->overlay_entry_count));
   if (overlay_entries == nullptr) {
@@ -329,8 +366,8 @@
   // Can't use make_unique because LoadedIdmap constructor is private.
   return std::unique_ptr<LoadedIdmap>(
       new LoadedIdmap(idmap_path.to_string(), header, data_header, target_entries,
-                      target_inline_entries, overlay_entries, std::move(idmap_string_pool),
-                      *target_path, *overlay_path));
+                      target_inline_entries, target_inline_entry_values, configurations,
+                      overlay_entries, std::move(idmap_string_pool), *target_path, *overlay_path));
 }
 
 bool LoadedIdmap::IsUpToDate() const {
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index 6804472..a1cbbbf 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -23,6 +23,7 @@
 #include <variant>
 
 #include "android-base/macros.h"
+#include "androidfw/ConfigDescription.h"
 #include "androidfw/StringPiece.h"
 #include "androidfw/ResourceTypes.h"
 #include "utils/ByteOrder.h"
@@ -35,6 +36,7 @@
 struct Idmap_data_header;
 struct Idmap_target_entry;
 struct Idmap_target_entry_inline;
+struct Idmap_target_entry_inline_value;
 struct Idmap_overlay_entry;
 
 // A string pool for overlay apk assets. The string pool holds the strings of the overlay resources
@@ -91,7 +93,8 @@
    public:
     Result() = default;
     explicit Result(uint32_t value) : data_(value) {};
-    explicit Result(const Res_value& value) : data_(value) { };
+    explicit Result(const std::map<ConfigDescription, Res_value> &value)
+        : data_(value) { };
 
     // Returns `true` if the resource is overlaid.
     explicit operator bool() const {
@@ -107,15 +110,16 @@
     }
 
     bool IsInlineValue() const {
-      return std::get_if<Res_value>(&data_) != nullptr;
+      return std::get_if<2>(&data_) != nullptr;
     }
 
-    const Res_value& GetInlineValue() const {
-      return std::get<Res_value>(data_);
+    const std::map<ConfigDescription, Res_value>& GetInlineValue() const {
+      return std::get<2>(data_);
     }
 
    private:
-      std::variant<std::monostate, uint32_t, Res_value> data_;
+      std::variant<std::monostate, uint32_t,
+          std::map<ConfigDescription, Res_value> > data_;
   };
 
   // Looks up the value that overlays the target resource id.
@@ -129,12 +133,16 @@
   explicit IdmapResMap(const Idmap_data_header* data_header,
                        const Idmap_target_entry* entries,
                        const Idmap_target_entry_inline* inline_entries,
+                       const Idmap_target_entry_inline_value* inline_entry_values,
+                       const ConfigDescription* configs,
                        uint8_t target_assigned_package_id,
                        const OverlayDynamicRefTable* overlay_ref_table);
 
   const Idmap_data_header* data_header_;
   const Idmap_target_entry* entries_;
   const Idmap_target_entry_inline* inline_entries_;
+  const Idmap_target_entry_inline_value* inline_entry_values_;
+  const ConfigDescription* configurations_;
   const uint8_t target_assigned_package_id_;
   const OverlayDynamicRefTable* overlay_ref_table_;
 
@@ -170,8 +178,8 @@
   // Returns a mapping from target resource ids to overlay values.
   const IdmapResMap GetTargetResourcesMap(uint8_t target_assigned_package_id,
                                           const OverlayDynamicRefTable* overlay_ref_table) const {
-    return IdmapResMap(data_header_, target_entries_, target_inline_entries_,
-                       target_assigned_package_id, overlay_ref_table);
+    return IdmapResMap(data_header_, target_entries_, target_inline_entries_, inline_entry_values_,
+                       configurations_, target_assigned_package_id, overlay_ref_table);
   }
 
   // Returns a dynamic reference table for a loaded overlay package.
@@ -191,6 +199,8 @@
   const Idmap_data_header* data_header_;
   const Idmap_target_entry* target_entries_;
   const Idmap_target_entry_inline* target_inline_entries_;
+  const Idmap_target_entry_inline_value* inline_entry_values_;
+  const ConfigDescription* configurations_;
   const Idmap_overlay_entry* overlay_entries_;
   const std::unique_ptr<ResStringPool> string_pool_;
 
@@ -207,6 +217,8 @@
                        const Idmap_data_header* data_header,
                        const Idmap_target_entry* target_entries,
                        const Idmap_target_entry_inline* target_inline_entries,
+                       const Idmap_target_entry_inline_value* inline_entry_values_,
+                       const ConfigDescription* configs,
                        const Idmap_overlay_entry* overlay_entries,
                        std::unique_ptr<ResStringPool>&& string_pool,
                        std::string_view overlay_apk_path,
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 8c614bc..9309091 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -45,7 +45,7 @@
 namespace android {
 
 constexpr const uint32_t kIdmapMagic = 0x504D4449u;
-constexpr const uint32_t kIdmapCurrentVersion = 0x00000008u;
+constexpr const uint32_t kIdmapCurrentVersion = 0x00000009u;
 
 // This must never change.
 constexpr const uint32_t kFabricatedOverlayMagic = 0x4f525246; // FRRO (big endian)
@@ -1098,7 +1098,7 @@
         SDKVERSION_ANY = 0
     };
     
-  enum {
+    enum {
         MINORVERSION_ANY = 0
     };
     
diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap
index 88eadcc..8e847e8 100644
--- a/libs/androidfw/tests/data/overlay/overlay.idmap
+++ b/libs/androidfw/tests/data/overlay/overlay.idmap
Binary files differ
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 7159673..be8647e 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -1098,6 +1098,7 @@
         private void enforceActor(@NonNull OverlayIdentifier overlay, @NonNull String methodName,
                 int realUserId) throws SecurityException {
             OverlayInfo overlayInfo = mImpl.getOverlayInfo(overlay, realUserId);
+
             if (overlayInfo == null) {
                 throw new IllegalArgumentException("Unable to retrieve overlay information for "
                         + overlay);
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index dade7aa..6e95cd6 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -519,6 +519,7 @@
             throws OperationFailedException {
         final OverlayIdentifier overlayIdentifier = new OverlayIdentifier(
                 info.packageName, info.overlayName);
+
         final Set<PackageAndUser> updatedTargets = new ArraySet<>();
         OverlayInfo oi = mSettings.getNullableOverlayInfo(overlayIdentifier, userId);
         if (oi != null) {