Parse <overlay> and abstract resource mapping

This change introduces idmap parsing of <overlay> tags.

The <overlay> tag allows one to explicitly map resources in the target
to either a resource in the overlay or an inline attribute value.

Use the android:resourcesMap atttribute on the <overlay> tag in the
android manifest to specify a file to provide the resource mapping.

Bug: 135943783
Bug: 135051420
Test: idmap2_tests
Change-Id: I1740dcdc01849c43b1f2cb8c6645d666dbb05dba
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 4649675..389ade5 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -30,6 +30,7 @@
 #include "android-base/macros.h"
 #include "android-base/stringprintf.h"
 #include "androidfw/AssetManager2.h"
+#include "idmap2/ResourceMapping.h"
 #include "idmap2/ResourceUtils.h"
 #include "idmap2/Result.h"
 #include "idmap2/SysTrace.h"
@@ -41,10 +42,6 @@
 
 namespace {
 
-#define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16)
-
-#define EXTRACT_ENTRY(resid) (0x0000ffff & (resid))
-
 class MatchingResources {
  public:
   void Add(ResourceId target_resid, ResourceId overlay_resid) {
@@ -97,28 +94,6 @@
   return true;
 }
 
-ResourceId NameToResid(const AssetManager2& am, const std::string& name) {
-  return am.GetResourceId(name);
-}
-
-// TODO(martenkongstad): scan for package name instead of assuming package at index 0
-//
-// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
-// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
-// this assumption tends to work out. That said, the correct thing to do is to scan
-// resources.arsc for a package with a given name as read from the package manifest instead of
-// relying on a hard-coded index. This however requires storing the package name in the idmap
-// header, which in turn requires incrementing the idmap version. Because the initial version of
-// idmap2 is compatible with idmap, this will have to wait for now.
-const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
-  const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages();
-  if (packages.empty()) {
-    return nullptr;
-  }
-  int id = packages[0]->GetPackageId();
-  return loaded_arsc.GetPackageById(id);
-}
-
 Result<uint32_t> GetCrc(const ZipFile& zip) {
   const Result<uint32_t> a = zip.Crc("resources.arsc");
   const Result<uint32_t> b = zip.Crc("AndroidManifest.xml");
@@ -266,95 +241,57 @@
   return {std::move(idmap)};
 }
 
-std::string ConcatPolicies(const std::vector<std::string>& policies) {
-  std::string message;
-  for (const std::string& policy : policies) {
-    if (!message.empty()) {
-      message.append("|");
+Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping(
+    const ResourceMapping& resource_mapping) {
+  if (resource_mapping.GetTargetToOverlayMap().empty()) {
+    return Error("no resources were overlaid");
+  }
+
+  MatchingResources matching_resources;
+  for (const auto mapping : resource_mapping.GetTargetToOverlayMap()) {
+    if (mapping.second.data_type != Res_value::TYPE_REFERENCE) {
+      // The idmap format must change to support non-references.
+      continue;
     }
-    message.append(policy);
+
+    matching_resources.Add(mapping.first, mapping.second.data_value);
   }
 
-  return message;
+  // encode idmap data
+  std::unique_ptr<IdmapData> data(new IdmapData());
+  const auto types_end = matching_resources.Map().cend();
+  for (auto ti = matching_resources.Map().cbegin(); ti != types_end; ++ti) {
+    auto ei = ti->second.cbegin();
+    std::unique_ptr<IdmapData::TypeEntry> type(new IdmapData::TypeEntry());
+    type->target_type_id_ = EXTRACT_TYPE(ei->first);
+    type->overlay_type_id_ = EXTRACT_TYPE(ei->second);
+    type->entry_offset_ = EXTRACT_ENTRY(ei->first);
+    EntryId last_target_entry = kNoEntry;
+    for (; ei != ti->second.cend(); ++ei) {
+      if (last_target_entry != kNoEntry) {
+        int count = EXTRACT_ENTRY(ei->first) - last_target_entry - 1;
+        type->entries_.insert(type->entries_.end(), count, kNoEntry);
+      }
+      type->entries_.push_back(EXTRACT_ENTRY(ei->second));
+      last_target_entry = EXTRACT_ENTRY(ei->first);
+    }
+    data->type_entries_.push_back(std::move(type));
+  }
+
+  std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
+  data_header->target_package_id_ = resource_mapping.GetTargetPackageId();
+  data_header->type_count_ = data->type_entries_.size();
+  data->header_ = std::move(data_header);
+  return {std::move(data)};
 }
 
-Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
-                              const utils::OverlayManifestInfo& overlay_info,
-                              const PolicyBitmask& fulfilled_policies, const ResourceId& resid) {
-  static constexpr const PolicyBitmask sDefaultPolicies =
-      PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
-      PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION |
-      PolicyFlags::POLICY_PRODUCT_PARTITION | PolicyFlags::POLICY_SIGNATURE;
-
-  // If the resource does not have an overlayable definition, allow the resource to be overlaid if
-  // the overlay is preinstalled or signed with the same signature as the target.
-  if (!target_package.DefinesOverlayable()) {
-    return (sDefaultPolicies & fulfilled_policies) != 0
-               ? Result<Unit>({})
-               : Error(
-                     "overlay must be preinstalled or signed with the same signature as the "
-                     "target");
-  }
-
-  const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(resid);
-  if (overlayable_info == nullptr) {
-    // Do not allow non-overlayable resources to be overlaid.
-    return Error("resource has no overlayable declaration");
-  }
-
-  if (overlay_info.target_name != overlayable_info->name) {
-    // If the overlay supplies a target overlayable name, the resource must belong to the
-    // overlayable defined with the specified name to be overlaid.
-    return Error("<overlay> android:targetName '%s' does not match overlayable name '%s'",
-                 overlay_info.target_name.c_str(), overlayable_info->name.c_str());
-  }
-
-  // Enforce policy restrictions if the resource is declared as overlayable.
-  if ((overlayable_info->policy_flags & fulfilled_policies) == 0) {
-    return Error("overlay with policies '%s' does not fulfill any overlayable policies '%s'",
-                 ConcatPolicies(BitmaskToPolicies(fulfilled_policies)).c_str(),
-                 ConcatPolicies(BitmaskToPolicies(overlayable_info->policy_flags)).c_str());
-  }
-
-  return Result<Unit>({});
-}
-
-Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& target_apk_path,
-                                                          const ApkAssets& target_apk_assets,
-                                                          const std::string& overlay_apk_path,
+Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const ApkAssets& target_apk_assets,
                                                           const ApkAssets& overlay_apk_assets,
                                                           const PolicyBitmask& fulfilled_policies,
                                                           bool enforce_overlayable) {
   SYSTRACE << "Idmap::FromApkAssets";
-  AssetManager2 target_asset_manager;
-  if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true, false)) {
-    return Error("failed to create target asset manager");
-  }
-
-  AssetManager2 overlay_asset_manager;
-  if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets}, true, false)) {
-    return Error("failed to create overlay asset manager");
-  }
-
-  const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
-  if (target_arsc == nullptr) {
-    return Error("failed to load target resources.arsc");
-  }
-
-  const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
-  if (overlay_arsc == nullptr) {
-    return Error("failed to load overlay resources.arsc");
-  }
-
-  const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
-  if (target_pkg == nullptr) {
-    return Error("failed to load target package from resources.arsc");
-  }
-
-  const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
-  if (overlay_pkg == nullptr) {
-    return Error("failed to load overlay package from resources.arsc");
-  }
+  const std::string& target_apk_path = target_apk_assets.GetPath();
+  const std::string& overlay_apk_path = overlay_apk_assets.GetPath();
 
   const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_apk_path);
   if (!target_zip) {
@@ -366,11 +303,6 @@
     return Error("failed to open overlay as zip");
   }
 
-  auto overlay_info = utils::ExtractOverlayManifestInfo(overlay_apk_path);
-  if (!overlay_info) {
-    return overlay_info.GetError();
-  }
-
   std::unique_ptr<IdmapHeader> header(new IdmapHeader());
   header->magic_ = kIdmapMagic;
   header->version_ = kIdmapCurrentVersion;
@@ -395,7 +327,7 @@
   memcpy(header->target_path_, target_apk_path.data(), target_apk_path.size());
 
   if (overlay_apk_path.size() > sizeof(header->overlay_path_)) {
-    return Error("overlay apk path \"%s\" longer than maximum size %zu", target_apk_path.c_str(),
+    return Error("overlay apk path \"%s\" longer than maximum size %zu", overlay_apk_path.c_str(),
                  sizeof(header->target_path_));
   }
   memset(header->overlay_path_, 0, sizeof(header->overlay_path_));
@@ -404,70 +336,24 @@
   std::unique_ptr<Idmap> idmap(new Idmap());
   idmap->header_ = std::move(header);
 
-  // find the resources that exist in both packages
-  MatchingResources matching_resources;
-  const auto end = overlay_pkg->end();
-  for (auto iter = overlay_pkg->begin(); iter != end; ++iter) {
-    const ResourceId overlay_resid = *iter;
-    Result<std::string> name = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid);
-    if (!name) {
-      continue;
-    }
-    // prepend "<package>:" to turn name into "<package>:<type>/<name>"
-    const std::string full_name =
-        base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name->c_str());
-    const ResourceId target_resid = NameToResid(target_asset_manager, full_name);
-    if (target_resid == 0) {
-      continue;
-    }
-
-    if (enforce_overlayable) {
-      Result<Unit> success =
-          CheckOverlayable(*target_pkg, *overlay_info, fulfilled_policies, target_resid);
-      if (!success) {
-        LOG(WARNING) << "overlay \"" << overlay_apk_path
-                     << "\" is not allowed to overlay resource \"" << full_name
-                     << "\": " << success.GetErrorMessage();
-        continue;
-      }
-    }
-
-    matching_resources.Add(target_resid, overlay_resid);
+  auto overlay_info = utils::ExtractOverlayManifestInfo(overlay_apk_path);
+  if (!overlay_info) {
+    return overlay_info.GetError();
   }
 
-  if (matching_resources.Map().empty()) {
-    return Error("overlay \"%s\" does not successfully overlay any resource",
-                 overlay_apk_path.c_str());
+  auto resource_mapping =
+      ResourceMapping::FromApkAssets(target_apk_assets, overlay_apk_assets, *overlay_info,
+                                     fulfilled_policies, enforce_overlayable);
+  if (!resource_mapping) {
+    return resource_mapping.GetError();
   }
 
-  // encode idmap data
-  std::unique_ptr<IdmapData> data(new IdmapData());
-  const auto types_end = matching_resources.Map().cend();
-  for (auto ti = matching_resources.Map().cbegin(); ti != types_end; ++ti) {
-    auto ei = ti->second.cbegin();
-    std::unique_ptr<IdmapData::TypeEntry> type(new IdmapData::TypeEntry());
-    type->target_type_id_ = EXTRACT_TYPE(ei->first);
-    type->overlay_type_id_ = EXTRACT_TYPE(ei->second);
-    type->entry_offset_ = EXTRACT_ENTRY(ei->first);
-    EntryId last_target_entry = kNoEntry;
-    for (; ei != ti->second.cend(); ++ei) {
-      if (last_target_entry != kNoEntry) {
-        int count = EXTRACT_ENTRY(ei->first) - last_target_entry - 1;
-        type->entries_.insert(type->entries_.end(), count, kNoEntry);
-      }
-      type->entries_.push_back(EXTRACT_ENTRY(ei->second));
-      last_target_entry = EXTRACT_ENTRY(ei->first);
-    }
-    data->type_entries_.push_back(std::move(type));
+  auto idmap_data = IdmapData::FromResourceMapping(*resource_mapping);
+  if (!idmap_data) {
+    return idmap_data.GetError();
   }
 
-  std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
-  data_header->target_package_id_ = target_pkg->GetPackageId();
-  data_header->type_count_ = data->type_entries_.size();
-  data->header_ = std::move(data_header);
-
-  idmap->data_.push_back(std::move(data));
-
+  idmap->data_.push_back(std::move(*idmap_data));
   return {std::move(idmap)};
 }
 
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
new file mode 100644
index 0000000..95ae626
--- /dev/null
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2019 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 "idmap2/ResourceMapping.h"
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "android-base/stringprintf.h"
+#include "idmap2/ResourceUtils.h"
+
+using android::base::StringPrintf;
+using android::idmap2::utils::ResToTypeEntryName;
+
+namespace android::idmap2 {
+
+namespace {
+
+#define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24)
+
+std::string ConcatPolicies(const std::vector<std::string>& policies) {
+  std::string message;
+  for (const std::string& policy : policies) {
+    if (!message.empty()) {
+      message.append("|");
+    }
+    message.append(policy);
+  }
+
+  return message;
+}
+
+Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
+                              const OverlayManifestInfo& overlay_info,
+                              const PolicyBitmask& fulfilled_policies,
+                              const ResourceId& target_resource) {
+  static constexpr const PolicyBitmask sDefaultPolicies =
+      PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
+      PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION |
+      PolicyFlags::POLICY_PRODUCT_PARTITION | PolicyFlags::POLICY_SIGNATURE;
+
+  // If the resource does not have an overlayable definition, allow the resource to be overlaid if
+  // the overlay is preinstalled or signed with the same signature as the target.
+  if (!target_package.DefinesOverlayable()) {
+    return (sDefaultPolicies & fulfilled_policies) != 0
+               ? Result<Unit>({})
+               : Error(
+                     "overlay must be preinstalled or signed with the same signature as the "
+                     "target");
+  }
+
+  const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource);
+  if (overlayable_info == nullptr) {
+    // Do not allow non-overlayable resources to be overlaid.
+    return Error("target resource has no overlayable declaration");
+  }
+
+  if (overlay_info.target_name != overlayable_info->name) {
+    // If the overlay supplies a target overlayable name, the resource must belong to the
+    // overlayable defined with the specified name to be overlaid.
+    return Error(R"(<overlay> android:targetName "%s" does not match overlayable name "%s")",
+                 overlay_info.target_name.c_str(), overlayable_info->name.c_str());
+  }
+
+  // Enforce policy restrictions if the resource is declared as overlayable.
+  if ((overlayable_info->policy_flags & fulfilled_policies) == 0) {
+    return Error(R"(overlay with policies "%s" does not fulfill any overlayable policies "%s")",
+                 ConcatPolicies(BitmaskToPolicies(fulfilled_policies)).c_str(),
+                 ConcatPolicies(BitmaskToPolicies(overlayable_info->policy_flags)).c_str());
+  }
+
+  return Result<Unit>({});
+}
+
+// TODO(martenkongstad): scan for package name instead of assuming package at index 0
+//
+// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
+// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
+// this assumption tends to work out. That said, the correct thing to do is to scan
+// resources.arsc for a package with a given name as read from the package manifest instead of
+// relying on a hard-coded index. This however requires storing the package name in the idmap
+// header, which in turn requires incrementing the idmap version. Because the initial version of
+// idmap2 is compatible with idmap, this will have to wait for now.
+const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
+  const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages();
+  if (packages.empty()) {
+    return nullptr;
+  }
+  int id = packages[0]->GetPackageId();
+  return loaded_arsc.GetPackageById(id);
+}
+
+Result<std::unique_ptr<Asset>> OpenNonAssetFromResource(const ResourceId& resource_id,
+                                                        const AssetManager2& asset_manager) {
+  Res_value value{};
+  ResTable_config selected_config{};
+  uint32_t flags;
+  auto cookie =
+      asset_manager.GetResource(resource_id, /* may_be_bag */ false,
+                                /* density_override */ 0U, &value, &selected_config, &flags);
+  if (cookie == kInvalidCookie) {
+    return Error("failed to find resource for id 0x%08x", resource_id);
+  }
+
+  if (value.dataType != Res_value::TYPE_STRING) {
+    return Error("resource for is 0x%08x is not a file", resource_id);
+  }
+
+  auto string_pool = asset_manager.GetStringPoolForCookie(cookie);
+  size_t len;
+  auto file_path16 = string_pool->stringAt(value.data, &len);
+  if (file_path16 == nullptr) {
+    return Error("failed to find string for index %d", value.data);
+  }
+
+  // Load the overlay resource mappings from the file specified using android:resourcesMap.
+  auto file_path = String8(String16(file_path16));
+  auto asset = asset_manager.OpenNonAsset(file_path.c_str(), Asset::AccessMode::ACCESS_BUFFER);
+  if (asset == nullptr) {
+    return Error("file \"%s\" not found", file_path.c_str());
+  }
+
+  return asset;
+}
+
+}  // namespace
+
+Result<ResourceMapping> ResourceMapping::CreateResourceMapping(const AssetManager2* target_am,
+                                                               const LoadedPackage* target_package,
+                                                               const LoadedPackage* overlay_package,
+                                                               size_t string_pool_offset,
+                                                               const XmlParser& overlay_parser) {
+  ResourceMapping resource_mapping;
+  auto root_it = overlay_parser.tree_iterator();
+  if (root_it->event() != XmlParser::Event::START_TAG || root_it->name() != "overlay") {
+    return Error("root element is not <overlay> tag");
+  }
+
+  const uint8_t overlay_package_id = overlay_package->GetPackageId();
+  auto overlay_it_end = root_it.end();
+  for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) {
+    if (overlay_it->event() == XmlParser::Event::BAD_DOCUMENT) {
+      return Error("failed to parse overlay xml document");
+    }
+
+    if (overlay_it->event() != XmlParser::Event::START_TAG) {
+      continue;
+    }
+
+    if (overlay_it->name() != "item") {
+      return Error("unexpected tag <%s> in <overlay>", overlay_it->name().c_str());
+    }
+
+    Result<std::string> target_resource = overlay_it->GetAttributeStringValue("target");
+    if (!target_resource) {
+      return Error(R"(<item> tag missing expected attribute "target")");
+    }
+
+    Result<android::Res_value> overlay_resource = overlay_it->GetAttributeValue("value");
+    if (!overlay_resource) {
+      return Error(R"(<item> tag missing expected attribute "value")");
+    }
+
+    ResourceId target_id =
+        target_am->GetResourceId(*target_resource, "", target_package->GetPackageName());
+    if (target_id == 0U) {
+      LOG(WARNING) << "failed to find resource \"" << *target_resource << "\" in target resources";
+      continue;
+    }
+
+    if (overlay_resource->dataType == Res_value::TYPE_STRING) {
+      overlay_resource->data += string_pool_offset;
+    }
+
+    // Only rewrite resources defined within the overlay package to their corresponding target
+    // resource ids at runtime.
+    bool rewrite_overlay_reference =
+        (overlay_resource->dataType == Res_value::TYPE_REFERENCE)
+            ? overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data)
+            : false;
+
+    resource_mapping.AddMapping(target_id, overlay_resource->dataType, overlay_resource->data,
+                                rewrite_overlay_reference);
+  }
+
+  return resource_mapping;
+}
+
+Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy(
+    const AssetManager2* target_am, const AssetManager2* overlay_am,
+    const LoadedPackage* target_package, const LoadedPackage* overlay_package) {
+  ResourceMapping resource_mapping;
+  const auto end = overlay_package->end();
+  for (auto iter = overlay_package->begin(); iter != end; ++iter) {
+    const ResourceId overlay_resid = *iter;
+    Result<std::string> name = utils::ResToTypeEntryName(*overlay_am, overlay_resid);
+    if (!name) {
+      continue;
+    }
+
+    // Find the resource with the same type and entry name within the target package.
+    const std::string full_name =
+        base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str());
+    const ResourceId target_resource = target_am->GetResourceId(full_name);
+    if (target_resource == 0U) {
+      continue;
+    }
+
+    resource_mapping.AddMapping(target_resource, Res_value::TYPE_REFERENCE, overlay_resid,
+                                /* rewrite_overlay_reference */ true);
+  }
+
+  return resource_mapping;
+}
+
+void ResourceMapping::FilterOverlayableResources(const AssetManager2* target_am,
+                                                 const LoadedPackage* target_package,
+                                                 const LoadedPackage* overlay_package,
+                                                 const OverlayManifestInfo& overlay_info,
+                                                 const PolicyBitmask& fulfilled_policies) {
+  std::set<ResourceId> remove_ids;
+  for (const auto& target_map : target_map_) {
+    const ResourceId target_resid = target_map.first;
+    Result<Unit> success =
+        CheckOverlayable(*target_package, overlay_info, fulfilled_policies, target_resid);
+    if (success) {
+      continue;
+    }
+
+    // Attempting to overlay a resource that is not allowed to be overlaid is treated as a
+    // warning.
+    Result<std::string> name = utils::ResToTypeEntryName(*target_am, target_resid);
+    if (!name) {
+      name = StringPrintf("0x%08x", target_resid);
+    }
+
+    LOG(WARNING) << "overlay \"" << overlay_package->GetPackageName()
+                 << "\" is not allowed to overlay resource \"" << *name
+                 << "\" in target: " << success.GetErrorMessage();
+
+    remove_ids.insert(target_resid);
+  }
+
+  for (const ResourceId target_resid : remove_ids) {
+    RemoveMapping(target_resid);
+  }
+}
+
+Result<ResourceMapping> ResourceMapping::FromApkAssets(const ApkAssets& target_apk_assets,
+                                                       const ApkAssets& overlay_apk_assets,
+                                                       const OverlayManifestInfo& overlay_info,
+                                                       const PolicyBitmask& fulfilled_policies,
+                                                       bool enforce_overlayable) {
+  AssetManager2 target_asset_manager;
+  if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true /* invalidate_caches */,
+                                         false /* filter_incompatible_configs*/)) {
+    return Error("failed to create target asset manager");
+  }
+
+  AssetManager2 overlay_asset_manager;
+  if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets}, true /* invalidate_caches */,
+                                          false /* filter_incompatible_configs */)) {
+    return Error("failed to create overlay asset manager");
+  }
+
+  const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
+  if (target_arsc == nullptr) {
+    return Error("failed to load target resources.arsc");
+  }
+
+  const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
+  if (overlay_arsc == nullptr) {
+    return Error("failed to load overlay resources.arsc");
+  }
+
+  const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
+  if (target_pkg == nullptr) {
+    return Error("failed to load target package from resources.arsc");
+  }
+
+  const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
+  if (overlay_pkg == nullptr) {
+    return Error("failed to load overlay package from resources.arsc");
+  }
+
+  size_t string_pool_data_length = 0U;
+  size_t string_pool_offset = 0U;
+  std::unique_ptr<uint8_t[]> string_pool_data;
+  Result<ResourceMapping> resource_mapping = {{}};
+  if (overlay_info.resource_mapping != 0U) {
+    // Load the overlay resource mappings from the file specified using android:resourcesMap.
+    auto asset = OpenNonAssetFromResource(overlay_info.resource_mapping, overlay_asset_manager);
+    if (!asset) {
+      return Error("failed opening xml for android:resourcesMap: %s",
+                   asset.GetErrorMessage().c_str());
+    }
+
+    auto parser =
+        XmlParser::Create((*asset)->getBuffer(true /* wordAligned*/), (*asset)->getLength());
+    if (!parser) {
+      return Error("failed opening ResXMLTree");
+    }
+
+    // Copy the xml string pool data before the parse goes out of scope.
+    auto& string_pool = (*parser)->get_strings();
+    string_pool_data_length = string_pool.bytes();
+    string_pool_data.reset(new uint8_t[string_pool_data_length]);
+    memcpy(string_pool_data.get(), string_pool.data(), string_pool_data_length);
+
+    // Offset string indices by the size of the overlay resource table string pool.
+    string_pool_offset = overlay_arsc->GetStringPool()->size();
+
+    resource_mapping = CreateResourceMapping(&target_asset_manager, target_pkg, overlay_pkg,
+                                             string_pool_offset, *(*parser));
+  } else {
+    // If no file is specified using android:resourcesMap, it is assumed that the overlay only
+    // defines resources intended to override target resources of the same type and name.
+    resource_mapping = CreateResourceMappingLegacy(&target_asset_manager, &overlay_asset_manager,
+                                                   target_pkg, overlay_pkg);
+  }
+
+  if (!resource_mapping) {
+    return resource_mapping.GetError();
+  }
+
+  if (enforce_overlayable) {
+    // Filter out resources the overlay is not allowed to override.
+    (*resource_mapping)
+        .FilterOverlayableResources(&target_asset_manager, target_pkg, overlay_pkg, overlay_info,
+                                    fulfilled_policies);
+  }
+
+  resource_mapping->target_package_id_ = target_pkg->GetPackageId();
+  resource_mapping->overlay_package_id_ = overlay_pkg->GetPackageId();
+  resource_mapping->string_pool_offset_ = string_pool_offset;
+  resource_mapping->string_pool_data_ = std::move(string_pool_data);
+  resource_mapping->string_pool_data_length_ = string_pool_data_length;
+  return std::move(*resource_mapping);
+}
+
+OverlayResourceMap ResourceMapping::GetOverlayToTargetMap() const {
+  // An overlay resource can override multiple target resources at once. Rewrite the overlay
+  // resource as the first target resource it overrides.
+  OverlayResourceMap map;
+  for (const auto& mappings : overlay_map_) {
+    map.insert(std::make_pair(mappings.first, mappings.second));
+  }
+  return map;
+}
+
+Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource,
+                                         TargetValue::DataType data_type,
+                                         TargetValue::DataValue data_value,
+                                         bool rewrite_overlay_reference) {
+  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.
+
+  target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value}));
+
+  if (rewrite_overlay_reference && data_type == Res_value::TYPE_REFERENCE) {
+    overlay_map_.insert(std::make_pair(data_value, target_resource));
+  }
+
+  return Result<Unit>({});
+}
+
+void ResourceMapping::RemoveMapping(ResourceId target_resource) {
+  auto target_iter = target_map_.find(target_resource);
+  if (target_iter == target_map_.end()) {
+    return;
+  }
+
+  const TargetValue value = target_iter->second;
+  target_map_.erase(target_iter);
+
+  // Remove rewriting of overlay resource id to target resource id.
+  if (value.data_type != Res_value::TYPE_REFERENCE) {
+    return;
+  }
+
+  auto overlay_iter = overlay_map_.equal_range(value.data_value);
+  for (auto i = overlay_iter.first; i != overlay_iter.second; ++i) {
+    if (i->second == target_resource) {
+      overlay_map_.erase(i);
+      return;
+    }
+  }
+}
+
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
index d9c97e1..9d32692 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -98,6 +98,15 @@
     info.target_name = *result_str;
   }
 
+  if (auto result_value = overlay_it->GetAttributeValue("resourcesMap")) {
+    if ((*result_value).dataType == Res_value::TYPE_REFERENCE) {
+      info.resource_mapping = (*result_value).data;
+    } else {
+      return Error("android:resourcesMap is not a reference in AndroidManifest.xml of %s",
+                   path.c_str());
+    }
+  }
+
   if (auto result_value = overlay_it->GetAttributeValue("isStatic")) {
     if ((*result_value).dataType >= Res_value::TYPE_FIRST_INT &&
         (*result_value).dataType <= Res_value::TYPE_LAST_INT) {
@@ -116,14 +125,12 @@
     }
   }
 
-  iter = tag->find("requiredSystemPropertyName");
-  if (iter != tag->end()) {
-    info.requiredSystemPropertyName = iter->second;
+  if (auto result_str = overlay_it->GetAttributeStringValue("requiredSystemPropertyName")) {
+    info.requiredSystemPropertyName = *result_str;
   }
 
-  iter = tag->find("requiredSystemPropertyValue");
-  if (iter != tag->end()) {
-    info.requiredSystemPropertyValue = iter->second;
+   if (auto result_str = overlay_it->GetAttributeStringValue("requiredSystemPropertyValue")) {
+    info.requiredSystemPropertyValue = *result_str;
   }
 
   return info;