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;