summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Ryan Mitchell <rtmitchell@google.com> 2019-10-10 22:36:44 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2019-10-10 22:36:44 +0000
commitd61d44da96884834519d76c044d5e8e47c4677b3 (patch)
tree06b30abd0a524df18ecc806b91581bcd0b1c4f65
parent98bfc275494411c7888d806f307ae193f09c4e9e (diff)
parent9e4f52b2e5c76791edc885397389771692c0de8a (diff)
Merge changes I1740dcdc,I12f965b5
* changes: Parse <overlay> and abstract resource mapping Improve idmap2 xml parsing
-rw-r--r--api/system-current.txt1
-rw-r--r--cmds/idmap2/Android.bp6
-rw-r--r--cmds/idmap2/idmap2/Create.cpp4
-rw-r--r--cmds/idmap2/idmap2/Lookup.cpp39
-rw-r--r--cmds/idmap2/idmap2/Scan.cpp2
-rw-r--r--cmds/idmap2/idmap2d/Idmap2Service.cpp4
-rw-r--r--cmds/idmap2/include/idmap2/Idmap.h19
-rw-r--r--cmds/idmap2/include/idmap2/ResourceMapping.h132
-rw-r--r--cmds/idmap2/include/idmap2/ResourceUtils.h19
-rw-r--r--cmds/idmap2/include/idmap2/Xml.h49
-rw-r--r--cmds/idmap2/include/idmap2/XmlParser.h147
-rw-r--r--cmds/idmap2/libidmap2/Idmap.cpp218
-rw-r--r--cmds/idmap2/libidmap2/ResourceMapping.cpp411
-rw-r--r--cmds/idmap2/libidmap2/ResourceUtils.cpp80
-rw-r--r--cmds/idmap2/libidmap2/Xml.cpp80
-rw-r--r--cmds/idmap2/libidmap2/XmlParser.cpp163
-rw-r--r--cmds/idmap2/tests/BinaryStreamVisitorTests.cpp5
-rw-r--r--cmds/idmap2/tests/IdmapTests.cpp13
-rw-r--r--cmds/idmap2/tests/PrettyPrintVisitorTests.cpp5
-rw-r--r--cmds/idmap2/tests/RawPrintVisitorTests.cpp7
-rw-r--r--cmds/idmap2/tests/ResourceMappingTests.cpp322
-rw-r--r--cmds/idmap2/tests/XmlParserTests.cpp174
-rw-r--r--cmds/idmap2/tests/XmlTests.cpp68
-rw-r--r--cmds/idmap2/tests/data/overlay/AndroidManifest.xml5
-rwxr-xr-xcmds/idmap2/tests/data/overlay/build2
-rw-r--r--cmds/idmap2/tests/data/overlay/overlay-no-name-static.apkbin1643 -> 3477 bytes
-rw-r--r--cmds/idmap2/tests/data/overlay/overlay-no-name.apkbin1599 -> 3389 bytes
-rw-r--r--cmds/idmap2/tests/data/overlay/overlay-static-1.apkbin1643 -> 3469 bytes
-rw-r--r--cmds/idmap2/tests/data/overlay/overlay-static-2.apkbin1643 -> 3469 bytes
-rw-r--r--cmds/idmap2/tests/data/overlay/overlay.apkbin1595 -> 3489 bytes
-rw-r--r--cmds/idmap2/tests/data/overlay/res/xml/overlays.xml23
-rw-r--r--cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml20
-rw-r--r--cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml20
-rw-r--r--cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml21
-rw-r--r--cmds/idmap2/tests/data/target/res/xml/test.xml19
-rw-r--r--cmds/idmap2/tests/data/target/target-no-overlayable.apkbin2263 -> 2749 bytes
-rw-r--r--cmds/idmap2/tests/data/target/target.apkbin5017 -> 5017 bytes
-rw-r--r--core/res/res/values/attrs_manifest.xml3
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--libs/androidfw/ResourceTypes.cpp5
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h2
41 files changed, 1616 insertions, 474 deletions
diff --git a/api/system-current.txt b/api/system-current.txt
index 4986aeb057e2..447ba30382a8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -226,6 +226,7 @@ package android {
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
+ field public static final int resourcesMap = 16844297; // 0x1010609
field public static final int supportsAmbientMode = 16844173; // 0x101058d
field public static final int userRestriction = 16844164; // 0x1010584
}
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 4c77ba402595..41a17064c3ba 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -43,9 +43,10 @@ cc_library {
"libidmap2/Policies.cpp",
"libidmap2/PrettyPrintVisitor.cpp",
"libidmap2/RawPrintVisitor.cpp",
+ "libidmap2/ResourceMapping.cpp",
"libidmap2/ResourceUtils.cpp",
"libidmap2/Result.cpp",
- "libidmap2/Xml.cpp",
+ "libidmap2/XmlParser.cpp",
"libidmap2/ZipFile.cpp",
],
export_include_dirs: ["include"],
@@ -97,9 +98,10 @@ cc_test {
"tests/PoliciesTests.cpp",
"tests/PrettyPrintVisitorTests.cpp",
"tests/RawPrintVisitorTests.cpp",
+ "tests/ResourceMappingTests.cpp",
"tests/ResourceUtilsTests.cpp",
"tests/ResultTests.cpp",
- "tests/XmlTests.cpp",
+ "tests/XmlParserTests.cpp",
"tests/ZipFileTests.cpp",
],
required: [
diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
index f482191b09a8..3ff6d3514331 100644
--- a/cmds/idmap2/idmap2/Create.cpp
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -99,8 +99,8 @@ Result<Unit> Create(const std::vector<std::string>& args) {
return Error("failed to load apk %s", overlay_apk_path.c_str());
}
- const auto idmap = Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path,
- *overlay_apk, fulfilled_policies, !ignore_overlayable);
+ const auto idmap =
+ Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, !ignore_overlayable);
if (!idmap) {
return Error(idmap.GetError(), "failed to create idmap");
}
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index b7ae9d090cee..c5cf9807b689 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -33,9 +33,10 @@
#include "androidfw/Util.h"
#include "idmap2/CommandLineOptions.h"
#include "idmap2/Idmap.h"
+#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
-#include "idmap2/Xml.h"
+#include "idmap2/XmlParser.h"
#include "idmap2/ZipFile.h"
#include "utils/String16.h"
#include "utils/String8.h"
@@ -57,8 +58,7 @@ using android::idmap2::IdmapHeader;
using android::idmap2::ResourceId;
using android::idmap2::Result;
using android::idmap2::Unit;
-using android::idmap2::Xml;
-using android::idmap2::ZipFile;
+using android::idmap2::utils::ExtractOverlayManifestInfo;
using android::util::Utf16ToUtf8;
namespace {
@@ -132,29 +132,6 @@ Result<std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId res
return out;
}
-Result<std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) {
- const auto zip = ZipFile::Open(apk_path);
- if (!zip) {
- return Error("failed to open %s as zip", apk_path.c_str());
- }
- const auto entry = zip->Uncompress("AndroidManifest.xml");
- if (!entry) {
- return Error("failed to uncompress AndroidManifest.xml in %s", apk_path.c_str());
- }
- const auto xml = Xml::Create(entry->buf, entry->size);
- if (!xml) {
- return Error("failed to create XML buffer");
- }
- const auto tag = xml->FindTag("overlay");
- if (!tag) {
- return Error("failed to find <overlay> tag");
- }
- const auto iter = tag->find("targetPackage");
- if (iter == tag->end()) {
- return Error("failed to find targetPackage attribute");
- }
- return iter->second;
-}
} // namespace
Result<Unit> Lookup(const std::vector<std::string>& args) {
@@ -202,12 +179,12 @@ Result<Unit> Lookup(const std::vector<std::string>& args) {
}
apk_assets.push_back(std::move(target_apk));
- const Result<std::string> package_name =
- GetTargetPackageNameFromManifest(idmap_header->GetOverlayPath().to_string());
- if (!package_name) {
- return Error("failed to parse android:targetPackage from overlay manifest");
+ auto manifest_info = ExtractOverlayManifestInfo(idmap_header->GetOverlayPath().to_string(),
+ true /* assert_overlay */);
+ if (!manifest_info) {
+ return manifest_info.GetError();
}
- target_package_name = *package_name;
+ target_package_name = (*manifest_info).target_package;
} else if (target_path != idmap_header->GetTargetPath()) {
return Error("different target APKs (expected target APK %s but %s has target APK %s)",
target_path.c_str(), idmap_path.c_str(),
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
index d0530f2d344e..e643ab567644 100644
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -33,7 +33,7 @@
#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
-#include "idmap2/Xml.h"
+#include "idmap2/XmlParser.h"
#include "idmap2/ZipFile.h"
using android::idmap2::CommandLineOptions;
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index 8ee79f61520a..4aabf8399a25 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -137,8 +137,8 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path,
return error("failed to load apk " + overlay_apk_path);
}
- const auto idmap = Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path,
- *overlay_apk, policy_bitmask, enforce_overlayable);
+ const auto idmap =
+ Idmap::FromApkAssets(*target_apk, *overlay_apk, policy_bitmask, enforce_overlayable);
if (!idmap) {
return error(idmap.GetErrorMessage());
}
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index ebbb5ffc989d..f2cae58b910a 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -56,20 +56,14 @@
#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
#include "idmap2/Policies.h"
+#include "idmap2/ResourceMapping.h"
namespace android::idmap2 {
class Idmap;
class Visitor;
-// use typedefs to let the compiler warn us about implicit casts
-typedef uint32_t ResourceId; // 0xpptteeee
-typedef uint8_t PackageId; // pp in 0xpptteeee
-typedef uint8_t TypeId; // tt in 0xpptteeee
-typedef uint16_t EntryId; // eeee in 0xpptteeee
-
static constexpr const ResourceId kPadding = 0xffffffffu;
-
static constexpr const EntryId kNoEntry = 0xffffu;
// magic number: all idmap files start with this
@@ -155,7 +149,7 @@ class IdmapData {
PackageId target_package_id_;
uint16_t type_count_;
- friend Idmap;
+ friend IdmapData;
DISALLOW_COPY_AND_ASSIGN(Header);
};
@@ -194,12 +188,15 @@ class IdmapData {
uint16_t entry_offset_;
std::vector<EntryId> entries_;
- friend Idmap;
+ friend IdmapData;
DISALLOW_COPY_AND_ASSIGN(TypeEntry);
};
static std::unique_ptr<const IdmapData> FromBinaryStream(std::istream& stream);
+ static Result<std::unique_ptr<const IdmapData>> FromResourceMapping(
+ const ResourceMapping& resource_mapping);
+
inline const std::unique_ptr<const Header>& GetHeader() const {
return header_;
}
@@ -232,9 +229,7 @@ class Idmap {
// file is used; change this in the next version of idmap to use a named
// package instead; also update FromApkAssets to take additional parameters:
// the target and overlay package names
- static Result<std::unique_ptr<const Idmap>> FromApkAssets(const std::string& target_apk_path,
- const ApkAssets& target_apk_assets,
- const std::string& overlay_apk_path,
+ static Result<std::unique_ptr<const Idmap>> FromApkAssets(const ApkAssets& target_apk_assets,
const ApkAssets& overlay_apk_assets,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable);
diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h
new file mode 100644
index 000000000000..c3e1ef06c20f
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/ResourceMapping.h
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
+#define IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#include "androidfw/ApkAssets.h"
+#include "idmap2/Policies.h"
+#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
+#include "idmap2/XmlParser.h"
+
+using android::idmap2::utils::OverlayManifestInfo;
+
+namespace android::idmap2 {
+
+struct TargetValue {
+ typedef uint8_t DataType;
+ typedef uint32_t DataValue;
+ DataType data_type;
+ DataValue data_value;
+};
+
+using TargetResourceMap = std::map<ResourceId, TargetValue>;
+using OverlayResourceMap = std::map<ResourceId, ResourceId>;
+
+class ResourceMapping {
+ public:
+ // Creates a ResourceMapping using the target and overlay APKs. Setting enforce_overlayable to
+ // `false` disables all overlayable and policy enforcement: this is intended for backwards
+ // compatibility pre-Q and unit tests.
+ static Result<ResourceMapping> FromApkAssets(const ApkAssets& target_apk_assets,
+ const ApkAssets& overlay_apk_assets,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable);
+
+ // Retrieves the mapping of target resource id to overlay value.
+ inline TargetResourceMap GetTargetToOverlayMap() const {
+ return target_map_;
+ }
+
+ // Retrieves the mapping of overlay resource id to target resource id. This allows a reference to
+ // an overlay resource to appear as a reference to its corresponding target resource at runtime.
+ OverlayResourceMap GetOverlayToTargetMap() const;
+
+ // Retrieves the build-time package id of the target package.
+ inline uint32_t GetTargetPackageId() const {
+ return target_package_id_;
+ }
+
+ // Retrieves the build-time package id of the overlay package.
+ inline uint32_t GetOverlayPackageId() const {
+ return overlay_package_id_;
+ }
+
+ // Retrieves the offset that was added to the index of inline string overlay values so the indices
+ // do not collide with the indices of the overlay resource table string pool.
+ inline uint32_t GetStringPoolOffset() const {
+ return string_pool_offset_;
+ }
+
+ // Retrieves the raw string pool data from the xml referenced in android:resourcesMap.
+ inline const std::pair<const uint8_t*, uint32_t> GetStringPoolData() const {
+ return std::make_pair(string_pool_data_.get(), string_pool_data_length_);
+ }
+
+ private:
+ ResourceMapping() = default;
+
+ // Apps a mapping of target resource id to the type and value of the data that overlays the
+ // target resource. The data_type is the runtime format of the data value (see
+ // Res_value::dataType). If rewrite_overlay_reference is `true` then references to an overlay
+ // resource should appear as a reference to its corresponding target resource at runtime.
+ Result<Unit> AddMapping(ResourceId target_resource, TargetValue::DataType data_type,
+ TargetValue::DataValue data_value, bool rewrite_overlay_reference);
+
+ // Removes the overlay value mapping for the target resource.
+ void RemoveMapping(ResourceId target_resource);
+
+ // Parses the mapping of target resources to overlay resources to generate a ResourceMapping.
+ static Result<ResourceMapping> CreateResourceMapping(const AssetManager2* target_am,
+ const LoadedPackage* target_package,
+ const LoadedPackage* overlay_package,
+ size_t string_pool_offset,
+ const XmlParser& overlay_parser);
+
+ // Generates a ResourceMapping that maps target resources to overlay resources by name. To overlay
+ // a target resource, a resource must exist in the overlay with the same type and entry name as
+ // the target resource.
+ static Result<ResourceMapping> CreateResourceMappingLegacy(const AssetManager2* target_am,
+ const AssetManager2* overlay_am,
+ const LoadedPackage* target_package,
+ const LoadedPackage* overlay_package);
+
+ // Removes resources that do not pass policy or overlayable checks of the target package.
+ void FilterOverlayableResources(const AssetManager2* target_am,
+ const LoadedPackage* target_package,
+ const LoadedPackage* overlay_package,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies);
+
+ TargetResourceMap target_map_;
+ std::multimap<ResourceId, ResourceId> overlay_map_;
+
+ uint32_t target_package_id_ = 0;
+ uint32_t overlay_package_id_ = 0;
+ uint32_t string_pool_offset_ = 0;
+ uint32_t string_pool_data_length_ = 0;
+ std::unique_ptr<uint8_t[]> string_pool_data_ = nullptr;
+};
+
+} // namespace android::idmap2
+
+#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index 9a0c2abced5a..abc2df1a147f 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -21,17 +21,28 @@
#include <string>
#include "androidfw/AssetManager2.h"
-#include "idmap2/Idmap.h"
#include "idmap2/Result.h"
#include "idmap2/ZipFile.h"
-namespace android::idmap2::utils {
+namespace android::idmap2 {
+
+// use typedefs to let the compiler warn us about implicit casts
+typedef uint32_t ResourceId; // 0xpptteeee
+typedef uint8_t PackageId; // pp in 0xpptteeee
+typedef uint8_t TypeId; // tt in 0xpptteeee
+typedef uint16_t EntryId; // eeee in 0xpptteeee
+
+#define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16)
+#define EXTRACT_ENTRY(resid) (0x0000ffff & (resid))
+
+namespace utils {
struct OverlayManifestInfo {
std::string target_package; // NOLINT(misc-non-private-member-variables-in-classes)
std::string target_name; // NOLINT(misc-non-private-member-variables-in-classes)
std::string requiredSystemPropertyName; // NOLINT(misc-non-private-member-variables-in-classes)
std::string requiredSystemPropertyValue; // NOLINT(misc-non-private-member-variables-in-classes)
+ uint32_t resource_mapping; // NOLINT(misc-non-private-member-variables-in-classes)
bool is_static; // NOLINT(misc-non-private-member-variables-in-classes)
int priority = -1; // NOLINT(misc-non-private-member-variables-in-classes)
};
@@ -41,6 +52,8 @@ Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
Result<std::string> ResToTypeEntryName(const AssetManager2& am, ResourceId resid);
-} // namespace android::idmap2::utils
+} // namespace utils
+
+} // namespace android::idmap2
#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
diff --git a/cmds/idmap2/include/idmap2/Xml.h b/cmds/idmap2/include/idmap2/Xml.h
deleted file mode 100644
index dd89dee0f64f..000000000000
--- a/cmds/idmap2/include/idmap2/Xml.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#ifndef IDMAP2_INCLUDE_IDMAP2_XML_H_
-#define IDMAP2_INCLUDE_IDMAP2_XML_H_
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include "android-base/macros.h"
-#include "androidfw/ResourceTypes.h"
-#include "utils/String16.h"
-
-namespace android::idmap2 {
-
-class Xml {
- public:
- static std::unique_ptr<const Xml> Create(const uint8_t* data, size_t size, bool copyData = false);
-
- std::unique_ptr<std::map<std::string, std::string>> FindTag(const std::string& name) const;
-
- ~Xml();
-
- private:
- Xml() {
- }
-
- mutable ResXMLTree xml_;
-
- DISALLOW_COPY_AND_ASSIGN(Xml);
-};
-
-} // namespace android::idmap2
-
-#endif // IDMAP2_INCLUDE_IDMAP2_XML_H_
diff --git a/cmds/idmap2/include/idmap2/XmlParser.h b/cmds/idmap2/include/idmap2/XmlParser.h
new file mode 100644
index 000000000000..972a6d7e3427
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/XmlParser.h
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
+#define IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
+
+#include <iostream>
+#include <map>
+#include <memory>
+#include <string>
+
+#include "Result.h"
+#include "android-base/macros.h"
+#include "androidfw/ResourceTypes.h"
+#include "utils/String16.h"
+
+namespace android::idmap2 {
+
+class XmlParser {
+ public:
+ using Event = ResXMLParser::event_code_t;
+ class iterator;
+
+ class Node {
+ public:
+ Event event() const;
+ std::string name() const;
+
+ Result<std::string> GetAttributeStringValue(const std::string& name) const;
+ Result<Res_value> GetAttributeValue(const std::string& name) const;
+
+ bool operator==(const Node& rhs) const;
+ bool operator!=(const Node& rhs) const;
+
+ private:
+ explicit Node(const ResXMLTree& tree);
+ Node(const ResXMLTree& tree, const ResXMLParser::ResXMLPosition& pos);
+
+ // Retrieves/Sets the position of the position of the xml parser in the xml tree.
+ ResXMLParser::ResXMLPosition get_position() const;
+ void set_position(const ResXMLParser::ResXMLPosition& pos);
+
+ // If `inner_child` is true, seek advances the parser to the first inner child of the current
+ // node. Otherwise, seek advances the parser to the following node. Returns false if there is
+ // no node to seek to.
+ bool Seek(bool inner_child);
+
+ ResXMLParser parser_;
+ friend iterator;
+ };
+
+ class iterator {
+ public:
+ iterator(const iterator& other) : iterator(other.tree_, other.iter_) {
+ }
+
+ inline iterator& operator=(const iterator& rhs) {
+ iter_.set_position(rhs.iter_.get_position());
+ return *this;
+ }
+
+ inline bool operator==(const iterator& rhs) const {
+ return iter_ == rhs.iter_;
+ }
+
+ inline bool operator!=(const iterator& rhs) const {
+ return !(*this == rhs);
+ }
+
+ inline iterator operator++() {
+ // Seek to the following xml node.
+ iter_.Seek(false /* inner_child */);
+ return *this;
+ }
+
+ iterator begin() const {
+ iterator child_it(*this);
+ // Seek to the first inner child of the current node.
+ child_it.iter_.Seek(true /* inner_child */);
+ return child_it;
+ }
+
+ iterator end() const {
+ iterator child_it = begin();
+ while (child_it.iter_.Seek(false /* inner_child */)) {
+ // Continue iterating until the end tag is found.
+ }
+
+ return child_it;
+ }
+
+ inline const Node operator*() {
+ return Node(tree_, iter_.get_position());
+ }
+
+ inline const Node* operator->() {
+ return &iter_;
+ }
+
+ private:
+ explicit iterator(const ResXMLTree& tree) : tree_(tree), iter_(Node(tree)) {
+ }
+ iterator(const ResXMLTree& tree, const Node& node)
+ : tree_(tree), iter_(Node(tree, node.get_position())) {
+ }
+
+ const ResXMLTree& tree_;
+ Node iter_;
+ friend XmlParser;
+ };
+
+ // Creates a new xml parser beginning at the first tag.
+ static Result<std::unique_ptr<const XmlParser>> Create(const void* data, size_t size,
+ bool copy_data = false);
+ ~XmlParser();
+
+ inline iterator tree_iterator() const {
+ return iterator(tree_);
+ }
+
+ inline const ResStringPool& get_strings() const {
+ return tree_.getStrings();
+ }
+
+ private:
+ XmlParser() = default;
+ mutable ResXMLTree tree_;
+
+ DISALLOW_COPY_AND_ASSIGN(XmlParser);
+};
+
+} // namespace android::idmap2
+
+#endif // IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 4649675965db..389ade59200c 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 android::idmap2 {
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 @@ bool WARN_UNUSED ReadString(std::istream& stream, char out[kIdmapStringLength])
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 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromBinaryStream(std::istream& strea
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("|");
- }
- message.append(policy);
+Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping(
+ const ResourceMapping& resource_mapping) {
+ if (resource_mapping.GetTargetToOverlayMap().empty()) {
+ return Error("no resources were overlaid");
}
- return message;
-}
-
-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");
- }
+ 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;
+ }
- 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());
+ matching_resources.Add(mapping.first, mapping.second.data_value);
}
- // 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());
+ // 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));
}
- return Result<Unit>({});
+ 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<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 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& tar
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 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& tar
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 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& tar
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 000000000000..95ae626664dd
--- /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 dce83e35978d..9d3269207c91 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -22,18 +22,18 @@
#include "androidfw/StringPiece.h"
#include "androidfw/Util.h"
#include "idmap2/Result.h"
-#include "idmap2/Xml.h"
+#include "idmap2/XmlParser.h"
#include "idmap2/ZipFile.h"
using android::StringPiece16;
using android::idmap2::Result;
-using android::idmap2::Xml;
+using android::idmap2::XmlParser;
using android::idmap2::ZipFile;
using android::util::Utf16ToUtf8;
namespace android::idmap2::utils {
-Result<std::string> ResToTypeEntryName(const AssetManager2& am, ResourceId resid) {
+Result<std::string> ResToTypeEntryName(const AssetManager2& am, uint32_t resid) {
AssetManager2::ResourceName name;
if (!am.GetResourceName(resid, &name)) {
return Error("no resource 0x%08x in asset manager", resid);
@@ -65,52 +65,72 @@ Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
return Error("failed to uncompress AndroidManifest.xml from %s", path.c_str());
}
- std::unique_ptr<const Xml> xml = Xml::Create(entry->buf, entry->size);
+ Result<std::unique_ptr<const XmlParser>> xml = XmlParser::Create(entry->buf, entry->size);
if (!xml) {
return Error("failed to parse AndroidManifest.xml from %s", path.c_str());
}
+ auto manifest_it = (*xml)->tree_iterator();
+ if (manifest_it->event() != XmlParser::Event::START_TAG || manifest_it->name() != "manifest") {
+ return Error("root element tag is not <manifest> in AndroidManifest.xml of %s", path.c_str());
+ }
+
+ auto overlay_it = std::find_if(manifest_it.begin(), manifest_it.end(), [](const auto& it) {
+ return it.event() == XmlParser::Event::START_TAG && it.name() == "overlay";
+ });
+
OverlayManifestInfo info{};
- const auto tag = xml->FindTag("overlay");
- if (!tag) {
- if (assert_overlay) {
- return Error("<overlay> missing from AndroidManifest.xml of %s", path.c_str());
+ if (overlay_it == manifest_it.end()) {
+ if (!assert_overlay) {
+ return info;
}
- return info;
+ return Error("<overlay> missing from AndroidManifest.xml of %s", path.c_str());
}
- auto iter = tag->find("targetPackage");
- if (iter == tag->end()) {
- if (assert_overlay) {
- return Error("android:targetPackage missing from <overlay> of %s", path.c_str());
- }
+ if (auto result_str = overlay_it->GetAttributeStringValue("targetPackage")) {
+ info.target_package = *result_str;
} else {
- info.target_package = iter->second;
+ return Error("android:targetPackage missing from <overlay> of %s: %s", path.c_str(),
+ result_str.GetErrorMessage().c_str());
}
- iter = tag->find("targetName");
- if (iter != tag->end()) {
- info.target_name = iter->second;
+ if (auto result_str = overlay_it->GetAttributeStringValue("targetName")) {
+ info.target_name = *result_str;
}
- iter = tag->find("isStatic");
- if (iter != tag->end()) {
- info.is_static = std::stoul(iter->second) != 0U;
+ 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());
+ }
}
- iter = tag->find("priority");
- if (iter != tag->end()) {
- info.priority = std::stoi(iter->second);
+ 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) {
+ info.is_static = (*result_value).data != 0U;
+ } else {
+ return Error("android:isStatic is not a boolean in AndroidManifest.xml of %s", path.c_str());
+ }
+ }
+
+ if (auto result_value = overlay_it->GetAttributeValue("priority")) {
+ if ((*result_value).dataType >= Res_value::TYPE_FIRST_INT &&
+ (*result_value).dataType <= Res_value::TYPE_LAST_INT) {
+ info.priority = (*result_value).data;
+ } else {
+ return Error("android:priority is not an integer in AndroidManifest.xml of %s", path.c_str());
+ }
}
- 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;
diff --git a/cmds/idmap2/libidmap2/Xml.cpp b/cmds/idmap2/libidmap2/Xml.cpp
deleted file mode 100644
index 264586829c47..000000000000
--- a/cmds/idmap2/libidmap2/Xml.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2018 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/Xml.h"
-
-#include <map>
-#include <memory>
-#include <string>
-#include <utility>
-
-namespace android::idmap2 {
-
-std::unique_ptr<const Xml> Xml::Create(const uint8_t* data, size_t size, bool copyData) {
- std::unique_ptr<Xml> xml(new Xml());
- if (xml->xml_.setTo(data, size, copyData) != NO_ERROR) {
- return nullptr;
- }
- return xml;
-}
-
-std::unique_ptr<std::map<std::string, std::string>> Xml::FindTag(const std::string& name) const {
- const String16 tag_to_find(name.c_str(), name.size());
- xml_.restart();
- ResXMLParser::event_code_t type;
- do {
- type = xml_.next();
- if (type == ResXMLParser::START_TAG) {
- size_t len;
- const String16 tag(xml_.getElementName(&len));
- if (tag == tag_to_find) {
- std::unique_ptr<std::map<std::string, std::string>> map(
- new std::map<std::string, std::string>());
- for (size_t i = 0; i < xml_.getAttributeCount(); i++) {
- const String16 key16(xml_.getAttributeName(i, &len));
- std::string key = String8(key16).c_str();
-
- std::string value;
- switch (xml_.getAttributeDataType(i)) {
- case Res_value::TYPE_STRING: {
- const String16 value16(xml_.getAttributeStringValue(i, &len));
- value = String8(value16).c_str();
- } break;
- case Res_value::TYPE_INT_DEC:
- case Res_value::TYPE_INT_HEX:
- case Res_value::TYPE_INT_BOOLEAN: {
- Res_value resValue;
- xml_.getAttributeValue(i, &resValue);
- value = std::to_string(resValue.data);
- } break;
- default:
- return nullptr;
- }
-
- map->emplace(std::make_pair(key, value));
- }
- return map;
- }
- }
- } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);
- return nullptr;
-}
-
-Xml::~Xml() {
- xml_.uninit();
-}
-
-} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp
new file mode 100644
index 000000000000..526a560907aa
--- /dev/null
+++ b/cmds/idmap2/libidmap2/XmlParser.cpp
@@ -0,0 +1,163 @@
+/*
+ * 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/XmlParser.h"
+
+#include <iostream>
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+
+namespace android::idmap2 {
+
+template <typename T>
+ResXMLParser::ResXMLPosition get_tree_position(const T& tree) {
+ ResXMLParser::ResXMLPosition pos{};
+ tree.getPosition(&pos);
+ return pos;
+}
+
+XmlParser::Node::Node(const ResXMLTree& tree) : Node(tree, get_tree_position(tree)) {
+}
+XmlParser::Node::Node(const ResXMLTree& tree, const ResXMLParser::ResXMLPosition& pos)
+ : parser_(tree) {
+ set_position(pos);
+}
+
+bool XmlParser::Node::operator==(const XmlParser::Node& rhs) const {
+ ResXMLParser::ResXMLPosition pos = get_position();
+ ResXMLParser::ResXMLPosition rhs_pos = rhs.get_position();
+ return pos.curExt == rhs_pos.curExt && pos.curNode == rhs_pos.curNode &&
+ pos.eventCode == rhs_pos.eventCode;
+}
+
+bool XmlParser::Node::operator!=(const XmlParser::Node& rhs) const {
+ return !(*this == rhs);
+}
+
+ResXMLParser::ResXMLPosition XmlParser::Node::get_position() const {
+ return get_tree_position(parser_);
+}
+
+void XmlParser::Node::set_position(const ResXMLParser::ResXMLPosition& pos) {
+ parser_.setPosition(pos);
+}
+
+bool XmlParser::Node::Seek(bool inner_child) {
+ if (parser_.getEventType() == XmlParser::Event::END_TAG) {
+ return false;
+ }
+
+ ssize_t depth = 0;
+ XmlParser::Event code;
+ while ((code = parser_.next()) != XmlParser::Event::BAD_DOCUMENT &&
+ code != XmlParser::Event::END_DOCUMENT) {
+ if (code == XmlParser::Event::START_TAG) {
+ if (++depth == (inner_child ? 1 : 0)) {
+ return true;
+ }
+ } else if (code == XmlParser::Event::END_TAG) {
+ if (--depth == (inner_child ? -1 : -2)) {
+ return false;
+ }
+ }
+ }
+
+ return false;
+}
+
+XmlParser::Event XmlParser::Node::event() const {
+ return parser_.getEventType();
+}
+
+std::string XmlParser::Node::name() const {
+ size_t len;
+ const String16 key16(parser_.getElementName(&len));
+ return String8(key16).c_str();
+}
+
+Result<std::string> XmlParser::Node::GetAttributeStringValue(const std::string& name) const {
+ auto value = GetAttributeValue(name);
+ if (!value) {
+ return value.GetError();
+ }
+
+ switch ((*value).dataType) {
+ case Res_value::TYPE_STRING: {
+ size_t len;
+ const String16 value16(parser_.getStrings().stringAt((*value).data, &len));
+ return std::string(String8(value16).c_str());
+ }
+ case Res_value::TYPE_INT_DEC:
+ case Res_value::TYPE_INT_HEX:
+ case Res_value::TYPE_INT_BOOLEAN: {
+ return std::to_string((*value).data);
+ }
+ default:
+ return Error(R"(Failed to convert attribute "%s" value to a string)", name.c_str());
+ }
+}
+
+Result<Res_value> XmlParser::Node::GetAttributeValue(const std::string& name) const {
+ size_t len;
+ for (size_t i = 0; i < parser_.getAttributeCount(); i++) {
+ const String16 key16(parser_.getAttributeName(i, &len));
+ std::string key = String8(key16).c_str();
+ if (key != name) {
+ continue;
+ }
+
+ Res_value res_value{};
+ if (parser_.getAttributeValue(i, &res_value) == BAD_TYPE) {
+ return Error(R"(Bad value for attribute "%s")", name.c_str());
+ }
+
+ return res_value;
+ }
+
+ return Error(R"(Failed to find attribute "%s")", name.c_str());
+}
+
+Result<std::unique_ptr<const XmlParser>> XmlParser::Create(const void* data, size_t size,
+ bool copy_data) {
+ auto parser = std::unique_ptr<const XmlParser>(new XmlParser());
+ if (parser->tree_.setTo(data, size, copy_data) != NO_ERROR) {
+ return Error("Malformed xml block");
+ }
+
+ // Find the beginning of the first tag.
+ XmlParser::Event event;
+ while ((event = parser->tree_.next()) != XmlParser::Event::BAD_DOCUMENT &&
+ event != XmlParser::Event::END_DOCUMENT && event != XmlParser::Event::START_TAG) {
+ }
+
+ if (event == XmlParser::Event::END_DOCUMENT) {
+ return Error("Root tag was not be found");
+ }
+
+ if (event == XmlParser::Event::BAD_DOCUMENT) {
+ return Error("Bad xml document");
+ }
+
+ return parser;
+}
+
+XmlParser::~XmlParser() {
+ tree_.uninit();
+}
+
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
index 9348ab721493..43fdc9a78186 100644
--- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -75,9 +75,8 @@ TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) {
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto idmap =
- Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
+ const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
std::stringstream stream;
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 0f47f1e77d7b..47e5b17f4a98 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -179,8 +179,7 @@ void CreateIdmap(const StringPiece& target_apk_path, const StringPiece& overlay_
ASSERT_THAT(overlay_apk, NotNull());
auto result =
- Idmap::FromApkAssets(target_apk_path.to_string(), *target_apk, overlay_apk_path.to_string(),
- *overlay_apk, fulfilled_policies, enforce_overlayable);
+ Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, enforce_overlayable);
*out_idmap = result ? std::move(*result) : nullptr;
}
@@ -195,7 +194,7 @@ TEST(IdmapTests, CreateIdmapFromApkAssets) {
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x76a20829);
- ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x8635c2ed);
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xc054fb26);
ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path);
ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
@@ -480,9 +479,8 @@ TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) {
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto result =
- Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
+ const auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_FALSE(result);
}
@@ -497,8 +495,7 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- auto result = Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC,
+ auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(result);
const auto idmap = std::move(*result);
diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
index c41250457678..1d34e42e188d 100644
--- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
@@ -43,9 +43,8 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) {
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto idmap =
- Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
+ const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
std::stringstream stream;
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index 26951763cd66..c243d745e568 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -38,9 +38,8 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto idmap =
- Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
+ const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
std::stringstream stream;
@@ -50,7 +49,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos);
ASSERT_NE(stream.str().find("00000004: 00000001 version\n"), std::string::npos);
ASSERT_NE(stream.str().find("00000008: 76a20829 target crc\n"), std::string::npos);
- ASSERT_NE(stream.str().find("0000000c: 8635c2ed overlay crc\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("0000000c: c054fb26 overlay crc\n"), std::string::npos);
ASSERT_NE(stream.str().find("0000021c: 00000000 0x7f010000 -> 0x7f010000 integer/int1\n"),
std::string::npos);
}
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
new file mode 100644
index 000000000000..1ef41de4410d
--- /dev/null
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2018 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 <cstdio> // fclose
+#include <fstream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "TestHelpers.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "idmap2/ResourceMapping.h"
+
+using android::idmap2::utils::ExtractOverlayManifestInfo;
+
+namespace android::idmap2 {
+
+#define ASSERT_RESULT(r) \
+ do { \
+ auto result = r; \
+ ASSERT_TRUE(result) << result.GetErrorMessage(); \
+ } while (0)
+
+Result<ResourceMapping> TestGetResourceMapping(const android::StringPiece& local_target_apk_path,
+ const android::StringPiece& local_overlay_apk_path,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable) {
+ const std::string target_apk_path(GetTestDataPath() + local_target_apk_path.data());
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ if (!target_apk) {
+ return Error(R"(Failed to load target apk "%s")", target_apk_path.data());
+ }
+
+ const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path.data());
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ if (!overlay_apk) {
+ return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data());
+ }
+
+ return ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, overlay_info, fulfilled_policies,
+ enforce_overlayable);
+}
+
+Result<ResourceMapping> TestGetResourceMapping(const android::StringPiece& local_target_apk_path,
+ const android::StringPiece& local_overlay_apk_path,
+ const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable) {
+ auto overlay_info = ExtractOverlayManifestInfo(GetTestDataPath() + local_overlay_apk_path.data());
+ if (!overlay_info) {
+ return overlay_info.GetError();
+ }
+ return TestGetResourceMapping(local_target_apk_path, local_overlay_apk_path, *overlay_info,
+ fulfilled_policies, enforce_overlayable);
+}
+
+Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& target_resource,
+ const uint8_t type, const uint32_t value, bool rewrite) {
+ auto target_map = mapping.GetTargetToOverlayMap();
+ auto entry_map = target_map.find(target_resource);
+ if (entry_map == target_map.end()) {
+ return Error("Failed to find mapping for target resource");
+ }
+
+ if (entry_map->second.data_type != type) {
+ return Error(R"(Expected type: "0x%02x" Actual type: "0x%02x")", type,
+ entry_map->second.data_type);
+ }
+
+ if (entry_map->second.data_value != value) {
+ return Error(R"(Expected value: "0x%08x" Actual value: "0x%08x")", type,
+ entry_map->second.data_value);
+ }
+
+ auto overlay_map = mapping.GetOverlayToTargetMap();
+ auto overlay_iter = overlay_map.find(entry_map->second.data_value);
+ if ((overlay_iter != overlay_map.end()) != rewrite) {
+ return Error(R"(Expected rewriting: "%s")", rewrite ? "true" : "false");
+ }
+
+ return Result<Unit>({});
+}
+
+TEST(ResourceMappingTests, ResourcesFromApkAssetsLegacy) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0U; // no xml
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
+ ASSERT_RESULT(MappingExists(res, 0x7f010000, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000,
+ true /* rewrite */)); // integer/int1
+ ASSERT_RESULT(MappingExists(res, 0x7f02000c, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020000,
+ true /* rewrite */)); // string/str1
+ ASSERT_RESULT(MappingExists(res, 0x7f02000e, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020001,
+ true /* rewrite */)); // string/str3
+ ASSERT_RESULT(MappingExists(res, 0x7f02000f, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020002,
+ true /* rewrite */)); // string/str4
+}
+
+TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0x7f030003; // xml/overlays_swap
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+ ASSERT_RESULT(MappingExists(res, 0x7f02000c, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020002,
+ true /* rewrite */)); // string/str1 -> string/str4
+ ASSERT_RESULT(MappingExists(res, 0x7f02000e, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020000,
+ true /* rewrite */)); // string/str3 -> string/str1
+ ASSERT_RESULT(MappingExists(res, 0x7f02000f, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020001,
+ true /* rewrite */)); // string/str4 -> string/str3
+}
+
+TEST(ResourceMappingTests, DoNotRewriteNonResourceMapping) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0x7f030001; // xml/overlays_different_packages
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
+ ASSERT_EQ(res.GetOverlayToTargetMap().size(), 1U);
+ ASSERT_RESULT(MappingExists(res, 0x7f02000c, 0x01 /* Res_value::TYPE_REFERENCE */, 0x0104000a,
+ false /* rewrite */)); // string/str1 -> android:string/ok
+ ASSERT_RESULT(MappingExists(res, 0x7f02000e, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020001,
+ true /* rewrite */)); // string/str3 -> string/str4
+}
+
+TEST(ResourceMappingTests, InlineResources) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0x7f030002; // xml/overlays_inline
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ constexpr size_t overlay_string_pool_size = 8U;
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
+ ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U);
+ ASSERT_RESULT(MappingExists(res, 0x7f02000c, 0x03 /* Res_value::TYPE_STRING */,
+ overlay_string_pool_size + 0U,
+ false /* rewrite */)); // string/str1 -> "Hello World"
+ ASSERT_RESULT(MappingExists(res, 0x7f010000, 0x10 /* Res_value::TYPE_INT_DEC */, 73U,
+ false /* rewrite */)); // string/str1 -> "Hello World"
+}
+
+TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
+ auto resources =
+ TestGetResourceMapping("/target/target.apk", "/system-overlay/system-overlay.apk",
+ PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+ ASSERT_RESULT(MappingExists(res, 0x7f020008, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000,
+ true /* rewrite */)); // string/policy_public
+ ASSERT_RESULT(MappingExists(res, 0x7f02000a, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010001,
+ true /* rewrite */)); // string/policy_system
+ ASSERT_RESULT(MappingExists(res, 0x7f02000b, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010002,
+ true /* rewrite */)); // string/policy_system_vendor
+}
+
+// Resources that are not declared as overlayable and resources that a protected by policies the
+// overlay does not fulfill must not map to overlay resources.
+TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
+ auto resources = TestGetResourceMapping(
+ "/target/target.apk", "/system-overlay-invalid/system-overlay-invalid.apk",
+ PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+ ASSERT_RESULT(MappingExists(res, 0x7f020008, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010005,
+ true /* rewrite */)); // string/policy_public
+ ASSERT_RESULT(MappingExists(res, 0x7f02000a, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010007,
+ true /* rewrite */)); // string/policy_system
+ ASSERT_RESULT(MappingExists(res, 0x7f02000b, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010008,
+ true /* rewrite */)); // string/policy_system_vendor
+}
+
+// Resources that are not declared as overlayable and resources that a protected by policies the
+// overlay does not fulfilled can map to overlay resources when overlayable enforcement is turned
+// off.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
+ auto resources = TestGetResourceMapping(
+ "/target/target.apk", "/system-overlay-invalid/system-overlay-invalid.apk",
+ PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 9U);
+ ASSERT_RESULT(MappingExists(res, 0x7f020003, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000,
+ true /* rewrite */)); // string/not_overlayable
+ ASSERT_RESULT(MappingExists(res, 0x7f020004, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010001,
+ true /* rewrite */)); // string/other
+ ASSERT_RESULT(MappingExists(res, 0x7f020005, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010002,
+ true /* rewrite */)); // string/policy_odm
+ ASSERT_RESULT(MappingExists(res, 0x7f020006, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010003,
+ true /* rewrite */)); // string/policy_oem
+ ASSERT_RESULT(MappingExists(res, 0x7f020007, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010004,
+ true /* rewrite */)); // string/policy_product
+ ASSERT_RESULT(MappingExists(res, 0x7f020008, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010005,
+ true /* rewrite */)); // string/policy_public
+ ASSERT_RESULT(MappingExists(res, 0x7f020009, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010006,
+ true /* rewrite */)); // string/policy_signature
+ ASSERT_RESULT(MappingExists(res, 0x7f02000a, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010007,
+ true /* rewrite */)); // string/policy_system
+ ASSERT_RESULT(MappingExists(res, 0x7f02000b, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010008,
+ true /* rewrite */)); // string/policy_system_vendor
+}
+
+// Overlays that do not target an <overlayable> tag can overlay resources defined within any
+// <overlayable> tag.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTargetName) {
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-no-name.apk",
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
+ ASSERT_RESULT(MappingExists(res, 0x7f010000, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000,
+ true /* rewrite */)); // integer/int1
+ ASSERT_RESULT(MappingExists(res, 0x7f02000c, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020000,
+ true /* rewrite */)); // string/str1
+ ASSERT_RESULT(MappingExists(res, 0x7f02000e, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020001,
+ true /* rewrite */)); // string/str3
+ ASSERT_RESULT(MappingExists(res, 0x7f02000f, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020002,
+ true /* rewrite */)); // string/str4
+}
+
+// Overlays that are neither pre-installed nor signed with the same signature as the target cannot
+// overlay packages that have not defined overlayable resources.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) {
+ auto resources =
+ TestGetResourceMapping("/target/target-no-overlayable.apk", "/overlay/overlay-no-name.apk",
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U);
+}
+
+// Overlays that are pre-installed or are signed with the same signature as the target can overlay
+// packages that have not defined overlayable resources.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) {
+ auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) -> void {
+ auto resources = TestGetResourceMapping("/target/target-no-overlayable.apk",
+ "/system-overlay-invalid/system-overlay-invalid.apk",
+ fulfilled_policies,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 9U);
+ ASSERT_RESULT(MappingExists(res, 0x7f020003, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000,
+ true /* rewrite */)); // string/not_overlayable
+ ASSERT_RESULT(MappingExists(res, 0x7f020004, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010001,
+ true /* rewrite */)); // string/other
+ ASSERT_RESULT(MappingExists(res, 0x7f020005, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010002,
+ true /* rewrite */)); // string/policy_odm
+ ASSERT_RESULT(MappingExists(res, 0x7f020006, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010003,
+ true /* rewrite */)); // string/policy_oem
+ ASSERT_RESULT(MappingExists(res, 0x7f020007, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010004,
+ true /* rewrite */)); // string/policy_product
+ ASSERT_RESULT(MappingExists(res, 0x7f020008, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010005,
+ true /* rewrite */)); // string/policy_public
+ ASSERT_RESULT(MappingExists(res, 0x7f020009, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010006,
+ true /* rewrite */)); // string/policy_signature
+ ASSERT_RESULT(MappingExists(res, 0x7f02000a, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010007,
+ true /* rewrite */)); // string/policy_system
+ ASSERT_RESULT(MappingExists(res, 0x7f02000b, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010008,
+ true /* rewrite */)); // string/policy_system_vendor
+ };
+
+ CheckEntries(PolicyFlags::POLICY_SIGNATURE);
+ CheckEntries(PolicyFlags::POLICY_PRODUCT_PARTITION);
+ CheckEntries(PolicyFlags::POLICY_SYSTEM_PARTITION);
+ CheckEntries(PolicyFlags::POLICY_VENDOR_PARTITION);
+ CheckEntries(PolicyFlags::POLICY_ODM_PARTITION);
+ CheckEntries(PolicyFlags::POLICY_OEM_PARTITION);
+}
+
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/XmlParserTests.cpp b/cmds/idmap2/tests/XmlParserTests.cpp
new file mode 100644
index 000000000000..1a7eaca4d67b
--- /dev/null
+++ b/cmds/idmap2/tests/XmlParserTests.cpp
@@ -0,0 +1,174 @@
+/*
+ * 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 <cstdio> // fclose
+#include <memory>
+#include <string>
+
+#include "TestHelpers.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "idmap2/XmlParser.h"
+#include "idmap2/ZipFile.h"
+
+namespace android::idmap2 {
+
+Result<std::unique_ptr<const XmlParser>> CreateTestParser(const std::string& test_file) {
+ auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+ if (zip == nullptr) {
+ return Error("Failed to open zip file");
+ }
+
+ auto data = zip->Uncompress(test_file);
+ if (data == nullptr) {
+ return Error("Failed to open xml file");
+ }
+
+ return XmlParser::Create(data->buf, data->size, /* copy_data */ true);
+}
+
+TEST(XmlParserTests, Create) {
+ auto xml = CreateTestParser("AndroidManifest.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ fclose(stderr); // silence expected warnings from libandroidfw
+ const char* not_xml = "foo";
+ auto fail = XmlParser::Create(reinterpret_cast<const uint8_t*>(not_xml), strlen(not_xml));
+ ASSERT_FALSE(fail);
+}
+
+TEST(XmlParserTests, NextChild) {
+ auto xml = CreateTestParser("res/xml/test.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ auto root_iter = (*xml)->tree_iterator();
+ ASSERT_EQ(root_iter->event(), XmlParser::Event::START_TAG);
+ ASSERT_EQ(root_iter->name(), "a");
+
+ auto a_iter = root_iter.begin();
+ ASSERT_EQ(a_iter->event(), XmlParser::Event::START_TAG);
+ ASSERT_EQ(a_iter->name(), "b");
+
+ auto c_iter = a_iter.begin();
+ ASSERT_EQ(c_iter->event(), XmlParser::Event::START_TAG);
+ ASSERT_EQ(c_iter->name(), "c");
+
+ ++c_iter;
+ ASSERT_EQ(c_iter->event(), XmlParser::Event::END_TAG);
+ ASSERT_EQ(c_iter, a_iter.end());
+
+ ++a_iter;
+ ASSERT_EQ(a_iter->event(), XmlParser::Event::START_TAG);
+ ASSERT_EQ(a_iter->name(), "d");
+
+ // Skip the <e> tag.
+ ++a_iter;
+ ASSERT_EQ(a_iter->event(), XmlParser::Event::END_TAG);
+ ASSERT_EQ(a_iter, root_iter.end());
+}
+
+TEST(XmlParserTests, AttributeValues) {
+ auto xml = CreateTestParser("res/xml/test.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ // Start at the <a> tag.
+ auto root_iter = (*xml)->tree_iterator();
+
+ // Start at the <b> tag.
+ auto a_iter = root_iter.begin();
+ auto attribute_str = a_iter->GetAttributeStringValue("type_string");
+ ASSERT_TRUE(attribute_str);
+ ASSERT_EQ(*attribute_str, "fortytwo");
+
+ auto attribute_value = a_iter->GetAttributeValue("type_int_dec");
+ ASSERT_TRUE(attribute_value);
+ ASSERT_EQ(attribute_value->data, 42);
+
+ attribute_value = a_iter->GetAttributeValue("type_int_hex");
+ ASSERT_TRUE(attribute_value);
+ ASSERT_EQ(attribute_value->data, 42);
+
+ attribute_value = a_iter->GetAttributeValue("type_int_boolean");
+ ASSERT_TRUE(attribute_value);
+ ASSERT_EQ(attribute_value->data, 0xffffffff);
+}
+
+TEST(XmlParserTests, IteratorEquality) {
+ auto xml = CreateTestParser("res/xml/test.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ // Start at the <a> tag.
+ auto root_iter_1 = (*xml)->tree_iterator();
+ auto root_iter_2 = (*xml)->tree_iterator();
+ ASSERT_EQ(root_iter_1, root_iter_2);
+ ASSERT_EQ(*root_iter_1, *root_iter_2);
+
+ // Start at the <b> tag.
+ auto a_iter_1 = root_iter_1.begin();
+ auto a_iter_2 = root_iter_2.begin();
+ ASSERT_NE(a_iter_1, root_iter_1.end());
+ ASSERT_NE(a_iter_2, root_iter_2.end());
+ ASSERT_EQ(a_iter_1, a_iter_2);
+ ASSERT_EQ(*a_iter_1, *a_iter_2);
+
+ // Move to the <d> tag.
+ ++a_iter_1;
+ ++a_iter_2;
+ ASSERT_NE(a_iter_1, root_iter_1.end());
+ ASSERT_NE(a_iter_2, root_iter_2.end());
+ ASSERT_EQ(a_iter_1, a_iter_2);
+ ASSERT_EQ(*a_iter_1, *a_iter_2);
+
+ // Move to the end of the <a> tag.
+ ++a_iter_1;
+ ++a_iter_2;
+ ASSERT_EQ(a_iter_1, root_iter_1.end());
+ ASSERT_EQ(a_iter_2, root_iter_2.end());
+ ASSERT_EQ(a_iter_1, a_iter_2);
+ ASSERT_EQ(*a_iter_1, *a_iter_2);
+}
+
+TEST(XmlParserTests, Backtracking) {
+ auto xml = CreateTestParser("res/xml/test.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ // Start at the <a> tag.
+ auto root_iter_1 = (*xml)->tree_iterator();
+
+ // Start at the <b> tag.
+ auto a_iter_1 = root_iter_1.begin();
+
+ // Start a second iterator at the <a> tag.
+ auto root_iter_2 = root_iter_1;
+ ASSERT_EQ(root_iter_1, root_iter_2);
+ ASSERT_EQ(*root_iter_1, *root_iter_2);
+
+ // Move the first iterator to the end of the <a> tag.
+ auto root_iter_end_1 = root_iter_1.end();
+ ++root_iter_1;
+ ASSERT_NE(root_iter_1, root_iter_2);
+ ASSERT_NE(*root_iter_1, *root_iter_2);
+
+ // Move to the <d> tag.
+ ++a_iter_1;
+ ASSERT_NE(a_iter_1, root_iter_end_1);
+
+ // Move to the end of the <a> tag.
+ ++a_iter_1;
+ ASSERT_EQ(a_iter_1, root_iter_end_1);
+}
+
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/XmlTests.cpp b/cmds/idmap2/tests/XmlTests.cpp
deleted file mode 100644
index df63211a9209..000000000000
--- a/cmds/idmap2/tests/XmlTests.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2018 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 <cstdio> // fclose
-
-#include "TestHelpers.h"
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "idmap2/Xml.h"
-#include "idmap2/ZipFile.h"
-
-using ::testing::IsNull;
-using ::testing::NotNull;
-
-namespace android::idmap2 {
-
-TEST(XmlTests, Create) {
- auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
- ASSERT_THAT(zip, NotNull());
-
- auto data = zip->Uncompress("AndroidManifest.xml");
- ASSERT_THAT(data, NotNull());
-
- auto xml = Xml::Create(data->buf, data->size);
- ASSERT_THAT(xml, NotNull());
-
- fclose(stderr); // silence expected warnings from libandroidfw
- const char* not_xml = "foo";
- auto fail = Xml::Create(reinterpret_cast<const uint8_t*>(not_xml), strlen(not_xml));
- ASSERT_THAT(fail, IsNull());
-}
-
-TEST(XmlTests, FindTag) {
- auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
- ASSERT_THAT(zip, NotNull());
-
- auto data = zip->Uncompress("res/xml/test.xml");
- ASSERT_THAT(data, NotNull());
-
- auto xml = Xml::Create(data->buf, data->size);
- ASSERT_THAT(xml, NotNull());
-
- auto attrs = xml->FindTag("c");
- ASSERT_THAT(attrs, NotNull());
- ASSERT_EQ(attrs->size(), 4U);
- ASSERT_EQ(attrs->at("type_string"), "fortytwo");
- ASSERT_EQ(std::stoi(attrs->at("type_int_dec")), 42);
- ASSERT_EQ(std::stoi(attrs->at("type_int_hex")), 42);
- ASSERT_NE(std::stoul(attrs->at("type_int_boolean")), 0U);
-
- auto fail = xml->FindTag("does-not-exist");
- ASSERT_THAT(fail, IsNull());
-}
-
-} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifest.xml b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
index 619bb6ce0f25..cf3691c3b3cf 100644
--- a/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
+++ b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
@@ -16,8 +16,11 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="test.overlay">
+
<application android:hasCode="false"/>
+
<overlay
android:targetPackage="test.target"
- android:targetName="TestResources"/>
+ android:targetName="TestResources"
+ android:resourcesMap="@xml/overlays"/>
</manifest>
diff --git a/cmds/idmap2/tests/data/overlay/build b/cmds/idmap2/tests/data/overlay/build
index 68b9f507a11d..b921b0d3d3ad 100755
--- a/cmds/idmap2/tests/data/overlay/build
+++ b/cmds/idmap2/tests/data/overlay/build
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-FRAMEWORK_RES_APK=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar
+FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk
aapt2 compile --dir res -o compiled.flata
diff --git a/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk b/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk
index 18ee43dc57a4..7c25985e5a61 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-no-name.apk b/cmds/idmap2/tests/data/overlay/overlay-no-name.apk
index 642519008b15..c75f3e1dbddf 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-no-name.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-no-name.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-1.apk b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
index 642ab90d00ae..5b8a6e4a90ed 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-2.apk b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
index 2ec56020c4aa..698a1fd6e702 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay.apk b/cmds/idmap2/tests/data/overlay/overlay.apk
index 5842da4f432e..1db303ff05b5 100644
--- a/cmds/idmap2/tests/data/overlay/overlay.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays.xml
new file mode 100644
index 000000000000..edd33f7dc90d
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<overlay>
+ <item target="string/str1" value="@string/str1"/>
+ <item target="string/str3" value="@string/str3" />
+ <item target="string/str4" value="@string/str4" />
+ <item target="integer/int1" value="@integer/int1" />
+ <item target="integer/not_in_target" value="@integer/not_in_target" />
+</overlay>
+
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml
new file mode 100644
index 000000000000..aa7fefaa305e
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<overlay>
+ <item target="string/str1" value="@android:string/ok"/>
+ <item target="string/str3" value="@string/str3" />
+</overlay>
+
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml
new file mode 100644
index 000000000000..e12b823ff50d
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<overlay>
+ <item target="string/str1" value="Hello World"/>
+ <item target="integer/int1" value="73" />
+</overlay>
+
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml
new file mode 100644
index 000000000000..5728e672d94a
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<overlay>
+ <item target="string/str1" value="@string/str4"/>
+ <item target="string/str3" value="@string/str1" />
+ <item target="string/str4" value="@string/str3" />
+ <item target="integer/int_not_in_target" value="@integer/int1" />
+</overlay>
diff --git a/cmds/idmap2/tests/data/target/res/xml/test.xml b/cmds/idmap2/tests/data/target/res/xml/test.xml
index 0fe21c6b6d0a..56a3f7f0b13a 100644
--- a/cmds/idmap2/tests/data/target/res/xml/test.xml
+++ b/cmds/idmap2/tests/data/target/res/xml/test.xml
@@ -14,12 +14,15 @@
limitations under the License.
-->
<a>
- <b>
- <c
- type_string="fortytwo"
- type_int_dec="42"
- type_int_hex="0x2a"
- type_int_boolean="true"
- />
+ <b type_string="fortytwo"
+ type_int_dec="42"
+ type_int_hex="0x2a"
+ type_int_boolean="true">
+
+ <c />
</b>
-</a>
+
+ <d>
+ <e />
+ </d>
+</a> \ No newline at end of file
diff --git a/cmds/idmap2/tests/data/target/target-no-overlayable.apk b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
index 033305aaed4f..2eb7c477c3b4 100644
--- a/cmds/idmap2/tests/data/target/target-no-overlayable.apk
+++ b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk
index 9bcd6dcabcde..251cf46f969d 100644
--- a/cmds/idmap2/tests/data/target/target.apk
+++ b/cmds/idmap2/tests/data/target/target.apk
Binary files differ
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 381ed7f80602..a301702e5f9b 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2840,6 +2840,9 @@
<!-- The name of the overlayable whose resources will be overlaid. -->
<attr name="targetName" />
+
+ <!-- The xml file that defines the target id to overlay value mappings. -->
+ <attr name="resourcesMap" format="reference" />
</declare-styleable>
<!-- Declaration of an {@link android.content.Intent} object in XML. May
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 96c0cf3fd86a..9724c41c1b16 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3000,6 +3000,8 @@
<public-group type="attr" first-id="0x01010607">
<public name="importantForContentCapture" />
<public name="forceQueryable" />
+ <!-- @hide @SystemApi -->
+ <public name="resourcesMap" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 535386920265..3fe2c5b0e21a 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1063,6 +1063,11 @@ size_t ResStringPool::bytes() const
return (mError == NO_ERROR) ? mHeader->header.size : 0;
}
+const void* ResStringPool::data() const
+{
+ return mHeader;
+}
+
bool ResStringPool::isSorted() const
{
return (mHeader->flags&ResStringPool_header::SORTED_FLAG)!=0;
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index fc635aaeb0d8..c8ace90e6515 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -523,6 +523,8 @@ public:
size_t size() const;
size_t styleCount() const;
size_t bytes() const;
+ const void* data() const;
+
bool isSorted() const;
bool isUTF8() const;