Merge Android R (rvc-dev-plus-aosp-without-vendor@6692709)
Bug: 166295507
Merged-In: I3d92a6de21a938f6b352ec26dc23420c0fe02b27
Change-Id: Ifdb80563ef042738778ebb8a7581a97c4e3d96e2
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 4e57e88..878cef9 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -43,17 +43,7 @@
],
host_supported: true,
srcs: [
- "libidmap2/BinaryStreamVisitor.cpp",
- "libidmap2/CommandLineOptions.cpp",
- "libidmap2/FileUtils.cpp",
- "libidmap2/Idmap.cpp",
- "libidmap2/Policies.cpp",
- "libidmap2/PrettyPrintVisitor.cpp",
- "libidmap2/RawPrintVisitor.cpp",
- "libidmap2/ResourceUtils.cpp",
- "libidmap2/Result.cpp",
- "libidmap2/Xml.cpp",
- "libidmap2/ZipFile.cpp",
+ "libidmap2/**/*.cpp",
],
export_include_dirs: ["include"],
target: {
@@ -67,6 +57,7 @@
"libcutils",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
},
host: {
@@ -79,6 +70,37 @@
"libcutils",
"libutils",
"libziparchive",
+ "libidmap2_policies",
+ ],
+ },
+ },
+}
+
+cc_library {
+ name: "libidmap2_policies",
+ defaults: [
+ "idmap2_defaults",
+ ],
+ host_supported: true,
+ export_include_dirs: ["libidmap2_policies/include"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ android: {
+ static: {
+ enabled: false,
+ },
+ shared_libs: [
+ "libandroidfw",
+ ],
+ },
+ host: {
+ shared: {
+ enabled: false,
+ },
+ static_libs: [
+ "libandroidfw",
],
},
},
@@ -104,9 +126,10 @@
"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: [
@@ -123,6 +146,7 @@
"libutils",
"libz",
"libziparchive",
+ "libidmap2_policies",
],
},
host: {
@@ -134,6 +158,7 @@
"liblog",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
shared_libs: [
"libz",
@@ -150,12 +175,13 @@
],
host_supported: true,
srcs: [
+ "idmap2/CommandUtils.cpp",
"idmap2/Create.cpp",
+ "idmap2/CreateMultiple.cpp",
"idmap2/Dump.cpp",
"idmap2/Lookup.cpp",
"idmap2/Main.cpp",
"idmap2/Scan.cpp",
- "idmap2/Verify.cpp",
],
target: {
android: {
@@ -166,6 +192,7 @@
"libidmap2",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
},
host: {
@@ -177,12 +204,14 @@
"liblog",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
shared_libs: [
"libz",
],
},
},
+
}
cc_binary {
@@ -203,6 +232,7 @@
"libidmap2",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
static_libs: [
"libidmap2daidl",
@@ -235,3 +265,17 @@
],
path: "idmap2d/aidl",
}
+
+aidl_interface {
+ name: "overlayable_policy_aidl",
+ unstable: true,
+ srcs: [":overlayable_policy_aidl_files"],
+}
+
+filegroup {
+ name: "overlayable_policy_aidl_files",
+ srcs: [
+ "idmap2d/aidl/android/os/OverlayablePolicy.aidl",
+ ],
+ path: "idmap2d/aidl",
+}
diff --git a/cmds/idmap2/CPPLINT.cfg b/cmds/idmap2/CPPLINT.cfg
index 9dc6b4a..20ed43c 100644
--- a/cmds/idmap2/CPPLINT.cfg
+++ b/cmds/idmap2/CPPLINT.cfg
@@ -15,4 +15,4 @@
set noparent
linelength=100
root=..
-filter=+build/include_alpha
+filter=+build/include_alpha,-runtime/references,-build/c++
diff --git a/cmds/idmap2/TEST_MAPPING b/cmds/idmap2/TEST_MAPPING
index 26ccf03..9e0fb84 100644
--- a/cmds/idmap2/TEST_MAPPING
+++ b/cmds/idmap2/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name" : "idmap2_tests"
}
+ ],
+ "imports": [
+ {
+ "path": "frameworks/base/services/core/java/com/android/server/om"
+ }
]
}
diff --git a/cmds/idmap2/idmap2/Verify.cpp b/cmds/idmap2/idmap2/CommandUtils.cpp
similarity index 70%
rename from cmds/idmap2/idmap2/Verify.cpp
rename to cmds/idmap2/idmap2/CommandUtils.cpp
index 9cb67b3..8f5845b 100644
--- a/cmds/idmap2/idmap2/Verify.cpp
+++ b/cmds/idmap2/idmap2/CommandUtils.cpp
@@ -19,30 +19,19 @@
#include <string>
#include <vector>
-#include "idmap2/CommandLineOptions.h"
#include "idmap2/Idmap.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
-using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::IdmapHeader;
using android::idmap2::Result;
using android::idmap2::Unit;
-Result<Unit> Verify(const std::vector<std::string>& args) {
- SYSTRACE << "Verify " << args;
- std::string idmap_path;
-
- const CommandLineOptions opts =
- CommandLineOptions("idmap2 verify")
- .MandatoryOption("--idmap-path", "input: path to idmap file to verify", &idmap_path);
-
- const auto opts_ok = opts.Parse(args);
- if (!opts_ok) {
- return opts_ok.GetError();
- }
-
+Result<Unit> Verify(const std::string& idmap_path, const std::string& target_path,
+ const std::string& overlay_path, PolicyBitmask fulfilled_policies,
+ bool enforce_overlayable) {
+ SYSTRACE << "Verify " << idmap_path;
std::ifstream fin(idmap_path);
const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
fin.close();
@@ -50,7 +39,8 @@
return Error("failed to parse idmap header");
}
- const auto header_ok = header->IsUpToDate();
+ const auto header_ok = header->IsUpToDate(target_path.c_str(), overlay_path.c_str(),
+ fulfilled_policies, enforce_overlayable);
if (!header_ok) {
return Error(header_ok.GetError(), "idmap not up to date");
}
diff --git a/cmds/idmap2/idmap2/CommandUtils.h b/cmds/idmap2/idmap2/CommandUtils.h
new file mode 100644
index 0000000..e717e04
--- /dev/null
+++ b/cmds/idmap2/idmap2/CommandUtils.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 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_IDMAP2_COMMAND_UTILS_H_
+#define IDMAP2_IDMAP2_COMMAND_UTILS_H_
+
+#include "idmap2/PolicyUtils.h"
+#include "idmap2/Result.h"
+
+android::idmap2::Result<android::idmap2::Unit> Verify(const std::string& idmap_path,
+ const std::string& target_path,
+ const std::string& overlay_path,
+ PolicyBitmask fulfilled_policies,
+ bool enforce_overlayable);
+
+#endif // IDMAP2_IDMAP2_COMMAND_UTILS_H_
diff --git a/cmds/idmap2/idmap2/Commands.h b/cmds/idmap2/idmap2/Commands.h
index 718e361..69eea8d 100644
--- a/cmds/idmap2/idmap2/Commands.h
+++ b/cmds/idmap2/idmap2/Commands.h
@@ -23,9 +23,9 @@
#include "idmap2/Result.h"
android::idmap2::Result<android::idmap2::Unit> Create(const std::vector<std::string>& args);
+android::idmap2::Result<android::idmap2::Unit> CreateMultiple(const std::vector<std::string>& args);
android::idmap2::Result<android::idmap2::Unit> Dump(const std::vector<std::string>& args);
android::idmap2::Result<android::idmap2::Unit> Lookup(const std::vector<std::string>& args);
android::idmap2::Result<android::idmap2::Unit> Scan(const std::vector<std::string>& args);
-android::idmap2::Result<android::idmap2::Unit> Verify(const std::vector<std::string>& args);
#endif // IDMAP2_IDMAP2_COMMANDS_H_
diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
index f482191..9682b6ea 100644
--- a/cmds/idmap2/idmap2/Create.cpp
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -20,15 +20,14 @@
#include <fstream>
#include <memory>
#include <ostream>
-#include <sstream>
-#include <string>
#include <vector>
+#include "androidfw/ResourceTypes.h"
#include "idmap2/BinaryStreamVisitor.h"
#include "idmap2/CommandLineOptions.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
-#include "idmap2/Policies.h"
+#include "idmap2/PolicyUtils.h"
#include "idmap2/SysTrace.h"
using android::ApkAssets;
@@ -36,14 +35,15 @@
using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::Idmap;
-using android::idmap2::PoliciesToBitmask;
-using android::idmap2::PolicyBitmask;
-using android::idmap2::PolicyFlags;
using android::idmap2::Result;
using android::idmap2::Unit;
using android::idmap2::utils::kIdmapFilePermissionMask;
+using android::idmap2::utils::PoliciesToBitmaskResult;
using android::idmap2::utils::UidHasWriteAccessToPath;
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
Result<Unit> Create(const std::vector<std::string>& args) {
SYSTRACE << "Create " << args;
std::string target_apk_path;
@@ -78,7 +78,7 @@
}
PolicyBitmask fulfilled_policies = 0;
- auto conv_result = PoliciesToBitmask(policies);
+ auto conv_result = PoliciesToBitmaskResult(policies);
if (conv_result) {
fulfilled_policies |= *conv_result;
} else {
@@ -86,7 +86,7 @@
}
if (fulfilled_policies == 0) {
- fulfilled_policies |= PolicyFlags::POLICY_PUBLIC;
+ fulfilled_policies |= PolicyFlags::PUBLIC;
}
const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
@@ -99,8 +99,8 @@
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/CreateMultiple.cpp b/cmds/idmap2/idmap2/CreateMultiple.cpp
new file mode 100644
index 0000000..abdfaf4
--- /dev/null
+++ b/cmds/idmap2/idmap2/CreateMultiple.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2020 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 <sys/stat.h> // umask
+#include <sys/types.h> // umask
+
+#include <fstream>
+#include <memory>
+#include <ostream>
+#include <vector>
+
+#include "Commands.h"
+#include "android-base/stringprintf.h"
+#include "idmap2/BinaryStreamVisitor.h"
+#include "idmap2/CommandLineOptions.h"
+#include "idmap2/CommandUtils.h"
+#include "idmap2/FileUtils.h"
+#include "idmap2/Idmap.h"
+#include "idmap2/Policies.h"
+#include "idmap2/PolicyUtils.h"
+#include "idmap2/SysTrace.h"
+
+using android::ApkAssets;
+using android::base::StringPrintf;
+using android::idmap2::BinaryStreamVisitor;
+using android::idmap2::CommandLineOptions;
+using android::idmap2::Error;
+using android::idmap2::Idmap;
+using android::idmap2::Result;
+using android::idmap2::Unit;
+using android::idmap2::utils::kIdmapCacheDir;
+using android::idmap2::utils::kIdmapFilePermissionMask;
+using android::idmap2::utils::PoliciesToBitmaskResult;
+using android::idmap2::utils::UidHasWriteAccessToPath;
+
+Result<Unit> CreateMultiple(const std::vector<std::string>& args) {
+ SYSTRACE << "CreateMultiple " << args;
+ std::string target_apk_path;
+ std::string idmap_dir = kIdmapCacheDir;
+ std::vector<std::string> overlay_apk_paths;
+ std::vector<std::string> policies;
+ bool ignore_overlayable = false;
+
+ const CommandLineOptions opts =
+ CommandLineOptions("idmap2 create-multiple")
+ .MandatoryOption("--target-apk-path",
+ "input: path to apk which will have its resources overlaid",
+ &target_apk_path)
+ .MandatoryOption("--overlay-apk-path",
+ "input: path to apk which contains the new resource values",
+ &overlay_apk_paths)
+ .OptionalOption("--idmap-dir",
+ StringPrintf("output: path to the directory in which to write idmap file"
+ " (defaults to %s)",
+ kIdmapCacheDir),
+ &idmap_dir)
+ .OptionalOption("--policy",
+ "input: an overlayable policy this overlay fulfills"
+ " (if none or supplied, the overlay policy will default to \"public\")",
+ &policies)
+ .OptionalFlag("--ignore-overlayable", "disables overlayable and policy checks",
+ &ignore_overlayable);
+ const auto opts_ok = opts.Parse(args);
+ if (!opts_ok) {
+ return opts_ok.GetError();
+ }
+
+ PolicyBitmask fulfilled_policies = 0;
+ auto conv_result = PoliciesToBitmaskResult(policies);
+ if (conv_result) {
+ fulfilled_policies |= *conv_result;
+ } else {
+ return conv_result.GetError();
+ }
+
+ if (fulfilled_policies == 0) {
+ fulfilled_policies |= PolicyFlags::PUBLIC;
+ }
+
+ const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ if (!target_apk) {
+ return Error("failed to load apk %s", target_apk_path.c_str());
+ }
+
+ std::vector<std::string> idmap_paths;
+ for (const std::string& overlay_apk_path : overlay_apk_paths) {
+ const std::string idmap_path = Idmap::CanonicalIdmapPathFor(idmap_dir, overlay_apk_path);
+ const uid_t uid = getuid();
+ if (!UidHasWriteAccessToPath(uid, idmap_path)) {
+ LOG(WARNING) << "uid " << uid << "does not have write access to " << idmap_path.c_str();
+ continue;
+ }
+
+ if (!Verify(idmap_path, target_apk_path, overlay_apk_path, fulfilled_policies,
+ !ignore_overlayable)) {
+ const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ if (!overlay_apk) {
+ LOG(WARNING) << "failed to load apk " << overlay_apk_path.c_str();
+ continue;
+ }
+
+ const auto idmap =
+ Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, !ignore_overlayable);
+ if (!idmap) {
+ LOG(WARNING) << "failed to create idmap";
+ continue;
+ }
+
+ umask(kIdmapFilePermissionMask);
+ std::ofstream fout(idmap_path);
+ if (fout.fail()) {
+ LOG(WARNING) << "failed to open idmap path " << idmap_path.c_str();
+ continue;
+ }
+
+ BinaryStreamVisitor visitor(fout);
+ (*idmap)->accept(&visitor);
+ fout.close();
+ if (fout.fail()) {
+ LOG(WARNING) << "failed to write to idmap path %s" << idmap_path.c_str();
+ continue;
+ }
+ }
+
+ idmap_paths.emplace_back(idmap_path);
+ }
+
+ for (const std::string& idmap_path : idmap_paths) {
+ std::cout << idmap_path << std::endl;
+ }
+
+ return Unit{};
+}
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index b7ae9d0..c441709 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::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 {
@@ -85,11 +85,42 @@
return Error("failed to obtain resource id for %s", res.c_str());
}
-Result<std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) {
+void PrintValue(AssetManager2* const am, const Res_value& value, const ApkAssetsCookie& cookie,
+ std::string* const out) {
+ switch (value.dataType) {
+ case Res_value::TYPE_INT_DEC:
+ out->append(StringPrintf("%d", value.data));
+ break;
+ case Res_value::TYPE_INT_HEX:
+ out->append(StringPrintf("0x%08x", value.data));
+ break;
+ case Res_value::TYPE_INT_BOOLEAN:
+ out->append(value.data != 0 ? "true" : "false");
+ break;
+ case Res_value::TYPE_STRING: {
+ const ResStringPool* pool = am->GetStringPoolForCookie(cookie);
+ out->append("\"");
+ size_t len;
+ if (pool->isUTF8()) {
+ const char* str = pool->string8At(value.data, &len);
+ out->append(str, len);
+ } else {
+ const char16_t* str16 = pool->stringAt(value.data, &len);
+ out->append(Utf16ToUtf8(StringPiece16(str16, len)));
+ }
+ out->append("\"");
+ } break;
+ default:
+ out->append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data));
+ break;
+ }
+}
+
+Result<std::string> WARN_UNUSED GetValue(AssetManager2* const am, ResourceId resid) {
Res_value value;
ResTable_config config;
uint32_t flags;
- ApkAssetsCookie cookie = am.GetResource(resid, false, 0, &value, &config, &flags);
+ ApkAssetsCookie cookie = am->GetResource(resid, true, 0, &value, &config, &flags);
if (cookie == kInvalidCookie) {
return Error("no resource 0x%08x in asset manager", resid);
}
@@ -104,57 +135,40 @@
out.append(config.toString().c_str());
out.append("' value=");
- switch (value.dataType) {
- case Res_value::TYPE_INT_DEC:
- out.append(StringPrintf("%d", value.data));
- break;
- case Res_value::TYPE_INT_HEX:
- out.append(StringPrintf("0x%08x", value.data));
- break;
- case Res_value::TYPE_INT_BOOLEAN:
- out.append(value.data != 0 ? "true" : "false");
- break;
- case Res_value::TYPE_STRING: {
- const ResStringPool* pool = am.GetStringPoolForCookie(cookie);
- size_t len;
- if (pool->isUTF8()) {
- const char* str = pool->string8At(value.data, &len);
- out.append(str, len);
- } else {
- const char16_t* str16 = pool->stringAt(value.data, &len);
- out += Utf16ToUtf8(StringPiece16(str16, len));
- }
- } break;
- default:
+ if (value.dataType == Res_value::TYPE_REFERENCE) {
+ const android::ResolvedBag* bag = am->GetBag(static_cast<uint32_t>(value.data));
+ if (bag == nullptr) {
out.append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data));
- break;
+ return out;
+ }
+ out.append("[");
+ Res_value bag_val;
+ ResTable_config selected_config;
+ uint32_t flags;
+ uint32_t ref;
+ ApkAssetsCookie bag_cookie;
+ for (size_t i = 0; i < bag->entry_count; ++i) {
+ const android::ResolvedBag::Entry& entry = bag->entries[i];
+ bag_val = entry.value;
+ bag_cookie = am->ResolveReference(entry.cookie, &bag_val, &selected_config, &flags, &ref);
+ if (bag_cookie == kInvalidCookie) {
+ out.append(
+ StringPrintf("Error: dataType=0x%02x data=0x%08x", bag_val.dataType, bag_val.data));
+ continue;
+ }
+ PrintValue(am, bag_val, bag_cookie, &out);
+ if (i != bag->entry_count - 1) {
+ out.append(", ");
+ }
+ }
+ out.append("]");
+ } else {
+ PrintValue(am, value, cookie, &out);
}
+
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 +216,12 @@
}
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(),
@@ -235,7 +249,7 @@
return Error(resid.GetError(), "failed to parse resource ID");
}
- const Result<std::string> value = GetValue(am, *resid);
+ const Result<std::string> value = GetValue(&am, *resid);
if (!value) {
return Error(value.GetError(), "resource 0x%08x not found", *resid);
}
diff --git a/cmds/idmap2/idmap2/Main.cpp b/cmds/idmap2/idmap2/Main.cpp
index 8794908..fb093f0 100644
--- a/cmds/idmap2/idmap2/Main.cpp
+++ b/cmds/idmap2/idmap2/Main.cpp
@@ -53,7 +53,8 @@
int main(int argc, char** argv) {
SYSTRACE << "main";
const NameToFunctionMap commands = {
- {"create", Create}, {"dump", Dump}, {"lookup", Lookup}, {"scan", Scan}, {"verify", Verify},
+ {"create", Create}, {"create-multiple", CreateMultiple}, {"dump", Dump}, {"lookup", Lookup},
+ {"scan", Scan},
};
if (argc <= 1) {
PrintUsage(commands, std::cerr);
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
index da8c06e..3625045 100644
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -20,7 +20,6 @@
#include <memory>
#include <ostream>
#include <set>
-#include <sstream>
#include <string>
#include <utility>
#include <vector>
@@ -28,30 +27,33 @@
#include "Commands.h"
#include "android-base/properties.h"
#include "idmap2/CommandLineOptions.h"
+#include "idmap2/CommandUtils.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
+#include "idmap2/Policies.h"
+#include "idmap2/PolicyUtils.h"
#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
-#include "idmap2/Xml.h"
-#include "idmap2/ZipFile.h"
+#include "idmap2/XmlParser.h"
using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::Idmap;
-using android::idmap2::kPolicyOdm;
-using android::idmap2::kPolicyOem;
-using android::idmap2::kPolicyProduct;
-using android::idmap2::kPolicyPublic;
-using android::idmap2::kPolicySystem;
-using android::idmap2::kPolicyVendor;
-using android::idmap2::PolicyBitmask;
-using android::idmap2::PolicyFlags;
using android::idmap2::Result;
using android::idmap2::Unit;
+using android::idmap2::policy::kPolicyOdm;
+using android::idmap2::policy::kPolicyOem;
+using android::idmap2::policy::kPolicyProduct;
+using android::idmap2::policy::kPolicyPublic;
+using android::idmap2::policy::kPolicySystem;
+using android::idmap2::policy::kPolicyVendor;
using android::idmap2::utils::ExtractOverlayManifestInfo;
using android::idmap2::utils::FindFiles;
using android::idmap2::utils::OverlayManifestInfo;
+using android::idmap2::utils::PoliciesToBitmaskResult;
+
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
namespace {
@@ -98,6 +100,7 @@
}
std::vector<std::string> PoliciesForPath(const std::string& apk_path) {
+ // clang-format off
static const std::vector<std::pair<std::string, std::string>> values = {
{"/odm/", kPolicyOdm},
{"/oem/", kPolicyOem},
@@ -106,6 +109,7 @@
{"/system_ext/", kPolicySystem},
{"/vendor/", kPolicyVendor},
};
+ // clang-format on
std::vector<std::string> fulfilled_policies = {kPolicyPublic};
for (auto const& pair : values) {
@@ -178,11 +182,11 @@
// Note that conditional property enablement/exclusion only applies if
// the attribute is present. In its absence, all overlays are presumed enabled.
- if (!overlay_info->requiredSystemPropertyName.empty()
- && !overlay_info->requiredSystemPropertyValue.empty()) {
+ if (!overlay_info->requiredSystemPropertyName.empty() &&
+ !overlay_info->requiredSystemPropertyValue.empty()) {
// if property set & equal to value, then include overlay - otherwise skip
- if (android::base::GetProperty(overlay_info->requiredSystemPropertyName, "")
- != overlay_info->requiredSystemPropertyValue) {
+ if (android::base::GetProperty(overlay_info->requiredSystemPropertyName, "") !=
+ overlay_info->requiredSystemPropertyValue) {
continue;
}
}
@@ -215,7 +219,15 @@
std::stringstream stream;
for (const auto& overlay : interesting_apks) {
- if (!Verify(std::vector<std::string>({"--idmap-path", overlay.idmap_path}))) {
+ const auto policy_bitmask = PoliciesToBitmaskResult(overlay.policies);
+ if (!policy_bitmask) {
+ LOG(WARNING) << "failed to create idmap for overlay apk path \"" << overlay.apk_path
+ << "\": " << policy_bitmask.GetErrorMessage();
+ continue;
+ }
+
+ if (!Verify(overlay.idmap_path, target_apk_path, overlay.apk_path, *policy_bitmask,
+ !overlay.ignore_overlayable)) {
std::vector<std::string> create_args = {"--target-apk-path", target_apk_path,
"--overlay-apk-path", overlay.apk_path,
"--idmap-path", overlay.idmap_path};
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index d723776..15e22a3 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -33,22 +33,29 @@
#include "idmap2/BinaryStreamVisitor.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
-#include "idmap2/Policies.h"
+#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
+#include "idmap2/ZipFile.h"
#include "utils/String8.h"
using android::IPCThreadState;
+using android::base::StringPrintf;
using android::binder::Status;
using android::idmap2::BinaryStreamVisitor;
+using android::idmap2::GetPackageCrc;
using android::idmap2::Idmap;
using android::idmap2::IdmapHeader;
-using android::idmap2::PolicyBitmask;
+using android::idmap2::ZipFile;
using android::idmap2::utils::kIdmapCacheDir;
using android::idmap2::utils::kIdmapFilePermissionMask;
using android::idmap2::utils::UidHasWriteAccessToPath;
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+
namespace {
+constexpr const char* kFrameworkPath = "/system/framework/framework-res.apk";
+
Status ok() {
return Status::ok();
}
@@ -62,6 +69,21 @@
return static_cast<PolicyBitmask>(arg);
}
+Status GetCrc(const std::string& apk_path, uint32_t* out_crc) {
+ const auto zip = ZipFile::Open(apk_path);
+ if (!zip) {
+ return error(StringPrintf("failed to open apk %s", apk_path.c_str()));
+ }
+
+ const auto crc = GetPackageCrc(*zip);
+ if (!crc) {
+ return error(crc.GetErrorMessage());
+ }
+
+ *out_crc = *crc;
+ return ok();
+}
+
} // namespace
namespace android::os {
@@ -93,21 +115,52 @@
return ok();
}
-Status Idmap2Service::verifyIdmap(const std::string& overlay_apk_path,
- int32_t fulfilled_policies ATTRIBUTE_UNUSED,
- bool enforce_overlayable ATTRIBUTE_UNUSED,
- int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) {
+Status Idmap2Service::verifyIdmap(const std::string& target_apk_path,
+ const std::string& overlay_apk_path, int32_t fulfilled_policies,
+ bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED,
+ bool* _aidl_return) {
SYSTRACE << "Idmap2Service::verifyIdmap " << overlay_apk_path;
assert(_aidl_return);
+
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
std::ifstream fin(idmap_path);
const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
fin.close();
- *_aidl_return = header && header->IsUpToDate();
+ if (!header) {
+ *_aidl_return = false;
+ return error("failed to parse idmap header");
+ }
- // TODO(b/119328308): Check that the set of fulfilled policies of the overlay has not changed
+ uint32_t target_crc;
+ if (target_apk_path == kFrameworkPath && android_crc_) {
+ target_crc = *android_crc_;
+ } else {
+ auto target_crc_status = GetCrc(target_apk_path, &target_crc);
+ if (!target_crc_status.isOk()) {
+ *_aidl_return = false;
+ return target_crc_status;
+ }
- return ok();
+ // Loading the framework zip can take several milliseconds. Cache the crc of the framework
+ // resource APK to reduce repeated work during boot.
+ if (target_apk_path == kFrameworkPath) {
+ android_crc_ = target_crc;
+ }
+ }
+
+ uint32_t overlay_crc;
+ auto overlay_crc_status = GetCrc(overlay_apk_path, &overlay_crc);
+ if (!overlay_crc_status.isOk()) {
+ *_aidl_return = false;
+ return overlay_crc_status;
+ }
+
+ auto up_to_date =
+ header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(), target_crc, overlay_crc,
+ ConvertAidlArgToPolicyBitmask(fulfilled_policies), enforce_overlayable);
+
+ *_aidl_return = static_cast<bool>(up_to_date);
+ return *_aidl_return ? ok() : error(up_to_date.GetErrorMessage());
}
Status Idmap2Service::createIdmap(const std::string& target_apk_path,
@@ -137,21 +190,27 @@
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());
}
+ // idmap files are mapped with mmap in libandroidfw. Deleting and recreating the idmap guarantees
+ // that existing memory maps will continue to be valid and unaffected.
+ unlink(idmap_path.c_str());
+
umask(kIdmapFilePermissionMask);
std::ofstream fout(idmap_path);
if (fout.fail()) {
return error("failed to open idmap path " + idmap_path);
}
+
BinaryStreamVisitor visitor(fout);
(*idmap)->accept(&visitor);
fout.close();
if (fout.fail()) {
+ unlink(idmap_path.c_str());
return error("failed to write to idmap path " + idmap_path);
}
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index 73a236a..abee999 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -20,9 +20,6 @@
#include <android-base/unique_fd.h>
#include <binder/BinderService.h>
-#include <optional>
-#include <string>
-
#include "android/os/BnIdmap2.h"
namespace android::os {
@@ -34,18 +31,24 @@
}
binder::Status getIdmapPath(const std::string& overlay_apk_path, int32_t user_id,
- std::string* _aidl_return);
+ std::string* _aidl_return) override;
binder::Status removeIdmap(const std::string& overlay_apk_path, int32_t user_id,
- bool* _aidl_return);
+ bool* _aidl_return) override;
- binder::Status verifyIdmap(const std::string& overlay_apk_path, int32_t fulfilled_policies,
- bool enforce_overlayable, int32_t user_id, bool* _aidl_return);
+ binder::Status verifyIdmap(const std::string& target_apk_path,
+ const std::string& overlay_apk_path, int32_t fulfilled_policies,
+ bool enforce_overlayable, int32_t user_id,
+ bool* _aidl_return) override;
binder::Status createIdmap(const std::string& target_apk_path,
const std::string& overlay_apk_path, int32_t fulfilled_policies,
bool enforce_overlayable, int32_t user_id,
- std::optional<std::string>* _aidl_return);
+ std::optional<std::string>* _aidl_return) override;
+
+ private:
+ // Cache the crc of the android framework package since the crc cannot change without a reboot.
+ std::optional<uint32_t> android_crc_;
};
} // namespace android::os
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
index cd474c0..156f1d7 100644
--- a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
+++ b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
@@ -20,18 +20,13 @@
* @hide
*/
interface IIdmap2 {
- const int POLICY_PUBLIC = 0x00000001;
- const int POLICY_SYSTEM_PARTITION = 0x00000002;
- const int POLICY_VENDOR_PARTITION = 0x00000004;
- const int POLICY_PRODUCT_PARTITION = 0x00000008;
- const int POLICY_SIGNATURE = 0x00000010;
- const int POLICY_ODM_PARTITION = 0x00000020;
- const int POLICY_OEM_PARTITION = 0x00000040;
-
@utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId);
boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId);
- boolean verifyIdmap(@utf8InCpp String overlayApkPath, int fulfilledPolicies,
- boolean enforceOverlayable, int userId);
+ boolean verifyIdmap(@utf8InCpp String targetApkPath,
+ @utf8InCpp String overlayApkPath,
+ int fulfilledPolicies,
+ boolean enforceOverlayable,
+ int userId);
@nullable @utf8InCpp String createIdmap(@utf8InCpp String targetApkPath,
@utf8InCpp String overlayApkPath,
int fulfilledPolicies,
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl b/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl
new file mode 100644
index 0000000..02b27a8
--- /dev/null
+++ b/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+/**
+ * @see ResourcesTypes.h ResTable_overlayable_policy_header::PolicyFlags
+ * @hide
+ */
+interface OverlayablePolicy {
+ const int PUBLIC = 0x00000001;
+ const int SYSTEM_PARTITION = 0x00000002;
+ const int VENDOR_PARTITION = 0x00000004;
+ const int PRODUCT_PARTITION = 0x00000008;
+ const int SIGNATURE = 0x00000010;
+ const int ODM_PARTITION = 0x00000020;
+ const int OEM_PARTITION = 0x00000040;
+ const int ACTOR_SIGNATURE = 0x00000080;
+}
diff --git a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
index 2c3e9d3..ff45b14 100644
--- a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
+++ b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
@@ -29,16 +29,19 @@
public:
explicit BinaryStreamVisitor(std::ostream& stream) : stream_(stream) {
}
- virtual void visit(const Idmap& idmap);
- virtual void visit(const IdmapHeader& header);
- virtual void visit(const IdmapData& data);
- virtual void visit(const IdmapData::Header& header);
- virtual void visit(const IdmapData::TypeEntry& type_entry);
+ ~BinaryStreamVisitor() override = default;
+ void visit(const Idmap& idmap) override;
+ void visit(const IdmapHeader& header) override;
+ void visit(const IdmapData& data) override;
+ void visit(const IdmapData::Header& header) override;
private:
+ void Write(const void* value, size_t length);
+ void Write8(uint8_t value);
void Write16(uint16_t value);
void Write32(uint32_t value);
- void WriteString(const StringPiece& value);
+ void WriteString256(const StringPiece& value);
+ void WriteString(const std::string& value);
std::ostream& stream_;
};
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index ebbb5ffc..0f05592 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -18,19 +18,21 @@
* # idmap file format (current version)
*
* idmap := header data*
- * header := magic version target_crc overlay_crc target_path overlay_path
+ * header := magic version target_crc overlay_crc target_path overlay_path debug_info
* data := data_header data_block*
* data_header := target_package_id types_count
* data_block := target_type overlay_type entry_count entry_offset entry*
- * overlay_path := string
- * target_path := string
+ * overlay_path := string256
+ * target_path := string256
+ * debug_info := string
+ * string := <uint32_t> <uint8_t>+ '\0'+
* entry := <uint32_t>
* entry_count := <uint16_t>
* entry_offset := <uint16_t>
* magic := <uint32_t>
* overlay_crc := <uint32_t>
* overlay_type := <uint16_t>
- * string := <uint8_t>[256]
+ * string256 := <uint8_t>[256]
* target_crc := <uint32_t>
* target_package_id := <uint16_t>
* target_type := <uint16_t>
@@ -41,6 +43,22 @@
* # idmap file format changelog
* ## v1
* - Identical to idmap v1.
+ *
+ * ## v2
+ * - Entries are no longer separated by type into type specific data blocks.
+ * - Added overlay-indexed target resource id lookup capabilities.
+ * - Target and overlay entries are stored as a sparse array in the data block. The target entries
+ * array maps from target resource id to overlay data type and value and the array is sorted by
+ * target resource id. The overlay entries array maps from overlay resource id to target resource
+ * id and the array is sorted by overlay resource id. It is important for both arrays to be sorted
+ * to allow for O(log(number_of_overlaid_resources)) performance when looking up resource
+ * mappings at runtime.
+ * - Idmap can now encode a type and value to override a resource without needing a table entry.
+ * - A string pool block is included to retrieve the value of strings that do not have a resource
+ * table entry.
+ *
+ * ## v3
+ * - Add 'debug' block to IdmapHeader.
*/
#ifndef IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
@@ -55,21 +73,15 @@
#include "androidfw/ApkAssets.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
-#include "idmap2/Policies.h"
+#include "idmap2/ResourceMapping.h"
+#include "idmap2/ZipFile.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
@@ -82,6 +94,9 @@
// terminating null)
static constexpr const size_t kIdmapStringLength = 256;
+// Retrieves a crc generated using all of the files within the zip that can affect idmap generation.
+Result<uint32_t> GetPackageCrc(const ZipFile& zip_info);
+
class IdmapHeader {
public:
static std::unique_ptr<const IdmapHeader> FromBinaryStream(std::istream& stream);
@@ -102,6 +117,14 @@
return overlay_crc_;
}
+ inline uint32_t GetFulfilledPolicies() const {
+ return fulfilled_policies_;
+ }
+
+ bool GetEnforceOverlayable() const {
+ return enforce_overlayable_;
+ }
+
inline StringPiece GetTargetPath() const {
return StringPiece(target_path_);
}
@@ -110,10 +133,18 @@
return StringPiece(overlay_path_);
}
+ inline const std::string& GetDebugInfo() const {
+ return debug_info_;
+ }
+
// Invariant: anytime the idmap data encoding is changed, the idmap version
// field *must* be incremented. Because of this, we know that if the idmap
// header is up-to-date the entire file is up-to-date.
- Result<Unit> IsUpToDate() const;
+ Result<Unit> IsUpToDate(const char* target_path, const char* overlay_path,
+ PolicyBitmask fulfilled_policies, bool enforce_overlayable) const;
+ Result<Unit> IsUpToDate(const char* target_path, const char* overlay_path, uint32_t target_crc,
+ uint32_t overlay_crc, PolicyBitmask fulfilled_policies,
+ bool enforce_overlayable) const;
void accept(Visitor* v) const;
@@ -125,13 +156,15 @@
uint32_t version_;
uint32_t target_crc_;
uint32_t overlay_crc_;
+ uint32_t fulfilled_policies_;
+ bool enforce_overlayable_;
char target_path_[kIdmapStringLength];
char overlay_path_[kIdmapStringLength];
+ std::string debug_info_;
friend Idmap;
DISALLOW_COPY_AND_ASSIGN(IdmapHeader);
};
-
class IdmapData {
public:
class Header {
@@ -142,70 +175,72 @@
return target_package_id_;
}
- inline uint16_t GetTypeCount() const {
- return type_count_;
+ inline PackageId GetOverlayPackageId() const {
+ return overlay_package_id_;
+ }
+
+ inline uint32_t GetTargetEntryCount() const {
+ return target_entry_count;
+ }
+
+ inline uint32_t GetOverlayEntryCount() const {
+ return overlay_entry_count;
+ }
+
+ inline uint32_t GetStringPoolIndexOffset() const {
+ return string_pool_index_offset;
+ }
+
+ inline uint32_t GetStringPoolLength() const {
+ return string_pool_len;
}
void accept(Visitor* v) const;
private:
- Header() {
- }
-
PackageId target_package_id_;
- uint16_t type_count_;
+ PackageId overlay_package_id_;
+ uint32_t target_entry_count;
+ uint32_t overlay_entry_count;
+ uint32_t string_pool_index_offset;
+ uint32_t string_pool_len;
+ Header() = default;
friend Idmap;
+ friend IdmapData;
DISALLOW_COPY_AND_ASSIGN(Header);
};
- class TypeEntry {
- public:
- static std::unique_ptr<const TypeEntry> FromBinaryStream(std::istream& stream);
+ struct TargetEntry {
+ ResourceId target_id;
+ TargetValue::DataType data_type;
+ TargetValue::DataValue data_value;
+ };
- inline TypeId GetTargetTypeId() const {
- return target_type_id_;
- }
-
- inline TypeId GetOverlayTypeId() const {
- return overlay_type_id_;
- }
-
- inline uint16_t GetEntryCount() const {
- return entries_.size();
- }
-
- inline uint16_t GetEntryOffset() const {
- return entry_offset_;
- }
-
- inline EntryId GetEntry(size_t i) const {
- return i < entries_.size() ? entries_[i] : 0xffffu;
- }
-
- void accept(Visitor* v) const;
-
- private:
- TypeEntry() {
- }
-
- TypeId target_type_id_;
- TypeId overlay_type_id_;
- uint16_t entry_offset_;
- std::vector<EntryId> entries_;
-
- friend Idmap;
- DISALLOW_COPY_AND_ASSIGN(TypeEntry);
+ struct OverlayEntry {
+ ResourceId overlay_id;
+ ResourceId target_id;
};
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_;
}
- inline const std::vector<std::unique_ptr<const TypeEntry>>& GetTypeEntries() const {
- return type_entries_;
+ inline const std::vector<TargetEntry>& GetTargetEntries() const {
+ return target_entries_;
+ }
+
+ inline const std::vector<OverlayEntry>& GetOverlayEntries() const {
+ return overlay_entries_;
+ }
+
+ inline const void* GetStringPoolData() const {
+ return string_pool_.get();
}
void accept(Visitor* v) const;
@@ -215,7 +250,9 @@
}
std::unique_ptr<const Header> header_;
- std::vector<std::unique_ptr<const TypeEntry>> type_entries_;
+ std::vector<TargetEntry> target_entries_;
+ std::vector<OverlayEntry> overlay_entries_;
+ std::unique_ptr<uint8_t[]> string_pool_;
friend Idmap;
DISALLOW_COPY_AND_ASSIGN(IdmapData);
@@ -232,9 +269,7 @@
// 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);
@@ -267,7 +302,6 @@
virtual void visit(const IdmapHeader& header) = 0;
virtual void visit(const IdmapData& data) = 0;
virtual void visit(const IdmapData::Header& header) = 0;
- virtual void visit(const IdmapData::TypeEntry& type_entry) = 0;
};
} // namespace android::idmap2
diff --git a/cmds/idmap2/include/idmap2/LogInfo.h b/cmds/idmap2/include/idmap2/LogInfo.h
new file mode 100644
index 0000000..a6237e6
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/LogInfo.h
@@ -0,0 +1,81 @@
+/*
+ * 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_LOGINFO_H_
+#define IDMAP2_INCLUDE_IDMAP2_LOGINFO_H_
+
+#include <algorithm>
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#if __ANDROID__
+#include "android-base/logging.h"
+#else
+#include <iostream>
+#endif
+
+namespace android::idmap2 {
+
+class LogMessage {
+ public:
+ LogMessage() = default;
+
+ template <typename T>
+ LogMessage& operator<<(const T& value) {
+ stream_ << value;
+ return *this;
+ }
+
+ std::string GetString() const {
+ return stream_.str();
+ }
+
+ private:
+ std::stringstream stream_;
+};
+
+class LogInfo {
+ public:
+ LogInfo() = default;
+
+ inline void Info(const LogMessage& msg) {
+ lines_.push_back("I " + msg.GetString());
+ }
+
+ inline void Warning(const LogMessage& msg) {
+#ifdef __ANDROID__
+ LOG(WARNING) << msg.GetString();
+#else
+ std::cerr << "W " << msg.GetString() << std::endl;
+#endif
+ lines_.push_back("W " + msg.GetString());
+ }
+
+ inline std::string GetString() const {
+ std::ostringstream stream;
+ std::copy(lines_.begin(), lines_.end(), std::ostream_iterator<std::string>(stream, "\n"));
+ return stream.str();
+ }
+
+ private:
+ std::vector<std::string> lines_;
+};
+
+} // namespace android::idmap2
+
+#endif // IDMAP2_INCLUDE_IDMAP2_LOGINFO_H_
diff --git a/cmds/idmap2/include/idmap2/Policies.h b/cmds/idmap2/include/idmap2/Policies.h
deleted file mode 100644
index 87edd35..0000000
--- a/cmds/idmap2/include/idmap2/Policies.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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 <string>
-#include <vector>
-
-#include "Result.h"
-#include "androidfw/ResourceTypes.h"
-#include "androidfw/StringPiece.h"
-
-#ifndef IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
-#define IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
-
-namespace android::idmap2 {
-
-constexpr const char* kPolicyOdm = "odm";
-constexpr const char* kPolicyOem = "oem";
-constexpr const char* kPolicyProduct = "product";
-constexpr const char* kPolicyPublic = "public";
-constexpr const char* kPolicySignature = "signature";
-constexpr const char* kPolicySystem = "system";
-constexpr const char* kPolicyVendor = "vendor";
-
-using PolicyFlags = ResTable_overlayable_policy_header::PolicyFlags;
-using PolicyBitmask = uint32_t;
-
-// Parses the string representations of policies into a bitmask.
-Result<PolicyBitmask> PoliciesToBitmask(const std::vector<std::string>& policies);
-
-// Retrieves the string representations of policies in the bitmask.
-std::vector<std::string> BitmaskToPolicies(const PolicyBitmask& bitmask);
-
-} // namespace android::idmap2
-
-#endif // IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
diff --git a/cmds/idmap2/include/idmap2/PolicyUtils.h b/cmds/idmap2/include/idmap2/PolicyUtils.h
new file mode 100644
index 0000000..b95b8b4
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/PolicyUtils.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 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_POLICYUTILS_H_
+#define IDMAP2_INCLUDE_IDMAP2_POLICYUTILS_H_
+
+#include <string>
+#include <vector>
+
+#include "androidfw/ResourceTypes.h"
+#include "idmap2/Policies.h"
+#include "idmap2/Result.h"
+
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+
+namespace android::idmap2::utils {
+
+// Returns a Result object containing a policy flag bitmask built from a list of policy strings.
+// On error will contain a human readable message listing the invalid policies.
+Result<PolicyBitmask> PoliciesToBitmaskResult(const std::vector<std::string>& policies);
+
+// Converts a bitmask of policy flags into a list of their string representation as would be written
+// into XML
+std::vector<std::string> BitmaskToPolicies(const PolicyBitmask& bitmask);
+
+} // namespace android::idmap2::utils
+
+#endif // IDMAP2_INCLUDE_IDMAP2_POLICYUTILS_H_
diff --git a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
index 5111bb2..5dcf217 100644
--- a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
+++ b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
@@ -33,17 +33,16 @@
public:
explicit PrettyPrintVisitor(std::ostream& stream) : stream_(stream) {
}
- virtual void visit(const Idmap& idmap);
- virtual void visit(const IdmapHeader& header);
- virtual void visit(const IdmapData& data);
- virtual void visit(const IdmapData::Header& header);
- virtual void visit(const IdmapData::TypeEntry& type_entry);
+ ~PrettyPrintVisitor() override = default;
+ void visit(const Idmap& idmap) override;
+ void visit(const IdmapHeader& header) override;
+ void visit(const IdmapData& data) override;
+ void visit(const IdmapData::Header& header) override;
private:
std::ostream& stream_;
std::unique_ptr<const ApkAssets> target_apk_;
AssetManager2 target_am_;
- PackageId last_seen_package_id_;
};
} // namespace idmap2
diff --git a/cmds/idmap2/include/idmap2/RawPrintVisitor.h b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
index 2e543d4..92c1864 100644
--- a/cmds/idmap2/include/idmap2/RawPrintVisitor.h
+++ b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
@@ -34,22 +34,25 @@
public:
explicit RawPrintVisitor(std::ostream& stream) : stream_(stream), offset_(0) {
}
- virtual void visit(const Idmap& idmap);
- virtual void visit(const IdmapHeader& header);
- virtual void visit(const IdmapData& data);
- virtual void visit(const IdmapData::Header& header);
- virtual void visit(const IdmapData::TypeEntry& type_entry);
+ ~RawPrintVisitor() override = default;
+ void visit(const Idmap& idmap) override;
+ void visit(const IdmapHeader& header) override;
+ void visit(const IdmapData& data) override;
+ void visit(const IdmapData::Header& header) override;
private:
+ void print(uint8_t value, const char* fmt, ...);
void print(uint16_t value, const char* fmt, ...);
void print(uint32_t value, const char* fmt, ...);
- void print(const std::string& value, const char* fmt, ...);
+ void print(const std::string& value, size_t encoded_size, const char* fmt, ...);
+ void print_raw(uint32_t length, const char* fmt, ...);
std::ostream& stream_;
std::unique_ptr<const ApkAssets> target_apk_;
+ std::unique_ptr<const ApkAssets> overlay_apk_;
AssetManager2 target_am_;
+ AssetManager2 overlay_am_;
size_t offset_;
- PackageId last_seen_package_id_;
};
} // namespace idmap2
diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h
new file mode 100644
index 0000000..5869409
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/ResourceMapping.h
@@ -0,0 +1,136 @@
+/*
+ * 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/LogInfo.h"
+#include "idmap2/Policies.h"
+#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
+#include "idmap2/XmlParser.h"
+
+using android::idmap2::utils::OverlayManifestInfo;
+
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+
+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, LogInfo& log_info);
+
+ // 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,
+ LogInfo& log_info);
+
+ // 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, LogInfo& log_info);
+
+ 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 9a0c2ab..c643b0e 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -21,19 +21,36 @@
#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 {
+
+// Returns whether the Res_value::data_type represents a dynamic or regular resource reference.
+bool IsReference(uint8_t data_type);
+
+// Converts the Res_value::data_type to a human-readable string representation.
+StringPiece DataTypeToString(uint8_t data_type);
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 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)
- bool is_static; // NOLINT(misc-non-private-member-variables-in-classes)
- int priority = -1; // 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)
};
Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
@@ -41,6 +58,8 @@
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 dd89dee..0000000
--- 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 0000000..972a6d7
--- /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/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
index dee2d21..255212a 100644
--- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
+++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
@@ -24,6 +24,14 @@
namespace android::idmap2 {
+void BinaryStreamVisitor::Write(const void* value, size_t length) {
+ stream_.write(reinterpret_cast<const char*>(value), length);
+}
+
+void BinaryStreamVisitor::Write8(uint8_t value) {
+ stream_.write(reinterpret_cast<char*>(&value), sizeof(uint8_t));
+}
+
void BinaryStreamVisitor::Write16(uint16_t value) {
uint16_t x = htodl(value);
stream_.write(reinterpret_cast<char*>(&x), sizeof(uint16_t));
@@ -34,13 +42,21 @@
stream_.write(reinterpret_cast<char*>(&x), sizeof(uint32_t));
}
-void BinaryStreamVisitor::WriteString(const StringPiece& value) {
+void BinaryStreamVisitor::WriteString256(const StringPiece& value) {
char buf[kIdmapStringLength];
memset(buf, 0, sizeof(buf));
memcpy(buf, value.data(), std::min(value.size(), sizeof(buf)));
stream_.write(buf, sizeof(buf));
}
+void BinaryStreamVisitor::WriteString(const std::string& value) {
+ // pad with null to nearest word boundary; include at least one terminating null
+ size_t padding_size = 4 - (value.size() % 4);
+ Write32(value.size() + padding_size);
+ stream_.write(value.c_str(), value.size());
+ stream_.write("\0\0\0\0", padding_size);
+}
+
void BinaryStreamVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
// nothing to do
}
@@ -50,30 +66,35 @@
Write32(header.GetVersion());
Write32(header.GetTargetCrc());
Write32(header.GetOverlayCrc());
- WriteString(header.GetTargetPath());
- WriteString(header.GetOverlayPath());
+ Write32(header.GetFulfilledPolicies());
+ Write8(static_cast<uint8_t>(header.GetEnforceOverlayable()));
+ WriteString256(header.GetTargetPath());
+ WriteString256(header.GetOverlayPath());
+ WriteString(header.GetDebugInfo());
}
-void BinaryStreamVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
- // nothing to do
+void BinaryStreamVisitor::visit(const IdmapData& data) {
+ for (const auto& target_entry : data.GetTargetEntries()) {
+ Write32(target_entry.target_id);
+ Write8(target_entry.data_type);
+ Write32(target_entry.data_value);
+ }
+
+ for (const auto& overlay_entry : data.GetOverlayEntries()) {
+ Write32(overlay_entry.overlay_id);
+ Write32(overlay_entry.target_id);
+ }
+
+ Write(data.GetStringPoolData(), data.GetHeader()->GetStringPoolLength());
}
void BinaryStreamVisitor::visit(const IdmapData::Header& header) {
- Write16(header.GetTargetPackageId());
- Write16(header.GetTypeCount());
-}
-
-void BinaryStreamVisitor::visit(const IdmapData::TypeEntry& type_entry) {
- const uint16_t entryCount = type_entry.GetEntryCount();
-
- Write16(type_entry.GetTargetTypeId());
- Write16(type_entry.GetOverlayTypeId());
- Write16(entryCount);
- Write16(type_entry.GetEntryOffset());
- for (uint16_t i = 0; i < entryCount; i++) {
- EntryId entry_id = type_entry.GetEntry(i);
- Write32(entry_id != kNoEntry ? static_cast<uint32_t>(entry_id) : kPadding);
- }
+ Write8(header.GetTargetPackageId());
+ Write8(header.GetOverlayPackageId());
+ Write32(header.GetTargetEntryCount());
+ Write32(header.GetOverlayEntryCount());
+ Write32(header.GetStringPoolIndexOffset());
+ Write32(header.GetStringPoolLength());
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 4649675..23c25a7 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,34 +42,10 @@
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) {
- TypeId target_typeid = EXTRACT_TYPE(target_resid);
- if (map_.find(target_typeid) == map_.end()) {
- map_.emplace(target_typeid, std::set<std::pair<ResourceId, ResourceId>>());
- }
- map_[target_typeid].insert(std::make_pair(target_resid, overlay_resid));
- }
-
- inline const std::map<TypeId, std::set<std::pair<ResourceId, ResourceId>>>& WARN_UNUSED
- Map() const {
- return map_;
- }
-
- private:
- // target type id -> set { pair { overlay entry id, overlay entry id } }
- std::map<TypeId, std::set<std::pair<ResourceId, ResourceId>>> map_;
-};
-
-bool WARN_UNUSED Read16(std::istream& stream, uint16_t* out) {
- uint16_t value;
- if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint16_t))) {
- *out = dtohl(value);
+bool WARN_UNUSED Read8(std::istream& stream, uint8_t* out) {
+ uint8_t value;
+ if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint8_t))) {
+ *out = value;
return true;
}
return false;
@@ -83,8 +60,17 @@
return false;
}
+bool WARN_UNUSED ReadBuffer(std::istream& stream, std::unique_ptr<uint8_t[]>* out, size_t length) {
+ auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[length]);
+ if (stream.read(reinterpret_cast<char*>(buffer.get()), length)) {
+ *out = std::move(buffer);
+ return true;
+ }
+ return false;
+}
+
// a string is encoded as a kIdmapStringLength char array; the array is always null-terminated
-bool WARN_UNUSED ReadString(std::istream& stream, char out[kIdmapStringLength]) {
+bool WARN_UNUSED ReadString256(std::istream& stream, char out[kIdmapStringLength]) {
char buf[kIdmapStringLength];
memset(buf, 0, sizeof(buf));
if (!stream.read(buf, sizeof(buf))) {
@@ -97,29 +83,26 @@
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;
+Result<std::string> ReadString(std::istream& stream) {
+ uint32_t size;
+ if (!Read32(stream, &size)) {
+ return Error("failed to read string size");
}
- int id = packages[0]->GetPackageId();
- return loaded_arsc.GetPackageById(id);
+ if (size == 0) {
+ return std::string("");
+ }
+ std::string buf(size, '\0');
+ if (!stream.read(buf.data(), size)) {
+ return Error("failed to read string of size %u", size);
+ }
+ // buf is guaranteed to be null terminated (with enough nulls to end on a word boundary)
+ buf.resize(strlen(buf.c_str()));
+ return buf;
}
-Result<uint32_t> GetCrc(const ZipFile& zip) {
+} // namespace
+
+Result<uint32_t> GetPackageCrc(const ZipFile& zip) {
const Result<uint32_t> a = zip.Crc("resources.arsc");
const Result<uint32_t> b = zip.Crc("AndroidManifest.xml");
return a && b
@@ -127,22 +110,59 @@
: Error("failed to get CRC for \"%s\"", a ? "AndroidManifest.xml" : "resources.arsc");
}
-} // namespace
-
std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) {
std::unique_ptr<IdmapHeader> idmap_header(new IdmapHeader());
-
+ uint8_t enforce_overlayable;
if (!Read32(stream, &idmap_header->magic_) || !Read32(stream, &idmap_header->version_) ||
!Read32(stream, &idmap_header->target_crc_) || !Read32(stream, &idmap_header->overlay_crc_) ||
- !ReadString(stream, idmap_header->target_path_) ||
- !ReadString(stream, idmap_header->overlay_path_)) {
+ !Read32(stream, &idmap_header->fulfilled_policies_) || !Read8(stream, &enforce_overlayable) ||
+ !ReadString256(stream, idmap_header->target_path_) ||
+ !ReadString256(stream, idmap_header->overlay_path_)) {
return nullptr;
}
+ idmap_header->enforce_overlayable_ = static_cast<bool>(enforce_overlayable);
+
+ auto debug_str = ReadString(stream);
+ if (!debug_str) {
+ return nullptr;
+ }
+ idmap_header->debug_info_ = std::move(*debug_str);
+
return std::move(idmap_header);
}
-Result<Unit> IdmapHeader::IsUpToDate() const {
+Result<Unit> IdmapHeader::IsUpToDate(const char* target_path, const char* overlay_path,
+ PolicyBitmask fulfilled_policies,
+ bool enforce_overlayable) const {
+ const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_path);
+ if (!target_zip) {
+ return Error("failed to open target %s", target_path);
+ }
+
+ const Result<uint32_t> target_crc = GetPackageCrc(*target_zip);
+ if (!target_crc) {
+ return Error("failed to get target crc");
+ }
+
+ const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_path);
+ if (!overlay_zip) {
+ return Error("failed to overlay target %s", overlay_path);
+ }
+
+ const Result<uint32_t> overlay_crc = GetPackageCrc(*overlay_zip);
+ if (!overlay_crc) {
+ return Error("failed to get overlay crc");
+ }
+
+ return IsUpToDate(target_path, overlay_path, *target_crc, *overlay_crc, fulfilled_policies,
+ enforce_overlayable);
+}
+
+Result<Unit> IdmapHeader::IsUpToDate(const char* target_path, const char* overlay_path,
+ uint32_t target_crc, uint32_t overlay_crc,
+ PolicyBitmask fulfilled_policies,
+ bool enforce_overlayable) const {
if (magic_ != kIdmapMagic) {
return Error("bad magic: actual 0x%08x, expected 0x%08x", magic_, kIdmapMagic);
}
@@ -151,34 +171,34 @@
return Error("bad version: actual 0x%08x, expected 0x%08x", version_, kIdmapCurrentVersion);
}
- const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_path_);
- if (!target_zip) {
- return Error("failed to open target %s", GetTargetPath().to_string().c_str());
- }
-
- Result<uint32_t> target_crc = GetCrc(*target_zip);
- if (!target_crc) {
- return Error("failed to get target crc");
- }
-
- if (target_crc_ != *target_crc) {
+ if (target_crc_ != target_crc) {
return Error("bad target crc: idmap version 0x%08x, file system version 0x%08x", target_crc_,
- *target_crc);
+ target_crc);
}
- const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_path_);
- if (!overlay_zip) {
- return Error("failed to open overlay %s", GetOverlayPath().to_string().c_str());
- }
-
- Result<uint32_t> overlay_crc = GetCrc(*overlay_zip);
- if (!overlay_crc) {
- return Error("failed to get overlay crc");
- }
-
- if (overlay_crc_ != *overlay_crc) {
+ if (overlay_crc_ != overlay_crc) {
return Error("bad overlay crc: idmap version 0x%08x, file system version 0x%08x", overlay_crc_,
- *overlay_crc);
+ overlay_crc);
+ }
+
+ if (fulfilled_policies_ != fulfilled_policies) {
+ return Error("bad fulfilled policies: idmap version 0x%08x, file system version 0x%08x",
+ fulfilled_policies, fulfilled_policies_);
+ }
+
+ if (enforce_overlayable != enforce_overlayable_) {
+ return Error("bad enforce overlayable: idmap version %s, file system version %s",
+ enforce_overlayable ? "true" : "false", enforce_overlayable_ ? "true" : "false");
+ }
+
+ if (strcmp(target_path, target_path_) != 0) {
+ return Error("bad target path: idmap version %s, file system version %s", target_path,
+ target_path_);
+ }
+
+ if (strcmp(overlay_path, overlay_path_) != 0) {
+ return Error("bad overlay path: idmap version %s, file system version %s", overlay_path,
+ overlay_path_);
}
return Unit{};
@@ -187,51 +207,48 @@
std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std::istream& stream) {
std::unique_ptr<IdmapData::Header> idmap_data_header(new IdmapData::Header());
- uint16_t target_package_id16;
- if (!Read16(stream, &target_package_id16) || !Read16(stream, &idmap_data_header->type_count_)) {
+ if (!Read8(stream, &idmap_data_header->target_package_id_) ||
+ !Read8(stream, &idmap_data_header->overlay_package_id_) ||
+ !Read32(stream, &idmap_data_header->target_entry_count) ||
+ !Read32(stream, &idmap_data_header->overlay_entry_count) ||
+ !Read32(stream, &idmap_data_header->string_pool_index_offset) ||
+ !Read32(stream, &idmap_data_header->string_pool_len)) {
return nullptr;
}
- idmap_data_header->target_package_id_ = target_package_id16;
return std::move(idmap_data_header);
}
-std::unique_ptr<const IdmapData::TypeEntry> IdmapData::TypeEntry::FromBinaryStream(
- std::istream& stream) {
- std::unique_ptr<IdmapData::TypeEntry> data(new IdmapData::TypeEntry());
- uint16_t target_type16;
- uint16_t overlay_type16;
- uint16_t entry_count;
- if (!Read16(stream, &target_type16) || !Read16(stream, &overlay_type16) ||
- !Read16(stream, &entry_count) || !Read16(stream, &data->entry_offset_)) {
- return nullptr;
- }
- data->target_type_id_ = target_type16;
- data->overlay_type_id_ = overlay_type16;
- for (uint16_t i = 0; i < entry_count; i++) {
- ResourceId resid;
- if (!Read32(stream, &resid)) {
- return nullptr;
- }
- data->entries_.push_back(resid);
- }
-
- return std::move(data);
-}
-
std::unique_ptr<const IdmapData> IdmapData::FromBinaryStream(std::istream& stream) {
std::unique_ptr<IdmapData> data(new IdmapData());
data->header_ = IdmapData::Header::FromBinaryStream(stream);
if (!data->header_) {
return nullptr;
}
- for (size_t type_count = 0; type_count < data->header_->GetTypeCount(); type_count++) {
- std::unique_ptr<const TypeEntry> type = IdmapData::TypeEntry::FromBinaryStream(stream);
- if (!type) {
+ // Read the mapping of target resource id to overlay resource value.
+ for (size_t i = 0; i < data->header_->GetTargetEntryCount(); i++) {
+ TargetEntry target_entry{};
+ if (!Read32(stream, &target_entry.target_id) || !Read8(stream, &target_entry.data_type) ||
+ !Read32(stream, &target_entry.data_value)) {
return nullptr;
}
- data->type_entries_.push_back(std::move(type));
+ data->target_entries_.emplace_back(target_entry);
}
+
+ // Read the mapping of overlay resource id to target resource id.
+ for (size_t i = 0; i < data->header_->GetOverlayEntryCount(); i++) {
+ OverlayEntry overlay_entry{};
+ if (!Read32(stream, &overlay_entry.overlay_id) || !Read32(stream, &overlay_entry.target_id)) {
+ return nullptr;
+ }
+ data->overlay_entries_.emplace_back(overlay_entry);
+ }
+
+ // Read raw string pool bytes.
+ if (!ReadBuffer(stream, &data->string_pool_, data->header_->string_pool_len)) {
+ return nullptr;
+ }
+
return std::move(data);
}
@@ -266,95 +283,45 @@
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;
+ std::unique_ptr<IdmapData> data(new IdmapData());
+ for (const auto& mappings : resource_mapping.GetTargetToOverlayMap()) {
+ data->target_entries_.emplace_back(IdmapData::TargetEntry{
+ mappings.first, mappings.second.data_type, mappings.second.data_value});
+ }
+
+ for (const auto& mappings : resource_mapping.GetOverlayToTargetMap()) {
+ data->overlay_entries_.emplace_back(IdmapData::OverlayEntry{mappings.first, mappings.second});
+ }
+
+ std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
+ data_header->target_package_id_ = resource_mapping.GetTargetPackageId();
+ data_header->overlay_package_id_ = resource_mapping.GetOverlayPackageId();
+ data_header->target_entry_count = static_cast<uint32_t>(data->target_entries_.size());
+ data_header->overlay_entry_count = static_cast<uint32_t>(data->overlay_entries_.size());
+ data_header->string_pool_index_offset = resource_mapping.GetStringPoolOffset();
+
+ const auto string_pool_data = resource_mapping.GetStringPoolData();
+ data_header->string_pool_len = string_pool_data.second;
+ data->string_pool_ = std::unique_ptr<uint8_t[]>(new uint8_t[data_header->string_pool_len]);
+ memcpy(data->string_pool_.get(), string_pool_data.first, data_header->string_pool_len);
+
+ data->header_ = std::move(data_header);
+ return {std::move(data)};
}
-Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
- const utils::OverlayManifestInfo& overlay_info,
- const PolicyBitmask& fulfilled_policies, const ResourceId& resid) {
- static constexpr const PolicyBitmask sDefaultPolicies =
- PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION |
- PolicyFlags::POLICY_PRODUCT_PARTITION | PolicyFlags::POLICY_SIGNATURE;
-
- // If the resource does not have an overlayable definition, allow the resource to be overlaid if
- // the overlay is preinstalled or signed with the same signature as the target.
- if (!target_package.DefinesOverlayable()) {
- return (sDefaultPolicies & fulfilled_policies) != 0
- ? Result<Unit>({})
- : Error(
- "overlay must be preinstalled or signed with the same signature as the "
- "target");
- }
-
- const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(resid);
- if (overlayable_info == nullptr) {
- // Do not allow non-overlayable resources to be overlaid.
- return Error("resource has no overlayable declaration");
- }
-
- if (overlay_info.target_name != overlayable_info->name) {
- // If the overlay supplies a target overlayable name, the resource must belong to the
- // overlayable defined with the specified name to be overlaid.
- return Error("<overlay> android:targetName '%s' does not match overlayable name '%s'",
- overlay_info.target_name.c_str(), overlayable_info->name.c_str());
- }
-
- // Enforce policy restrictions if the resource is declared as overlayable.
- if ((overlayable_info->policy_flags & fulfilled_policies) == 0) {
- return Error("overlay with policies '%s' does not fulfill any overlayable policies '%s'",
- ConcatPolicies(BitmaskToPolicies(fulfilled_policies)).c_str(),
- ConcatPolicies(BitmaskToPolicies(overlayable_info->policy_flags)).c_str());
- }
-
- return Result<Unit>({});
-}
-
-Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& target_apk_path,
- const ApkAssets& target_apk_assets,
- const std::string& overlay_apk_path,
+Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const ApkAssets& target_apk_assets,
const ApkAssets& overlay_apk_assets,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable) {
SYSTRACE << "Idmap::FromApkAssets";
- AssetManager2 target_asset_manager;
- if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true, false)) {
- return Error("failed to create target asset manager");
- }
-
- AssetManager2 overlay_asset_manager;
- if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets}, true, false)) {
- return Error("failed to create overlay asset manager");
- }
-
- const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
- if (target_arsc == nullptr) {
- return Error("failed to load target resources.arsc");
- }
-
- const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
- if (overlay_arsc == nullptr) {
- return Error("failed to load overlay resources.arsc");
- }
-
- const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
- if (target_pkg == nullptr) {
- return Error("failed to load target package from resources.arsc");
- }
-
- const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
- if (overlay_pkg == nullptr) {
- return Error("failed to load overlay package from resources.arsc");
- }
+ const std::string& target_apk_path = target_apk_assets.GetPath();
+ const std::string& overlay_apk_path = overlay_apk_assets.GetPath();
const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_apk_path);
if (!target_zip) {
@@ -366,27 +333,25 @@
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;
- Result<uint32_t> crc = GetCrc(*target_zip);
+ Result<uint32_t> crc = GetPackageCrc(*target_zip);
if (!crc) {
return Error(crc.GetError(), "failed to get zip CRC for target");
}
header->target_crc_ = *crc;
- crc = GetCrc(*overlay_zip);
+ crc = GetPackageCrc(*overlay_zip);
if (!crc) {
return Error(crc.GetError(), "failed to get zip CRC for overlay");
}
header->overlay_crc_ = *crc;
+ header->fulfilled_policies_ = fulfilled_policies;
+ header->enforce_overlayable_ = enforce_overlayable;
+
if (target_apk_path.size() > sizeof(header->target_path_)) {
return Error("target apk path \"%s\" longer than maximum size %zu", target_apk_path.c_str(),
sizeof(header->target_path_));
@@ -395,78 +360,34 @@
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_));
memcpy(header->overlay_path_, overlay_apk_path.data(), overlay_apk_path.size());
+ auto overlay_info = utils::ExtractOverlayManifestInfo(overlay_apk_path);
+ if (!overlay_info) {
+ return overlay_info.GetError();
+ }
+
+ LogInfo log_info;
+ auto resource_mapping =
+ ResourceMapping::FromApkAssets(target_apk_assets, overlay_apk_assets, *overlay_info,
+ fulfilled_policies, enforce_overlayable, log_info);
+ if (!resource_mapping) {
+ return resource_mapping.GetError();
+ }
+
+ auto idmap_data = IdmapData::FromResourceMapping(*resource_mapping);
+ if (!idmap_data) {
+ return idmap_data.GetError();
+ }
+
std::unique_ptr<Idmap> idmap(new Idmap());
+ header->debug_info_ = log_info.GetString();
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);
- }
-
- if (matching_resources.Map().empty()) {
- return Error("overlay \"%s\" does not successfully overlay any resource",
- overlay_apk_path.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));
- }
-
- 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)};
}
@@ -481,25 +402,16 @@
v->visit(*this);
}
-void IdmapData::TypeEntry::accept(Visitor* v) const {
- assert(v != nullptr);
- v->visit(*this);
-}
-
void IdmapData::accept(Visitor* v) const {
assert(v != nullptr);
- v->visit(*this);
header_->accept(v);
- auto end = type_entries_.cend();
- for (auto iter = type_entries_.cbegin(); iter != end; ++iter) {
- (*iter)->accept(v);
- }
+ v->visit(*this);
}
void Idmap::accept(Visitor* v) const {
assert(v != nullptr);
- v->visit(*this);
header_->accept(v);
+ v->visit(*this);
auto end = data_.cend();
for (auto iter = data_.cbegin(); iter != end; ++iter) {
(*iter)->accept(v);
diff --git a/cmds/idmap2/libidmap2/Policies.cpp b/cmds/idmap2/libidmap2/Policies.cpp
deleted file mode 100644
index 495fe61..0000000
--- a/cmds/idmap2/libidmap2/Policies.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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/Policies.h"
-
-#include <iterator>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include "androidfw/ResourceTypes.h"
-#include "idmap2/Idmap.h"
-#include "idmap2/Result.h"
-
-namespace android::idmap2 {
-
-Result<PolicyBitmask> PoliciesToBitmask(const std::vector<std::string>& policies) {
- static const std::unordered_map<android::StringPiece, PolicyFlags> kStringToFlag = {
- {kPolicyOdm, PolicyFlags::POLICY_ODM_PARTITION},
- {kPolicyOem, PolicyFlags::POLICY_OEM_PARTITION},
- {kPolicyPublic, PolicyFlags::POLICY_PUBLIC},
- {kPolicyProduct, PolicyFlags::POLICY_PRODUCT_PARTITION},
- {kPolicySignature, PolicyFlags::POLICY_SIGNATURE},
- {kPolicySystem, PolicyFlags::POLICY_SYSTEM_PARTITION},
- {kPolicyVendor, PolicyFlags::POLICY_VENDOR_PARTITION},
- };
-
- PolicyBitmask bitmask = 0;
- for (const std::string& policy : policies) {
- const auto iter = kStringToFlag.find(policy);
- if (iter != kStringToFlag.end()) {
- bitmask |= iter->second;
- } else {
- return Error("unknown policy \"%s\"", policy.c_str());
- }
- }
-
- return Result<PolicyBitmask>(bitmask);
-}
-
-std::vector<std::string> BitmaskToPolicies(const PolicyBitmask& bitmask) {
- std::vector<std::string> policies;
-
- if ((bitmask & PolicyFlags::POLICY_ODM_PARTITION) != 0) {
- policies.emplace_back(kPolicyOdm);
- }
-
- if ((bitmask & PolicyFlags::POLICY_OEM_PARTITION) != 0) {
- policies.emplace_back(kPolicyOem);
- }
-
- if ((bitmask & PolicyFlags::POLICY_PUBLIC) != 0) {
- policies.emplace_back(kPolicyPublic);
- }
-
- if ((bitmask & PolicyFlags::POLICY_PRODUCT_PARTITION) != 0) {
- policies.emplace_back(kPolicyProduct);
- }
-
- if ((bitmask & PolicyFlags::POLICY_SIGNATURE) != 0) {
- policies.emplace_back(kPolicySignature);
- }
-
- if ((bitmask & PolicyFlags::POLICY_SYSTEM_PARTITION) != 0) {
- policies.emplace_back(kPolicySystem);
- }
-
- if ((bitmask & PolicyFlags::POLICY_VENDOR_PARTITION) != 0) {
- policies.emplace_back(kPolicyVendor);
- }
-
- return policies;
-}
-
-} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/PolicyUtils.cpp b/cmds/idmap2/libidmap2/PolicyUtils.cpp
new file mode 100644
index 0000000..fc5182a
--- /dev/null
+++ b/cmds/idmap2/libidmap2/PolicyUtils.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 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 "include/idmap2/PolicyUtils.h"
+
+#include <sstream>
+
+#include "android-base/strings.h"
+#include "idmap2/Policies.h"
+
+using android::idmap2::policy::kPolicyStringToFlag;
+
+namespace android::idmap2::utils {
+
+Result<PolicyBitmask> PoliciesToBitmaskResult(const std::vector<std::string>& policies) {
+ std::vector<std::string> unknown_policies;
+ PolicyBitmask bitmask = 0;
+ for (const std::string& policy : policies) {
+ const auto result = std::find_if(kPolicyStringToFlag.begin(), kPolicyStringToFlag.end(),
+ [policy](const auto& it) { return policy == it.first; });
+ if (result != kPolicyStringToFlag.end()) {
+ bitmask |= result->second;
+ } else {
+ unknown_policies.emplace_back(policy.empty() ? "empty" : policy);
+ }
+ }
+
+ if (unknown_policies.empty()) {
+ return Result<PolicyBitmask>(bitmask);
+ }
+
+ auto prefix = unknown_policies.size() == 1 ? "policy" : "policies";
+ return Error("unknown %s: \"%s\"", prefix, android::base::Join(unknown_policies, ",").c_str());
+}
+
+std::vector<std::string> BitmaskToPolicies(const PolicyBitmask& bitmask) {
+ std::vector<std::string> policies;
+
+ for (const auto& policy : kPolicyStringToFlag) {
+ if ((bitmask & policy.second) != 0) {
+ policies.emplace_back(policy.first.to_string());
+ }
+ }
+
+ return policies;
+}
+
+} // namespace android::idmap2::utils
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
index fbf2c77..63ee8a6 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -16,6 +16,7 @@
#include "idmap2/PrettyPrintVisitor.h"
+#include <istream>
#include <string>
#include "android-base/macros.h"
@@ -28,42 +29,59 @@
#define RESID(pkg, type, entry) (((pkg) << 24) | ((type) << 16) | (entry))
+#define TAB " "
+
void PrettyPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
}
void PrettyPrintVisitor::visit(const IdmapHeader& header) {
- stream_ << "target apk path : " << header.GetTargetPath() << std::endl
- << "overlay apk path : " << header.GetOverlayPath() << std::endl;
+ stream_ << "Paths:" << std::endl
+ << TAB "target apk path : " << header.GetTargetPath() << std::endl
+ << TAB "overlay apk path : " << header.GetOverlayPath() << std::endl;
+ const std::string& debug = header.GetDebugInfo();
+ if (!debug.empty()) {
+ std::istringstream debug_stream(debug);
+ std::string line;
+ stream_ << "Debug info:" << std::endl;
+ while (std::getline(debug_stream, line)) {
+ stream_ << TAB << line << std::endl;
+ }
+ }
target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string());
if (target_apk_) {
target_am_.SetApkAssets({target_apk_.get()});
}
-}
-
-void PrettyPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
+ stream_ << "Mapping:" << std::endl;
}
void PrettyPrintVisitor::visit(const IdmapData::Header& header ATTRIBUTE_UNUSED) {
- last_seen_package_id_ = header.GetTargetPackageId();
}
-void PrettyPrintVisitor::visit(const IdmapData::TypeEntry& type_entry) {
+void PrettyPrintVisitor::visit(const IdmapData& data) {
const bool target_package_loaded = !target_am_.GetApkAssets().empty();
- for (uint16_t i = 0; i < type_entry.GetEntryCount(); i++) {
- const EntryId entry = type_entry.GetEntry(i);
- if (entry == kNoEntry) {
- continue;
+ const ResStringPool string_pool(data.GetStringPoolData(),
+ data.GetHeader()->GetStringPoolLength());
+ const size_t string_pool_offset = data.GetHeader()->GetStringPoolIndexOffset();
+
+ for (auto& target_entry : data.GetTargetEntries()) {
+ stream_ << TAB << base::StringPrintf("0x%08x ->", target_entry.target_id);
+
+ if (target_entry.data_type != Res_value::TYPE_REFERENCE &&
+ target_entry.data_type != Res_value::TYPE_DYNAMIC_REFERENCE) {
+ stream_ << " " << utils::DataTypeToString(target_entry.data_type);
}
- const ResourceId target_resid =
- RESID(last_seen_package_id_, type_entry.GetTargetTypeId(), type_entry.GetEntryOffset() + i);
- const ResourceId overlay_resid =
- RESID(last_seen_package_id_, type_entry.GetOverlayTypeId(), entry);
+ if (target_entry.data_type == Res_value::TYPE_STRING) {
+ stream_ << " \""
+ << string_pool.string8ObjectAt(target_entry.data_value - string_pool_offset).c_str()
+ << "\"";
+ } else {
+ stream_ << " " << base::StringPrintf("0x%08x", target_entry.data_value);
+ }
- stream_ << base::StringPrintf("0x%08x -> 0x%08x", target_resid, overlay_resid);
if (target_package_loaded) {
- Result<std::string> name = utils::ResToTypeEntryName(target_am_, target_resid);
+ Result<std::string> name = utils::ResToTypeEntryName(target_am_, target_entry.target_id);
if (name) {
stream_ << " " << *name;
}
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index dd14fd4..3f62a2a 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -16,22 +16,31 @@
#include "idmap2/RawPrintVisitor.h"
+#include <algorithm>
#include <cstdarg>
#include <string>
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
#include "androidfw/ApkAssets.h"
+#include "idmap2/PolicyUtils.h"
#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
using android::ApkAssets;
+using android::idmap2::policy::PoliciesToDebugString;
+
+namespace {
+
+size_t StringSizeWhenEncoded(const std::string& s) {
+ size_t null_bytes = 4 - (s.size() % 4);
+ return sizeof(uint32_t) + s.size() + null_bytes;
+}
+
+} // namespace
namespace android::idmap2 {
-// verbatim copy fomr PrettyPrintVisitor.cpp, move to common utils
-#define RESID(pkg, type, entry) (((pkg) << 24) | ((type) << 16) | (entry))
-
void RawPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
}
@@ -40,53 +49,105 @@
print(header.GetVersion(), "version");
print(header.GetTargetCrc(), "target crc");
print(header.GetOverlayCrc(), "overlay crc");
- print(header.GetTargetPath().to_string(), "target path");
- print(header.GetOverlayPath().to_string(), "overlay path");
+ print(header.GetFulfilledPolicies(), "fulfilled policies: %s",
+ PoliciesToDebugString(header.GetFulfilledPolicies()).c_str());
+ print(static_cast<uint8_t>(header.GetEnforceOverlayable()), "enforce overlayable");
+ print(header.GetTargetPath().to_string(), kIdmapStringLength, "target path");
+ print(header.GetOverlayPath().to_string(), kIdmapStringLength, "overlay path");
+ print("...", StringSizeWhenEncoded(header.GetDebugInfo()), "debug info");
target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string());
if (target_apk_) {
target_am_.SetApkAssets({target_apk_.get()});
}
+
+ overlay_apk_ = ApkAssets::Load(header.GetOverlayPath().to_string());
+ if (overlay_apk_) {
+ overlay_am_.SetApkAssets({overlay_apk_.get()});
+ }
}
void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
+ const bool target_package_loaded = !target_am_.GetApkAssets().empty();
+ const bool overlay_package_loaded = !overlay_am_.GetApkAssets().empty();
+
+ for (auto& target_entry : data.GetTargetEntries()) {
+ Result<std::string> target_name(Error(""));
+ if (target_package_loaded) {
+ target_name = utils::ResToTypeEntryName(target_am_, target_entry.target_id);
+ }
+ if (target_name) {
+ print(target_entry.target_id, "target id: %s", target_name->c_str());
+ } else {
+ print(target_entry.target_id, "target id");
+ }
+
+ print(target_entry.data_type, "type: %s",
+ utils::DataTypeToString(target_entry.data_type).data());
+
+ Result<std::string> overlay_name(Error(""));
+ if (overlay_package_loaded && (target_entry.data_type == Res_value::TYPE_REFERENCE ||
+ target_entry.data_type == Res_value::TYPE_DYNAMIC_REFERENCE)) {
+ overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.data_value);
+ }
+ if (overlay_name) {
+ print(target_entry.data_value, "value: %s", overlay_name->c_str());
+ } else {
+ print(target_entry.data_value, "value");
+ }
+ }
+
+ for (auto& overlay_entry : data.GetOverlayEntries()) {
+ Result<std::string> overlay_name(Error(""));
+ if (overlay_package_loaded) {
+ overlay_name = utils::ResToTypeEntryName(overlay_am_, overlay_entry.overlay_id);
+ }
+
+ if (overlay_name) {
+ print(overlay_entry.overlay_id, "overlay id: %s", overlay_name->c_str());
+ } else {
+ print(overlay_entry.overlay_id, "overlay id");
+ }
+
+ Result<std::string> target_name(Error(""));
+ if (target_package_loaded) {
+ target_name = utils::ResToTypeEntryName(target_am_, overlay_entry.target_id);
+ }
+
+ if (target_name) {
+ print(overlay_entry.target_id, "target id: %s", target_name->c_str());
+ } else {
+ print(overlay_entry.target_id, "target id");
+ }
+ }
+
+ const size_t string_pool_length = data.GetHeader()->GetStringPoolLength();
+ if (string_pool_length > 0) {
+ print_raw(string_pool_length, "%zu raw string pool bytes", string_pool_length);
+ }
}
void RawPrintVisitor::visit(const IdmapData::Header& header) {
- print(static_cast<uint16_t>(header.GetTargetPackageId()), "target package id");
- print(header.GetTypeCount(), "type count");
- last_seen_package_id_ = header.GetTargetPackageId();
+ print(header.GetTargetPackageId(), "target package id");
+ print(header.GetOverlayPackageId(), "overlay package id");
+ print(header.GetTargetEntryCount(), "target entry count");
+ print(header.GetOverlayEntryCount(), "overlay entry count");
+ print(header.GetStringPoolIndexOffset(), "string pool index offset");
+ print(header.GetStringPoolLength(), "string pool byte length");
}
-void RawPrintVisitor::visit(const IdmapData::TypeEntry& type_entry) {
- const bool target_package_loaded = !target_am_.GetApkAssets().empty();
+// NOLINTNEXTLINE(cert-dcl50-cpp)
+void RawPrintVisitor::print(uint8_t value, const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ std::string comment;
+ base::StringAppendV(&comment, fmt, ap);
+ va_end(ap);
- print(static_cast<uint16_t>(type_entry.GetTargetTypeId()), "target type");
- print(static_cast<uint16_t>(type_entry.GetOverlayTypeId()), "overlay type");
- print(static_cast<uint16_t>(type_entry.GetEntryCount()), "entry count");
- print(static_cast<uint16_t>(type_entry.GetEntryOffset()), "entry offset");
+ stream_ << base::StringPrintf("%08zx: %02x", offset_, value) << " " << comment
+ << std::endl;
- for (uint16_t i = 0; i < type_entry.GetEntryCount(); i++) {
- const EntryId entry = type_entry.GetEntry(i);
- if (entry == kNoEntry) {
- print(kPadding, "no entry");
- } else {
- const ResourceId target_resid = RESID(last_seen_package_id_, type_entry.GetTargetTypeId(),
- type_entry.GetEntryOffset() + i);
- const ResourceId overlay_resid =
- RESID(last_seen_package_id_, type_entry.GetOverlayTypeId(), entry);
- Result<std::string> name(Error(""));
- if (target_package_loaded) {
- name = utils::ResToTypeEntryName(target_am_, target_resid);
- }
- if (name) {
- print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x %s", target_resid, overlay_resid,
- name->c_str());
- } else {
- print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x", target_resid, overlay_resid);
- }
- }
- }
+ offset_ += sizeof(uint8_t);
}
// NOLINTNEXTLINE(cert-dcl50-cpp)
@@ -116,17 +177,30 @@
}
// NOLINTNEXTLINE(cert-dcl50-cpp)
-void RawPrintVisitor::print(const std::string& value, const char* fmt, ...) {
+void RawPrintVisitor::print(const std::string& value, size_t encoded_size, const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
std::string comment;
base::StringAppendV(&comment, fmt, ap);
va_end(ap);
- stream_ << base::StringPrintf("%08zx: ", offset_) << "........ " << comment << ": " << value
+ stream_ << base::StringPrintf("%08zx: ", offset_) << "........ " << comment << ": " << value
<< std::endl;
- offset_ += kIdmapStringLength;
+ offset_ += encoded_size;
+}
+
+// NOLINTNEXTLINE(cert-dcl50-cpp)
+void RawPrintVisitor::print_raw(uint32_t length, const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ std::string comment;
+ base::StringAppendV(&comment, fmt, ap);
+ va_end(ap);
+
+ stream_ << base::StringPrintf("%08zx: ", offset_) << "........ " << comment << std::endl;
+
+ offset_ += length;
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
new file mode 100644
index 0000000..34589a1
--- /dev/null
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -0,0 +1,438 @@
+/*
+ * 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 "androidfw/ResourceTypes.h"
+#include "idmap2/PolicyUtils.h"
+#include "idmap2/ResourceUtils.h"
+
+using android::base::StringPrintf;
+using android::idmap2::utils::BitmaskToPolicies;
+using android::idmap2::utils::IsReference;
+using android::idmap2::utils::ResToTypeEntryName;
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
+namespace android::idmap2 {
+
+namespace {
+
+#define REWRITE_PACKAGE(resid, package_id) \
+ (((resid)&0x00ffffffU) | (((uint32_t)(package_id)) << 24U))
+#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::ODM_PARTITION | PolicyFlags::OEM_PARTITION | PolicyFlags::SYSTEM_PARTITION |
+ PolicyFlags::VENDOR_PARTITION | PolicyFlags::PRODUCT_PARTITION | PolicyFlags::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,
+ LogInfo& log_info) {
+ 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 target_package_id = target_package->GetPackageId();
+ 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_info.Warning(LogMessage() << "failed to find resource \"" << *target_resource
+ << "\" in target resources");
+ continue;
+ }
+
+ // Retrieve the compile-time resource id of the target resource.
+ target_id = REWRITE_PACKAGE(target_id, target_package_id);
+
+ 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 =
+ IsReference(overlay_resource->dataType)
+ ? overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data)
+ : false;
+
+ if (rewrite_overlay_reference) {
+ overlay_resource->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
+ }
+
+ 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 uint8_t target_package_id = target_package->GetPackageId();
+ 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());
+ ResourceId target_resource = target_am->GetResourceId(full_name);
+ if (target_resource == 0U) {
+ continue;
+ }
+
+ // Retrieve the compile-time resource id of the target resource.
+ target_resource = REWRITE_PACKAGE(target_resource, target_package_id);
+
+ resource_mapping.AddMapping(target_resource, Res_value::TYPE_REFERENCE, overlay_resid,
+ /* rewrite_overlay_reference */ false);
+ }
+
+ 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,
+ LogInfo& log_info) {
+ 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_info.Warning(LogMessage() << "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,
+ LogInfo& log_info) {
+ 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) {
+ // Use the dynamic reference table to find the assigned resource id of the map xml.
+ const auto& ref_table = overlay_asset_manager.GetDynamicRefTableForCookie(0);
+ uint32_t resource_mapping_id = overlay_info.resource_mapping;
+ ref_table->lookupResourceId(&resource_mapping_id);
+
+ // Load the overlay resource mappings from the file specified using android:resourcesMap.
+ auto asset = OpenNonAssetFromResource(resource_mapping_id, 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), log_info);
+ } 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, log_info);
+ }
+
+ 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 && IsReference(data_type)) {
+ 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);
+
+ if (!IsReference(value.data_type)) {
+ 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 dce83e3..98d026b 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -22,18 +22,56 @@
#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) {
+bool IsReference(uint8_t data_type) {
+ return data_type == Res_value::TYPE_REFERENCE || data_type == Res_value::TYPE_DYNAMIC_REFERENCE;
+}
+
+StringPiece DataTypeToString(uint8_t data_type) {
+ switch (data_type) {
+ case Res_value::TYPE_NULL:
+ return "null";
+ case Res_value::TYPE_REFERENCE:
+ return "reference";
+ case Res_value::TYPE_ATTRIBUTE:
+ return "attribute";
+ case Res_value::TYPE_STRING:
+ return "string";
+ case Res_value::TYPE_FLOAT:
+ return "float";
+ case Res_value::TYPE_DIMENSION:
+ return "dimension";
+ case Res_value::TYPE_FRACTION:
+ return "fraction";
+ case Res_value::TYPE_DYNAMIC_REFERENCE:
+ return "reference (dynamic)";
+ case Res_value::TYPE_DYNAMIC_ATTRIBUTE:
+ return "attribute (dynamic)";
+ case Res_value::TYPE_INT_DEC:
+ case Res_value::TYPE_INT_HEX:
+ return "integer";
+ case Res_value::TYPE_INT_BOOLEAN:
+ return "boolean";
+ case Res_value::TYPE_INT_COLOR_ARGB8:
+ case Res_value::TYPE_INT_COLOR_RGB8:
+ case Res_value::TYPE_INT_COLOR_RGB4:
+ return "color";
+ default:
+ return "unknown";
+ }
+}
+
+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 +103,72 @@
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 (IsReference((*result_value).dataType)) {
+ 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());
+ }
}
- iter = tag->find("requiredSystemPropertyName");
- if (iter != tag->end()) {
- info.requiredSystemPropertyName = iter->second;
+ 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("requiredSystemPropertyValue");
- if (iter != tag->end()) {
- info.requiredSystemPropertyValue = iter->second;
+ if (auto result_str = overlay_it->GetAttributeStringValue("requiredSystemPropertyName")) {
+ info.requiredSystemPropertyName = *result_str;
+ }
+
+ 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 2645868..0000000
--- 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 0000000..526a560
--- /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/libidmap2/ZipFile.cpp b/cmds/idmap2/libidmap2/ZipFile.cpp
index 4f5e3a4..1e1a218 100644
--- a/cmds/idmap2/libidmap2/ZipFile.cpp
+++ b/cmds/idmap2/libidmap2/ZipFile.cpp
@@ -34,6 +34,7 @@
::ZipArchiveHandle handle;
int32_t status = ::OpenArchive(path.c_str(), &handle);
if (status != 0) {
+ ::CloseArchive(handle);
return nullptr;
}
return std::unique_ptr<ZipFile>(new ZipFile(handle));
diff --git a/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h b/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h
new file mode 100644
index 0000000..5bd353a
--- /dev/null
+++ b/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h
@@ -0,0 +1,79 @@
+/*
+ * 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_POLICIES_H_
+#define IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
+
+#include <array>
+#include <string>
+#include <vector>
+
+#include "android-base/stringprintf.h"
+#include "androidfw/ResourceTypes.h"
+#include "androidfw/StringPiece.h"
+
+using android::base::StringPrintf;
+
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
+namespace android::idmap2::policy {
+
+constexpr const char* kPolicyActor = "actor";
+constexpr const char* kPolicyOdm = "odm";
+constexpr const char* kPolicyOem = "oem";
+constexpr const char* kPolicyProduct = "product";
+constexpr const char* kPolicyPublic = "public";
+constexpr const char* kPolicySignature = "signature";
+constexpr const char* kPolicySystem = "system";
+constexpr const char* kPolicyVendor = "vendor";
+
+inline static const std::array<std::pair<StringPiece, PolicyFlags>, 8> kPolicyStringToFlag = {
+ std::pair{kPolicyActor, PolicyFlags::ACTOR_SIGNATURE},
+ {kPolicyOdm, PolicyFlags::ODM_PARTITION},
+ {kPolicyOem, PolicyFlags::OEM_PARTITION},
+ {kPolicyProduct, PolicyFlags::PRODUCT_PARTITION},
+ {kPolicyPublic, PolicyFlags::PUBLIC},
+ {kPolicySignature, PolicyFlags::SIGNATURE},
+ {kPolicySystem, PolicyFlags::SYSTEM_PARTITION},
+ {kPolicyVendor, PolicyFlags::VENDOR_PARTITION},
+};
+
+inline static std::string PoliciesToDebugString(PolicyBitmask policies) {
+ std::string str;
+ uint32_t remaining = policies;
+ for (auto const& policy : kPolicyStringToFlag) {
+ if ((policies & policy.second) != policy.second) {
+ continue;
+ }
+ if (!str.empty()) {
+ str.append("|");
+ }
+ str.append(policy.first.data());
+ remaining &= ~policy.second;
+ }
+ if (remaining != 0) {
+ if (!str.empty()) {
+ str.append("|");
+ }
+ str.append(StringPrintf("0x%08x", remaining));
+ }
+ return !str.empty() ? str : "none";
+}
+
+} // namespace android::idmap2::policy
+
+#endif // IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
diff --git a/cmds/idmap2/static-checks.sh b/cmds/idmap2/static-checks.sh
index 41d3c69..a372abd 100755
--- a/cmds/idmap2/static-checks.sh
+++ b/cmds/idmap2/static-checks.sh
@@ -27,10 +27,11 @@
local red="\e[31m"
local green="\e[32m"
local reset="\e[0m"
+ local output
_log "${green}[ RUN ]${reset} ${label}"
- local output="$(eval "$cmd")"
- if [[ -z "${output}" ]]; then
+ output="$(eval "$cmd" 2>&1)"
+ if [[ $? -eq 0 ]]; then
_log "${green}[ OK ]${reset} ${label}"
return 0
else
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
index 9348ab7..5fea7bc 100644
--- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -18,6 +18,7 @@
#include <sstream>
#include <string>
#include <utility>
+#include <vector>
#include "TestHelpers.h"
#include "androidfw/ApkAssets.h"
@@ -47,118 +48,49 @@
ASSERT_TRUE(result2);
const auto idmap2 = std::move(*result2);
+ ASSERT_EQ(idmap1->GetHeader()->GetFulfilledPolicies(),
+ idmap2->GetHeader()->GetFulfilledPolicies());
+ ASSERT_EQ(idmap1->GetHeader()->GetEnforceOverlayable(),
+ idmap2->GetHeader()->GetEnforceOverlayable());
+ ASSERT_EQ(idmap1->GetHeader()->GetTargetPath(), idmap2->GetHeader()->GetTargetPath());
ASSERT_EQ(idmap1->GetHeader()->GetTargetCrc(), idmap2->GetHeader()->GetTargetCrc());
ASSERT_EQ(idmap1->GetHeader()->GetTargetPath(), idmap2->GetHeader()->GetTargetPath());
ASSERT_EQ(idmap1->GetData().size(), 1U);
ASSERT_EQ(idmap1->GetData().size(), idmap2->GetData().size());
- const auto& data1 = idmap1->GetData()[0];
- const auto& data2 = idmap2->GetData()[0];
+ const std::vector<std::unique_ptr<const IdmapData>>& data_blocks1 = idmap1->GetData();
+ ASSERT_EQ(data_blocks1.size(), 1U);
+ const std::unique_ptr<const IdmapData>& data1 = data_blocks1[0];
+ ASSERT_THAT(data1, NotNull());
- ASSERT_EQ(data1->GetHeader()->GetTargetPackageId(), data2->GetHeader()->GetTargetPackageId());
- ASSERT_EQ(data1->GetTypeEntries().size(), 2U);
- ASSERT_EQ(data1->GetTypeEntries().size(), data2->GetTypeEntries().size());
- ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(0), data2->GetTypeEntries()[0]->GetEntry(0));
- ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(1), data2->GetTypeEntries()[0]->GetEntry(1));
- ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(2), data2->GetTypeEntries()[0]->GetEntry(2));
- ASSERT_EQ(data1->GetTypeEntries()[1]->GetEntry(0), data2->GetTypeEntries()[1]->GetEntry(0));
- ASSERT_EQ(data1->GetTypeEntries()[1]->GetEntry(1), data2->GetTypeEntries()[1]->GetEntry(1));
- ASSERT_EQ(data1->GetTypeEntries()[1]->GetEntry(2), data2->GetTypeEntries()[1]->GetEntry(2));
-}
+ const std::vector<std::unique_ptr<const IdmapData>>& data_blocks2 = idmap2->GetData();
+ ASSERT_EQ(data_blocks2.size(), 1U);
+ const std::unique_ptr<const IdmapData>& data2 = data_blocks2[0];
+ ASSERT_THAT(data2, NotNull());
-TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) {
- const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
- std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
- ASSERT_THAT(target_apk, NotNull());
+ const auto& target_entries1 = data1->GetTargetEntries();
+ const auto& target_entries2 = data2->GetTargetEntries();
+ ASSERT_EQ(target_entries1.size(), target_entries2.size());
+ ASSERT_EQ(target_entries1[0].target_id, target_entries2[0].target_id);
+ ASSERT_EQ(target_entries1[0].data_value, target_entries2[0].data_value);
- const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
- std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
- ASSERT_THAT(overlay_apk, NotNull());
+ ASSERT_EQ(target_entries1[1].target_id, target_entries2[1].target_id);
+ ASSERT_EQ(target_entries1[1].data_value, target_entries2[1].data_value);
- const auto idmap =
- Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
- ASSERT_TRUE(idmap);
+ ASSERT_EQ(target_entries1[2].target_id, target_entries2[2].target_id);
+ ASSERT_EQ(target_entries1[2].data_value, target_entries2[2].data_value);
- std::stringstream stream;
- BinaryStreamVisitor visitor(stream);
- (*idmap)->accept(&visitor);
- const std::string str = stream.str();
- const StringPiece data(str);
- std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(data);
- ASSERT_THAT(loaded_idmap, NotNull());
- ASSERT_EQ(loaded_idmap->TargetPackageId(), 0x7f);
+ const auto& overlay_entries1 = data1->GetOverlayEntries();
+ const auto& overlay_entries2 = data2->GetOverlayEntries();
+ ASSERT_EQ(overlay_entries1.size(), overlay_entries2.size());
+ ASSERT_EQ(overlay_entries1[0].overlay_id, overlay_entries2[0].overlay_id);
+ ASSERT_EQ(overlay_entries1[0].target_id, overlay_entries2[0].target_id);
- const IdmapEntry_header* header = loaded_idmap->GetEntryMapForType(0x01);
- ASSERT_THAT(header, NotNull());
+ ASSERT_EQ(overlay_entries1[1].overlay_id, overlay_entries2[1].overlay_id);
+ ASSERT_EQ(overlay_entries1[1].target_id, overlay_entries2[1].target_id);
- EntryId entry;
- bool success = LoadedIdmap::Lookup(header, 0x0000, &entry);
- ASSERT_TRUE(success);
- ASSERT_EQ(entry, 0x0000);
-
- header = loaded_idmap->GetEntryMapForType(0x02);
- ASSERT_THAT(header, NotNull());
-
- success = LoadedIdmap::Lookup(header, 0x0000, &entry); // string/a
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0001, &entry); // string/b
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0002, &entry); // string/c
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0003, &entry); // string/policy_odm
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0004, &entry); // string/policy_oem
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0005, &entry); // string/other
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0006, &entry); // string/not_overlayable
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0007, &entry); // string/policy_product
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0008, &entry); // string/policy_public
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0009, &entry); // string/policy_system
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x000a, &entry); // string/policy_system_vendor
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x000b, &entry); // string/policy_signature
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x000c, &entry); // string/str1
- ASSERT_TRUE(success);
- ASSERT_EQ(entry, 0x0000);
-
- success = LoadedIdmap::Lookup(header, 0x000d, &entry); // string/str2
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x000e, &entry); // string/str3
- ASSERT_TRUE(success);
- ASSERT_EQ(entry, 0x0001);
-
- success = LoadedIdmap::Lookup(header, 0x000f, &entry); // string/str4
- ASSERT_TRUE(success);
- ASSERT_EQ(entry, 0x0002);
-
- success = LoadedIdmap::Lookup(header, 0x0010, &entry); // string/x
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0011, &entry); // string/y
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0012, &entry); // string/z
- ASSERT_FALSE(success);
+ ASSERT_EQ(overlay_entries1[2].overlay_id, overlay_entries2[2].overlay_id);
+ ASSERT_EQ(overlay_entries1[2].target_id, overlay_entries2[2].target_id);
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
index f55acee..8af4037 100644
--- a/cmds/idmap2/tests/FileUtilsTests.cpp
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -56,12 +56,12 @@
return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0;
});
ASSERT_THAT(v, NotNull());
- ASSERT_EQ(v->size(), 10U);
+ ASSERT_EQ(v->size(), 11U);
ASSERT_EQ(std::set<std::string>(v->begin(), v->end()),
std::set<std::string>(
{root + "/target/target.apk", root + "/target/target-no-overlayable.apk",
root + "/overlay/overlay.apk", root + "/overlay/overlay-no-name.apk",
- root + "/overlay/overlay-no-name-static.apk",
+ root + "/overlay/overlay-no-name-static.apk", root + "/overlay/overlay-shared.apk",
root + "/overlay/overlay-static-1.apk", root + "/overlay/overlay-static-2.apk",
root + "/signature-overlay/signature-overlay.apk",
root + "/system-overlay/system-overlay.apk",
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index 499eb99..d896cf9 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -34,6 +34,7 @@
#include <string>
#include <vector>
+#include "R.h"
#include "TestHelpers.h"
#include "androidfw/PosixUtils.h"
#include "gmock/gmock.h"
@@ -127,10 +128,14 @@
// clang-format on
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_NE(result->stdout.find("0x7f010000 -> 0x7f010000 integer/int1"), std::string::npos);
- ASSERT_NE(result->stdout.find("0x7f02000c -> 0x7f020000 string/str1"), std::string::npos);
- ASSERT_NE(result->stdout.find("0x7f02000e -> 0x7f020001 string/str3"), std::string::npos);
- ASSERT_NE(result->stdout.find("0x7f02000f -> 0x7f020002 string/str4"), std::string::npos);
+ ASSERT_NE(result->stdout.find(R::target::integer::literal::int1 + " -> 0x7f010000 integer/int1"),
+ std::string::npos);
+ ASSERT_NE(result->stdout.find(R::target::string::literal::str1 + " -> 0x7f020000 string/str1"),
+ std::string::npos);
+ ASSERT_NE(result->stdout.find(R::target::string::literal::str3 + " -> 0x7f020001 string/str3"),
+ std::string::npos);
+ ASSERT_NE(result->stdout.find(R::target::string::literal::str4 + " -> 0x7f020002 string/str4"),
+ std::string::npos);
// clang-format off
result = ExecuteBinary({"idmap2",
@@ -141,7 +146,6 @@
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
ASSERT_NE(result->stdout.find("00000000: 504d4449 magic"), std::string::npos);
- ASSERT_NE(result->stdout.find("00000210: 007f target package id"), std::string::npos);
// clang-format off
result = ExecuteBinary({"idmap2",
@@ -298,7 +302,7 @@
"lookup",
"--idmap-path", GetIdmapPath(),
"--config", "",
- "--resid", "0x7f02000c"}); // string/str1
+ "--resid", R::target::string::literal::str1});
// clang-format on
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 0f47f1e..6fab5e0 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -22,6 +22,8 @@
#include <utility>
#include <vector>
+#include "R.h"
+#include "TestConstants.h"
#include "TestHelpers.h"
#include "android-base/macros.h"
#include "androidfw/ApkAssets.h"
@@ -30,12 +32,25 @@
#include "idmap2/BinaryStreamVisitor.h"
#include "idmap2/CommandLineOptions.h"
#include "idmap2/Idmap.h"
+#include "idmap2/LogInfo.h"
+using android::Res_value;
using ::testing::IsNull;
using ::testing::NotNull;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace android::idmap2 {
+#define ASSERT_TARGET_ENTRY(entry, target_resid, type, value) \
+ ASSERT_EQ(entry.target_id, target_resid); \
+ ASSERT_EQ(entry.data_type, type); \
+ ASSERT_EQ(entry.data_value, value)
+
+#define ASSERT_OVERLAY_ENTRY(entry, overlay_resid, target_resid) \
+ ASSERT_EQ(entry.overlay_id, overlay_resid); \
+ ASSERT_EQ(entry.target_id, target_resid)
+
TEST(IdmapTests, TestCanonicalIdmapPathFor) {
ASSERT_EQ(Idmap::CanonicalIdmapPathFor("/foo", "/vendor/overlay/bar.apk"),
"/foo/vendor@overlay@bar.apk@idmap");
@@ -47,17 +62,20 @@
std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull());
ASSERT_EQ(header->GetMagic(), 0x504d4449U);
- ASSERT_EQ(header->GetVersion(), 0x01U);
+ ASSERT_EQ(header->GetVersion(), 0x04U);
ASSERT_EQ(header->GetTargetCrc(), 0x1234U);
ASSERT_EQ(header->GetOverlayCrc(), 0x5678U);
- ASSERT_EQ(header->GetTargetPath().to_string(), "target.apk");
- ASSERT_EQ(header->GetOverlayPath().to_string(), "overlay.apk");
+ ASSERT_EQ(header->GetFulfilledPolicies(), 0x11);
+ ASSERT_EQ(header->GetEnforceOverlayable(), true);
+ ASSERT_EQ(header->GetTargetPath().to_string(), "targetX.apk");
+ ASSERT_EQ(header->GetOverlayPath().to_string(), "overlayX.apk");
+ ASSERT_EQ(header->GetDebugInfo(), "debug");
}
TEST(IdmapTests, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) {
std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
// overwrite the target path string, including the terminating null, with '.'
- for (size_t i = 0x10; i < 0x110; i++) {
+ for (size_t i = 0x15; i < 0x115; i++) {
raw[i] = '.';
}
std::istringstream stream(raw);
@@ -66,58 +84,40 @@
}
TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
- const size_t offset = 0x210;
+ const size_t offset = 0x221;
std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
idmap_raw_data_len - offset);
std::istringstream stream(raw);
std::unique_ptr<const IdmapData::Header> header = IdmapData::Header::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull());
- ASSERT_EQ(header->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(header->GetTypeCount(), 2U);
-}
-
-TEST(IdmapTests, CreateIdmapDataResourceTypeFromBinaryStream) {
- const size_t offset = 0x214;
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
- idmap_raw_data_len - offset);
- std::istringstream stream(raw);
-
- std::unique_ptr<const IdmapData::TypeEntry> data = IdmapData::TypeEntry::FromBinaryStream(stream);
- ASSERT_THAT(data, NotNull());
- ASSERT_EQ(data->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(data->GetOverlayTypeId(), 0x02U);
- ASSERT_EQ(data->GetEntryCount(), 1U);
- ASSERT_EQ(data->GetEntryOffset(), 0U);
- ASSERT_EQ(data->GetEntry(0), 0U);
+ ASSERT_EQ(header->GetTargetEntryCount(), 0x03);
+ ASSERT_EQ(header->GetOverlayEntryCount(), 0x03);
}
TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
- const size_t offset = 0x210;
+ const size_t offset = 0x221;
std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
idmap_raw_data_len - offset);
std::istringstream stream(raw);
std::unique_ptr<const IdmapData> data = IdmapData::FromBinaryStream(stream);
ASSERT_THAT(data, NotNull());
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U);
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 2U);
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetEntryCount(), 1U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 0U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);
+ const auto& target_entries = data->GetTargetEntries();
+ ASSERT_EQ(target_entries.size(), 3U);
+ ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, 0x01 /* Res_value::TYPE_REFERENCE */,
+ 0x7f020000);
+ ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, 0x01 /* Res_value::TYPE_REFERENCE */,
+ 0x7f030000);
+ ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, 0x01 /* Res_value::TYPE_REFERENCE */,
+ 0x7f030001);
- ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03U);
- ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03U);
- ASSERT_EQ(types[1]->GetEntryCount(), 3U);
- ASSERT_EQ(types[1]->GetEntryOffset(), 3U);
- ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
- ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
- ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
+ const auto& overlay_entries = data->GetOverlayEntries();
+ ASSERT_EQ(target_entries.size(), 3U);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], 0x7f020000, 0x7f020000);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[1], 0x7f030000, 0x7f030000);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[2], 0x7f030001, 0x7f030002);
}
TEST(IdmapTests, CreateIdmapFromBinaryStream) {
@@ -130,34 +130,31 @@
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
- ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x04U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U);
- ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), "target.apk");
- ASSERT_EQ(idmap->GetHeader()->GetOverlayPath().to_string(), "overlay.apk");
+ ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), 0x11);
+ ASSERT_EQ(idmap->GetHeader()->GetEnforceOverlayable(), true);
+ ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), "targetX.apk");
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayPath().to_string(), "overlayX.apk");
const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
ASSERT_EQ(dataBlocks.size(), 1U);
const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U);
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 2U);
+ ASSERT_THAT(data, NotNull());
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetEntryCount(), 1U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 0U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);
+ const auto& target_entries = data->GetTargetEntries();
+ ASSERT_EQ(target_entries.size(), 3U);
+ ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, Res_value::TYPE_REFERENCE, 0x7f020000);
+ ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, Res_value::TYPE_REFERENCE, 0x7f030000);
+ ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, Res_value::TYPE_REFERENCE, 0x7f030001);
- ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03U);
- ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03U);
- ASSERT_EQ(types[1]->GetEntryCount(), 3U);
- ASSERT_EQ(types[1]->GetEntryOffset(), 3U);
- ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
- ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
- ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
+ const auto& overlay_entries = data->GetOverlayEntries();
+ ASSERT_EQ(target_entries.size(), 3U);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], 0x7f020000, 0x7f020000);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[1], 0x7f030000, 0x7f030000);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[2], 0x7f030001, 0x7f030002);
}
TEST(IdmapTests, GracefullyFailToCreateIdmapFromCorruptBinaryStream) {
@@ -169,301 +166,192 @@
ASSERT_FALSE(result);
}
-void CreateIdmap(const StringPiece& target_apk_path, const StringPiece& overlay_apk_path,
- const PolicyBitmask& fulfilled_policies, bool enforce_overlayable,
- std::unique_ptr<const Idmap>* out_idmap) {
- std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path.to_string());
- ASSERT_THAT(target_apk, NotNull());
-
- std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path.to_string());
- 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);
- *out_idmap = result ? std::move(*result) : nullptr;
-}
-
-TEST(IdmapTests, CreateIdmapFromApkAssets) {
- std::unique_ptr<const Idmap> idmap;
+TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) {
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk";
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ true, &idmap);
+
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
+ ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
+ auto& idmap = *idmap_result;
+ ASSERT_THAT(idmap, NotNull());
ASSERT_THAT(idmap->GetHeader(), NotNull());
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()->GetVersion(), 0x04U);
+ ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC);
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC);
+ ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), PolicyFlags::PUBLIC);
+ ASSERT_EQ(idmap->GetHeader()->GetEnforceOverlayable(), true);
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);
-
- const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
- ASSERT_EQ(dataBlocks.size(), 1U);
-
- const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
-
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U);
-
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 2U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 1U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 0U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);
-
- ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U);
- ASSERT_EQ(types[1]->GetEntryCount(), 4U);
- ASSERT_EQ(types[1]->GetEntryOffset(), 12U);
- ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
- ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
- ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
- ASSERT_EQ(types[1]->GetEntry(3), 0x0002U);
}
-// Overlays should abide by all overlayable restrictions if enforcement of overlayable is enabled.
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
- std::unique_ptr<const Idmap> idmap;
+Result<std::unique_ptr<const IdmapData>> TestIdmapDataFromApkAssets(
+ 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());
+ }
+
+ LogInfo log_info;
+ auto mapping = ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, overlay_info,
+ fulfilled_policies, enforce_overlayable, log_info);
+
+ if (!mapping) {
+ return mapping.GetError();
+ }
+
+ return IdmapData::FromResourceMapping(*mapping);
+}
+
+TEST(IdmapTests, CreateIdmapDataFromApkAssets) {
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
- std::string overlay_apk_path = GetTestDataPath() + "/system-overlay/system-overlay.apk";
- CreateIdmap(target_apk_path, overlay_apk_path,
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ true, &idmap);
+ std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk";
+
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
+ ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
+ auto& idmap = *idmap_result;
ASSERT_THAT(idmap, NotNull());
const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
ASSERT_EQ(dataBlocks.size(), 1U);
const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+ ASSERT_THAT(data, NotNull());
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
+ const auto& target_entries = data->GetTargetEntries();
+ ASSERT_EQ(target_entries.size(), 4U);
+ ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1,
+ Res_value::TYPE_DYNAMIC_REFERENCE, R::overlay::integer::int1);
+ ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str1);
+ ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str3);
+ ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str4);
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 1U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 4U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 8U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/policy_public
- ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/policy_signature
- ASSERT_EQ(types[0]->GetEntry(2), 0x0001U); // string/policy_system
- ASSERT_EQ(types[0]->GetEntry(3), 0x0002U); // string/policy_system_vendor
+ const auto& overlay_entries = data->GetOverlayEntries();
+ ASSERT_EQ(target_entries.size(), 4U);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], R::overlay::integer::int1, R::target::integer::int1);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[1], R::overlay::string::str1, R::target::string::str1);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[2], R::overlay::string::str3, R::target::string::str3);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[3], R::overlay::string::str4, R::target::string::str4);
}
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySignature) {
- std::unique_ptr<const Idmap> idmap;
+TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) {
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
- std::string overlay_apk_path = GetTestDataPath() + "/signature-overlay/signature-overlay.apk";
- CreateIdmap(target_apk_path, overlay_apk_path,
- PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_SIGNATURE,
- /* enforce_overlayable */ true, &idmap);
+ std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay-shared.apk";
+
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
+ ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
+ auto& idmap = *idmap_result;
ASSERT_THAT(idmap, NotNull());
const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
ASSERT_EQ(dataBlocks.size(), 1U);
const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+ ASSERT_THAT(data, NotNull());
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
+ const auto& target_entries = data->GetTargetEntries();
+ ASSERT_EQ(target_entries.size(), 4U);
+ ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1,
+ Res_value::TYPE_DYNAMIC_REFERENCE, R::overlay_shared::integer::int1);
+ ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay_shared::string::str1);
+ ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay_shared::string::str3);
+ ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay_shared::string::str4);
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 1U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 1U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 9U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/policy_signature
+ const auto& overlay_entries = data->GetOverlayEntries();
+ ASSERT_EQ(target_entries.size(), 4U);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], R::overlay_shared::integer::int1,
+ R::target::integer::int1);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[1], R::overlay_shared::string::str1,
+ R::target::string::str1);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[2], R::overlay_shared::string::str3,
+ R::target::string::str3);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[3], R::overlay_shared::string::str4,
+ R::target::string::str4);
}
-// Overlays should abide by all overlayable restrictions if enforcement of overlayable is enabled.
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
- std::unique_ptr<const Idmap> idmap;
- std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
- std::string overlay_apk_path =
- GetTestDataPath() + "/system-overlay-invalid/system-overlay-invalid.apk";
- CreateIdmap(target_apk_path, overlay_apk_path,
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
+TEST(IdmapTests, CreateIdmapDataDoNotRewriteNonOverlayResourceId) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0x7f030001; // xml/overlays_different_packages
+ auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ false);
- const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
- ASSERT_EQ(dataBlocks.size(), 1U);
+ ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage();
+ auto& data = *idmap_data;
- const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+ const auto& target_entries = data->GetTargetEntries();
+ ASSERT_EQ(target_entries.size(), 2U);
+ ASSERT_TARGET_ENTRY(target_entries[0], R::target::string::str1, Res_value::TYPE_REFERENCE,
+ 0x0104000a); // -> android:string/ok
+ ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str3);
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
-
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 1U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 4U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 8U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0005U); // string/policy_public
- ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/policy_signature
- ASSERT_EQ(types[0]->GetEntry(2), 0x0007U); // string/policy_system
- ASSERT_EQ(types[0]->GetEntry(3), 0x0008U); // string/policy_system_vendor
+ const auto& overlay_entries = data->GetOverlayEntries();
+ ASSERT_EQ(overlay_entries.size(), 1U);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], R::overlay::string::str3, R::target::string::str3);
}
-// Overlays should ignore all overlayable restrictions if enforcement of overlayable is disabled.
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
- std::unique_ptr<const Idmap> idmap;
- std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
- std::string overlay_apk_path =
- GetTestDataPath() + "/system-overlay-invalid/system-overlay-invalid.apk";
- CreateIdmap(target_apk_path, overlay_apk_path,
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ false, &idmap);
- ASSERT_THAT(idmap, NotNull());
+TEST(IdmapTests, CreateIdmapDataInlineResources) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0x7f030002; // xml/overlays_inline
+ auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ false);
- const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
- ASSERT_EQ(dataBlocks.size(), 1U);
+ ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage();
+ auto& data = *idmap_data;
- const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+ constexpr size_t overlay_string_pool_size = 8U;
+ const auto& target_entries = data->GetTargetEntries();
+ ASSERT_EQ(target_entries.size(), 2U);
+ ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1, Res_value::TYPE_INT_DEC,
+ 73U); // -> 73
+ ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_STRING,
+ overlay_string_pool_size + 0U); // -> "Hello World"
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
-
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 1U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 9U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 3U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/not_overlayable
- ASSERT_EQ(types[0]->GetEntry(1), 0x0001U); // string/policy_odm
- ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_oem
- ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/other
- ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_product
- ASSERT_EQ(types[0]->GetEntry(5), 0x0005U); // string/policy_public
- ASSERT_EQ(types[0]->GetEntry(6), 0x0006U); // string/policy_signature
- ASSERT_EQ(types[0]->GetEntry(7), 0x0007U); // string/policy_system
- ASSERT_EQ(types[0]->GetEntry(8), 0x0008U); // string/policy_system_vendor
-}
-
-// Overlays that do not specify a target <overlayable> can overlay resources defined as overlayable.
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsNoDefinedOverlayableAndNoTargetName) {
- std::unique_ptr<const Idmap> idmap;
- std::string target_apk_path = GetTestDataPath() + "/target/target-no-overlayable.apk";
- std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay-no-name.apk";
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ false, &idmap);
- ASSERT_THAT(idmap, NotNull());
-
- const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
- ASSERT_EQ(dataBlocks.size(), 1U);
-
- const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
-
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U);
-
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 2U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 1U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 0U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/int1
-
- ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U);
- ASSERT_EQ(types[1]->GetEntryCount(), 4U);
- ASSERT_EQ(types[1]->GetEntryOffset(), 12U);
- ASSERT_EQ(types[1]->GetEntry(0), 0x0000U); // string/str1
- ASSERT_EQ(types[1]->GetEntry(1), kNoEntry); // string/str2
- ASSERT_EQ(types[1]->GetEntry(2), 0x0001U); // string/str3
- ASSERT_EQ(types[1]->GetEntry(3), 0x0002U); // string/str4
-}
-
-// Overlays that are not pre-installed and are not signed with the same signature as the target
-// cannot overlay packages that have not defined overlayable resources.
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsDefaultPoliciesPublicFail) {
- std::unique_ptr<const Idmap> idmap;
- std::string target_apk_path = GetTestDataPath() + "/target/target-no-overlayable.apk";
- std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay-no-name.apk";
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, IsNull());
-}
-
-// 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(IdmapOverlayableTests, CreateIdmapFromApkAssetsDefaultPolicies) {
- std::unique_ptr<const Idmap> idmap;
- std::string target_apk_path = GetTestDataPath() + "/target/target-no-overlayable.apk";
- std::string overlay_apk_path =
- GetTestDataPath() + "/system-overlay-invalid/system-overlay-invalid.apk";
-
- auto CheckEntries = [&]() -> void {
- const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
- ASSERT_EQ(dataBlocks.size(), 1U);
-
- const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
-
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 1U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 9U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 3U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/not_overlayable
- ASSERT_EQ(types[0]->GetEntry(1), 0x0001U); // string/policy_odm
- ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_oem
- ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/other
- ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_product
- ASSERT_EQ(types[0]->GetEntry(5), 0x0005U); // string/policy_public
- ASSERT_EQ(types[0]->GetEntry(6), 0x0006U); // string/policy_signature
- ASSERT_EQ(types[0]->GetEntry(7), 0x0007U); // string/policy_system
- ASSERT_EQ(types[0]->GetEntry(8), 0x0008U); // string/policy_system_vendor
- };
-
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_SIGNATURE,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
- CheckEntries();
-
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_PRODUCT_PARTITION,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
- CheckEntries();
-
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_SYSTEM_PARTITION,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
- CheckEntries();
-
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_VENDOR_PARTITION,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
- CheckEntries();
-
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_ODM_PARTITION,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
- CheckEntries();
-
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_OEM_PARTITION,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
- CheckEntries();
+ const auto& overlay_entries = data->GetOverlayEntries();
+ ASSERT_EQ(overlay_entries.size(), 0U);
}
TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) {
@@ -480,9 +368,8 @@
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::PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_FALSE(result);
}
@@ -497,8 +384,7 @@
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::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(result);
const auto idmap = std::move(*result);
@@ -509,7 +395,8 @@
std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull());
- ASSERT_TRUE(header->IsUpToDate());
+ ASSERT_TRUE(header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
// magic: bytes (0x0, 0x03)
std::string bad_magic_string(stream.str());
@@ -522,7 +409,8 @@
IdmapHeader::FromBinaryStream(bad_magic_stream);
ASSERT_THAT(bad_magic_header, NotNull());
ASSERT_NE(header->GetMagic(), bad_magic_header->GetMagic());
- ASSERT_FALSE(bad_magic_header->IsUpToDate());
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
// version: bytes (0x4, 0x07)
std::string bad_version_string(stream.str());
@@ -535,7 +423,8 @@
IdmapHeader::FromBinaryStream(bad_version_stream);
ASSERT_THAT(bad_version_header, NotNull());
ASSERT_NE(header->GetVersion(), bad_version_header->GetVersion());
- ASSERT_FALSE(bad_version_header->IsUpToDate());
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
// target crc: bytes (0x8, 0xb)
std::string bad_target_crc_string(stream.str());
@@ -548,7 +437,8 @@
IdmapHeader::FromBinaryStream(bad_target_crc_stream);
ASSERT_THAT(bad_target_crc_header, NotNull());
ASSERT_NE(header->GetTargetCrc(), bad_target_crc_header->GetTargetCrc());
- ASSERT_FALSE(bad_target_crc_header->IsUpToDate());
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
// overlay crc: bytes (0xc, 0xf)
std::string bad_overlay_crc_string(stream.str());
@@ -561,27 +451,55 @@
IdmapHeader::FromBinaryStream(bad_overlay_crc_stream);
ASSERT_THAT(bad_overlay_crc_header, NotNull());
ASSERT_NE(header->GetOverlayCrc(), bad_overlay_crc_header->GetOverlayCrc());
- ASSERT_FALSE(bad_overlay_crc_header->IsUpToDate());
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
- // target path: bytes (0x10, 0x10f)
+ // fulfilled policy: bytes (0x10, 0x13)
+ std::string bad_policy_string(stream.str());
+ bad_policy_string[0x10] = '.';
+ bad_policy_string[0x11] = '.';
+ bad_policy_string[0x12] = '.';
+ bad_policy_string[0x13] = '.';
+ std::stringstream bad_policy_stream(bad_policy_string);
+ std::unique_ptr<const IdmapHeader> bad_policy_header =
+ IdmapHeader::FromBinaryStream(bad_policy_stream);
+ ASSERT_THAT(bad_policy_header, NotNull());
+ ASSERT_NE(header->GetFulfilledPolicies(), bad_policy_header->GetFulfilledPolicies());
+ ASSERT_FALSE(bad_policy_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
+
+ // enforce overlayable: bytes (0x14)
+ std::string bad_enforce_string(stream.str());
+ bad_enforce_string[0x14] = '\0';
+ std::stringstream bad_enforce_stream(bad_enforce_string);
+ std::unique_ptr<const IdmapHeader> bad_enforce_header =
+ IdmapHeader::FromBinaryStream(bad_enforce_stream);
+ ASSERT_THAT(bad_enforce_header, NotNull());
+ ASSERT_NE(header->GetEnforceOverlayable(), bad_enforce_header->GetEnforceOverlayable());
+ ASSERT_FALSE(bad_enforce_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
+
+ // target path: bytes (0x15, 0x114)
std::string bad_target_path_string(stream.str());
- bad_target_path_string[0x10] = '\0';
+ bad_target_path_string[0x15] = '\0';
std::stringstream bad_target_path_stream(bad_target_path_string);
std::unique_ptr<const IdmapHeader> bad_target_path_header =
IdmapHeader::FromBinaryStream(bad_target_path_stream);
ASSERT_THAT(bad_target_path_header, NotNull());
ASSERT_NE(header->GetTargetPath(), bad_target_path_header->GetTargetPath());
- ASSERT_FALSE(bad_target_path_header->IsUpToDate());
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
- // overlay path: bytes (0x110, 0x20f)
+ // overlay path: bytes (0x115, 0x214)
std::string bad_overlay_path_string(stream.str());
- bad_overlay_path_string[0x110] = '\0';
+ bad_overlay_path_string[0x115] = '\0';
std::stringstream bad_overlay_path_stream(bad_overlay_path_string);
std::unique_ptr<const IdmapHeader> bad_overlay_path_header =
IdmapHeader::FromBinaryStream(bad_overlay_path_stream);
ASSERT_THAT(bad_overlay_path_header, NotNull());
ASSERT_NE(header->GetOverlayPath(), bad_overlay_path_header->GetOverlayPath());
- ASSERT_FALSE(bad_overlay_path_header->IsUpToDate());
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
}
class TestVisitor : public Visitor {
@@ -605,10 +523,6 @@
stream_ << "TestVisitor::visit(IdmapData::Header)" << std::endl;
}
- void visit(const IdmapData::TypeEntry& idmap ATTRIBUTE_UNUSED) override {
- stream_ << "TestVisitor::visit(IdmapData::TypeEntry)" << std::endl;
- }
-
private:
std::ostream& stream_;
};
@@ -625,12 +539,10 @@
(*idmap)->accept(&visitor);
ASSERT_EQ(test_stream.str(),
- "TestVisitor::visit(Idmap)\n"
"TestVisitor::visit(IdmapHeader)\n"
- "TestVisitor::visit(IdmapData)\n"
+ "TestVisitor::visit(Idmap)\n"
"TestVisitor::visit(IdmapData::Header)\n"
- "TestVisitor::visit(IdmapData::TypeEntry)\n"
- "TestVisitor::visit(IdmapData::TypeEntry)\n");
+ "TestVisitor::visit(IdmapData)\n");
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/PoliciesTests.cpp b/cmds/idmap2/tests/PoliciesTests.cpp
index eca7404..1b27759 100644
--- a/cmds/idmap2/tests/PoliciesTests.cpp
+++ b/cmds/idmap2/tests/PoliciesTests.cpp
@@ -17,76 +17,96 @@
#include <string>
#include "TestHelpers.h"
+#include "androidfw/ResourceTypes.h"
#include "gtest/gtest.h"
-#include "idmap2/Policies.h"
+#include "idmap2/PolicyUtils.h"
-using android::idmap2::PolicyBitmask;
-using android::idmap2::PolicyFlags;
+using android::idmap2::utils::BitmaskToPolicies;
+using android::idmap2::utils::PoliciesToBitmaskResult;
+
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
namespace android::idmap2 {
-TEST(PoliciesTests, PoliciesToBitmasks) {
- const auto bitmask1 = PoliciesToBitmask({"system"});
+TEST(PoliciesTests, PoliciesToBitmaskResults) {
+ const auto bitmask1 = PoliciesToBitmaskResult({"system"});
ASSERT_TRUE(bitmask1);
- ASSERT_EQ(*bitmask1, PolicyFlags::POLICY_SYSTEM_PARTITION);
+ ASSERT_EQ(*bitmask1, PolicyFlags::SYSTEM_PARTITION);
- const auto bitmask2 = PoliciesToBitmask({"system", "vendor"});
+ const auto bitmask2 = PoliciesToBitmaskResult({"system", "vendor"});
ASSERT_TRUE(bitmask2);
- ASSERT_EQ(*bitmask2, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION);
+ ASSERT_EQ(*bitmask2, PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
- const auto bitmask3 = PoliciesToBitmask({"vendor", "system"});
+ const auto bitmask3 = PoliciesToBitmaskResult({"vendor", "system"});
ASSERT_TRUE(bitmask3);
- ASSERT_EQ(*bitmask3, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION);
+ ASSERT_EQ(*bitmask3, PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
- const auto bitmask4 = PoliciesToBitmask({"odm", "oem", "public", "product", "system", "vendor"});
+ const auto bitmask4 =
+ PoliciesToBitmaskResult({"odm", "oem", "public", "product", "system", "vendor"});
ASSERT_TRUE(bitmask4);
- ASSERT_EQ(*bitmask4, PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
- PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_PRODUCT_PARTITION |
- PolicyFlags::POLICY_SYSTEM_PARTITION |
- PolicyFlags::POLICY_VENDOR_PARTITION);
+ ASSERT_EQ(*bitmask4, PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION |
+ PolicyFlags::PUBLIC | PolicyFlags::PRODUCT_PARTITION |
+ PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
- const auto bitmask5 = PoliciesToBitmask({"system", "system", "system"});
+ const auto bitmask5 = PoliciesToBitmaskResult({"system", "system", "system"});
ASSERT_TRUE(bitmask5);
- ASSERT_EQ(*bitmask5, PolicyFlags::POLICY_SYSTEM_PARTITION);
+ ASSERT_EQ(*bitmask5, PolicyFlags::SYSTEM_PARTITION);
- const auto bitmask6 = PoliciesToBitmask({""});
+ const auto bitmask6 = PoliciesToBitmaskResult({""});
ASSERT_FALSE(bitmask6);
- const auto bitmask7 = PoliciesToBitmask({"foo"});
+ const auto bitmask7 = PoliciesToBitmaskResult({"foo"});
ASSERT_FALSE(bitmask7);
- const auto bitmask8 = PoliciesToBitmask({"system", "foo"});
+ const auto bitmask8 = PoliciesToBitmaskResult({"system", "foo"});
ASSERT_FALSE(bitmask8);
- const auto bitmask9 = PoliciesToBitmask({"system", ""});
+ const auto bitmask9 = PoliciesToBitmaskResult({"system", ""});
ASSERT_FALSE(bitmask9);
- const auto bitmask10 = PoliciesToBitmask({"system "});
+ const auto bitmask10 = PoliciesToBitmaskResult({"system "});
ASSERT_FALSE(bitmask10);
+
+ const auto bitmask11 = PoliciesToBitmaskResult({"signature"});
+ ASSERT_TRUE(bitmask11);
+ ASSERT_EQ(*bitmask11, PolicyFlags::SIGNATURE);
+
+ const auto bitmask12 = PoliciesToBitmaskResult({"actor"});
+ ASSERT_TRUE(bitmask12);
+ ASSERT_EQ(*bitmask12, PolicyFlags::ACTOR_SIGNATURE);
}
TEST(PoliciesTests, BitmaskToPolicies) {
- const auto policies1 = BitmaskToPolicies(PolicyFlags::POLICY_PUBLIC);
+ const auto policies1 = BitmaskToPolicies(PolicyFlags::PUBLIC);
ASSERT_EQ(1, policies1.size());
ASSERT_EQ(policies1[0], "public");
- const auto policies2 = BitmaskToPolicies(PolicyFlags::POLICY_SYSTEM_PARTITION |
- PolicyFlags::POLICY_VENDOR_PARTITION);
+ const auto policies2 =
+ BitmaskToPolicies(PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
ASSERT_EQ(2, policies2.size());
ASSERT_EQ(policies2[0], "system");
ASSERT_EQ(policies2[1], "vendor");
- const auto policies3 = BitmaskToPolicies(
- PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
- PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_PRODUCT_PARTITION |
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION);
+ const auto policies3 =
+ BitmaskToPolicies(PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION |
+ PolicyFlags::PUBLIC | PolicyFlags::PRODUCT_PARTITION |
+ PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
ASSERT_EQ(2, policies2.size());
ASSERT_EQ(policies3[0], "odm");
ASSERT_EQ(policies3[1], "oem");
- ASSERT_EQ(policies3[2], "public");
- ASSERT_EQ(policies3[3], "product");
+ ASSERT_EQ(policies3[2], "product");
+ ASSERT_EQ(policies3[3], "public");
ASSERT_EQ(policies3[4], "system");
ASSERT_EQ(policies3[5], "vendor");
+
+ const auto policies4 = BitmaskToPolicies(PolicyFlags::SIGNATURE);
+ ASSERT_EQ(1, policies4.size());
+ ASSERT_EQ(policies4[0], "signature");
+
+ const auto policies5 = BitmaskToPolicies(PolicyFlags::ACTOR_SIGNATURE);
+ ASSERT_EQ(1, policies5.size());
+ ASSERT_EQ(policies5[0], "actor");
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
index c412504..9a10079 100644
--- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
@@ -18,19 +18,22 @@
#include <sstream>
#include <string>
+#include "R.h"
#include "TestHelpers.h"
#include "androidfw/ApkAssets.h"
#include "androidfw/Idmap.h"
+#include "androidfw/ResourceTypes.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "idmap2/Idmap.h"
-#include "idmap2/Policies.h"
#include "idmap2/PrettyPrintVisitor.h"
using ::testing::NotNull;
using android::ApkAssets;
-using android::idmap2::PolicyBitmask;
+
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
namespace android::idmap2 {
@@ -43,9 +46,8 @@
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::PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
std::stringstream stream;
@@ -54,7 +56,8 @@
ASSERT_NE(stream.str().find("target apk path : "), std::string::npos);
ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
- ASSERT_NE(stream.str().find("0x7f010000 -> 0x7f010000 integer/int1\n"), std::string::npos);
+ ASSERT_NE(stream.str().find(R::target::integer::literal::int1 + " -> 0x7f010000 integer/int1\n"),
+ std::string::npos);
}
TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) {
diff --git a/cmds/idmap2/tests/R.h b/cmds/idmap2/tests/R.h
new file mode 100644
index 0000000..aed263a
--- /dev/null
+++ b/cmds/idmap2/tests/R.h
@@ -0,0 +1,107 @@
+/*
+ * 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_TESTS_R_H
+#define IDMAP2_TESTS_R_H
+
+#include <idmap2/ResourceUtils.h>
+
+namespace android::idmap2 {
+
+static std::string hexify(ResourceId id) {
+ std::stringstream stream;
+ stream << std::hex << static_cast<uint32_t>(id);
+ return stream.str();
+}
+
+// clang-format off
+namespace R::target {
+ namespace integer {
+ constexpr ResourceId int1 = 0x7f010000;
+
+ namespace literal {
+ inline const std::string int1 = hexify(R::target::integer::int1);
+ }
+ }
+
+ namespace string {
+ constexpr ResourceId not_overlayable = 0x7f020003;
+ constexpr ResourceId other = 0x7f020004;
+ constexpr ResourceId policy_actor = 0x7f020005;
+ constexpr ResourceId policy_odm = 0x7f020006;
+ constexpr ResourceId policy_oem = 0x7f020007;
+ constexpr ResourceId policy_product = 0x7f020008;
+ constexpr ResourceId policy_public = 0x7f020009;
+ constexpr ResourceId policy_signature = 0x7f02000a;
+ constexpr ResourceId policy_system = 0x7f02000b;
+ constexpr ResourceId policy_system_vendor = 0x7f02000c;
+ constexpr ResourceId str1 = 0x7f02000d;
+ constexpr ResourceId str3 = 0x7f02000f;
+ constexpr ResourceId str4 = 0x7f020010;
+
+ namespace literal {
+ inline const std::string str1 = hexify(R::target::string::str1);
+ inline const std::string str3 = hexify(R::target::string::str3);
+ inline const std::string str4 = hexify(R::target::string::str4);
+ }
+ }
+}
+
+namespace R::overlay {
+ namespace integer {
+ constexpr ResourceId int1 = 0x7f010000;
+ }
+ namespace string {
+ constexpr ResourceId str1 = 0x7f020000;
+ constexpr ResourceId str3 = 0x7f020001;
+ constexpr ResourceId str4 = 0x7f020002;
+ }
+}
+
+namespace R::overlay_shared {
+ namespace integer {
+ constexpr ResourceId int1 = 0x00010000;
+ }
+ namespace string {
+ constexpr ResourceId str1 = 0x00020000;
+ constexpr ResourceId str3 = 0x00020001;
+ constexpr ResourceId str4 = 0x00020002;
+ }
+}
+
+namespace R::system_overlay::string {
+ constexpr ResourceId policy_public = 0x7f010000;
+ constexpr ResourceId policy_system = 0x7f010001;
+ constexpr ResourceId policy_system_vendor = 0x7f010002;
+}
+
+namespace R::system_overlay_invalid::string {
+ constexpr ResourceId not_overlayable = 0x7f010000;
+ constexpr ResourceId other = 0x7f010001;
+ constexpr ResourceId policy_actor = 0x7f010002;
+ constexpr ResourceId policy_odm = 0x7f010003;
+ constexpr ResourceId policy_oem = 0x7f010004;
+ constexpr ResourceId policy_product = 0x7f010005;
+ constexpr ResourceId policy_public = 0x7f010006;
+ constexpr ResourceId policy_signature = 0x7f010007;
+ constexpr ResourceId policy_system = 0x7f010008;
+ constexpr ResourceId policy_system_vendor = 0x7f010009;
+};
+// clang-format on
+
+} // namespace android::idmap2
+
+#endif // IDMAP2_TESTS_R_H
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index 2695176..b268d5a 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -16,20 +16,38 @@
#include <cstdio> // fclose
#include <memory>
+#include <regex>
#include <sstream>
#include <string>
+#include "TestConstants.h"
#include "TestHelpers.h"
+#include "android-base/stringprintf.h"
+#include "androidfw/ResourceTypes.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "idmap2/Idmap.h"
#include "idmap2/RawPrintVisitor.h"
+using android::base::StringPrintf;
using ::testing::NotNull;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace android::idmap2 {
+#define ASSERT_CONTAINS_REGEX(pattern, str) \
+ do { \
+ ASSERT_TRUE(std::regex_search(str, std::regex(pattern))) \
+ << "pattern '" << pattern << "' not found in\n--------\n" \
+ << str << "--------"; \
+ } while (0)
+
+#define ADDRESS "[0-9a-f]{8}: "
+
TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
+ fclose(stderr); // silence expected warnings
+
const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
ASSERT_THAT(target_apk, NotNull());
@@ -38,21 +56,36 @@
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::PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
std::stringstream stream;
RawPrintVisitor visitor(stream);
(*idmap)->accept(&visitor);
- 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("0000021c: 00000000 0x7f010000 -> 0x7f010000 integer/int1\n"),
- std::string::npos);
+ ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000004 version\n", stream.str());
+ ASSERT_CONTAINS_REGEX(
+ StringPrintf(ADDRESS "%s target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING),
+ stream.str());
+ ASSERT_CONTAINS_REGEX(
+ StringPrintf(ADDRESS "%s overlay crc\n", android::idmap2::TestConstants::OVERLAY_CRC_STRING),
+ stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000001 fulfilled policies: public\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS " 01 enforce overlayable\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000004 target entry count\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000008 string pool index offset\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "000000b4 string pool byte length\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS " 07 type: reference \\(dynamic\\)\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 value: integer/int1\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 overlay id: integer/int1\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1\n", stream.str());
}
TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
@@ -68,11 +101,23 @@
RawPrintVisitor visitor(stream);
(*idmap)->accept(&visitor);
- 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: 00001234 target crc\n"), std::string::npos);
- ASSERT_NE(stream.str().find("0000000c: 00005678 overlay crc\n"), std::string::npos);
- ASSERT_NE(stream.str().find("0000021c: 00000000 0x7f020000 -> 0x7f020000\n"), std::string::npos);
+ ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000004 version\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00001234 target crc\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00005678 overlay crc\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000011 fulfilled policies: public|signature\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS " 01 enforce overlayable\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000003 target entry count\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000003 overlay entry count\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000000 string pool index offset\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000000 string pool byte length\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS " 01 type: reference\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 value\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 overlay id\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id\n", stream.str());
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
new file mode 100644
index 0000000..de039f4
--- /dev/null
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -0,0 +1,352 @@
+/*
+ * 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 "R.h"
+#include "TestHelpers.h"
+#include "androidfw/ResourceTypes.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "idmap2/LogInfo.h"
+#include "idmap2/ResourceMapping.h"
+
+using android::Res_value;
+using android::idmap2::utils::ExtractOverlayManifestInfo;
+
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
+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());
+ }
+
+ LogInfo log_info;
+ return ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, overlay_info, fulfilled_policies,
+ enforce_overlayable, log_info);
+}
+
+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::PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
+ ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_REFERENCE,
+ R::overlay::integer::int1, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str1, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str3, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str4, false /* rewrite */));
+}
+
+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::PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+ ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str4, true /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str1, true /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str3, true /* rewrite */));
+}
+
+TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) {
+ 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::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, R::target::string::str1, Res_value::TYPE_REFERENCE, 0x0104000a,
+ false /* rewrite */)); // -> android:string/ok
+ ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ 0x7f020001, true /* rewrite */));
+}
+
+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::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, R::target::string::str1, Res_value::TYPE_STRING,
+ overlay_string_pool_size + 0U,
+ false /* rewrite */)); // -> "Hello World"
+ ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 73U,
+ false /* rewrite */)); // -> 73
+}
+
+TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
+ auto resources =
+ TestGetResourceMapping("/target/target.apk", "/system-overlay/system-overlay.apk",
+ PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+ R::system_overlay::string::policy_public, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+ R::system_overlay::string::policy_system, false /* rewrite */));
+ ASSERT_RESULT(
+ MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
+ R::system_overlay::string::policy_system_vendor, false /* rewrite */));
+}
+
+// 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::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_public,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system,
+ false /* rewrite */));
+ ASSERT_RESULT(
+ MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
+}
+
+// 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::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 10U);
+ ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::not_overlayable,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::other, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::other, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_actor,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_odm, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_oem, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_product,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_public,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_signature,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system,
+ false /* rewrite */));
+ ASSERT_RESULT(
+ MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
+}
+
+// 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::PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
+ ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_REFERENCE,
+ R::overlay::integer::int1, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str1, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str3, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str4, false /* rewrite */));
+}
+
+// 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::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(), 10U);
+ ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::not_overlayable,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::other, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::other, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_actor,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_odm,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_oem,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_product,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_public,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_signature,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(
+ res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
+ };
+
+ CheckEntries(PolicyFlags::SIGNATURE);
+ CheckEntries(PolicyFlags::PRODUCT_PARTITION);
+ CheckEntries(PolicyFlags::SYSTEM_PARTITION);
+ CheckEntries(PolicyFlags::VENDOR_PARTITION);
+ CheckEntries(PolicyFlags::ODM_PARTITION);
+ CheckEntries(PolicyFlags::OEM_PARTITION);
+}
+
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/TestConstants.h b/cmds/idmap2/tests/TestConstants.h
new file mode 100644
index 0000000..6bc924e
--- /dev/null
+++ b/cmds/idmap2/tests/TestConstants.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 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_TESTS_TESTCONSTANTS_H
+#define IDMAP2_TESTS_TESTCONSTANTS_H
+
+namespace android::idmap2::TestConstants {
+
+constexpr const auto TARGET_CRC = 0x41c60c8c;
+constexpr const auto TARGET_CRC_STRING = "41c60c8c";
+
+constexpr const auto OVERLAY_CRC = 0xc054fb26;
+constexpr const auto OVERLAY_CRC_STRING = "c054fb26";
+
+} // namespace android::idmap2::TestConstants
+
+#endif // IDMAP2_TESTS_TESTCONSTANTS_H
diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h
index adea329..b599dcb 100644
--- a/cmds/idmap2/tests/TestHelpers.h
+++ b/cmds/idmap2/tests/TestHelpers.h
@@ -30,7 +30,7 @@
0x49, 0x44, 0x4d, 0x50,
// 0x4: version
- 0x01, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00,
// 0x8: target crc
0x34, 0x12, 0x00, 0x00,
@@ -38,8 +38,14 @@
// 0xc: overlay crc
0x78, 0x56, 0x00, 0x00,
- // 0x10: target path "target.apk"
- 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ // 0x10: fulfilled policies
+ 0x11, 0x00, 0x00, 0x00,
+
+ // 0x14: enforce overlayable
+ 0x01,
+
+ // 0x15: target path "targetX.apk"
+ 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -56,8 +62,8 @@
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- // 0x110: overlay path "overlay.apk"
- 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00,
+ // 0x115: overlay path "overlayX.apk"
+ 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -74,56 +80,77 @@
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ // 0x215: debug string
+ // string length, including terminating null
+ 0x08, 0x00, 0x00, 0x00,
+
+ // string contents "debug\0\0\0" (padded to word alignment)
+ 0x64, 0x65, 0x62, 0x75, 0x67, 0x00, 0x00, 0x00,
+
// DATA HEADER
- // 0x210: target package id
- 0x7f, 0x00,
+ // 0x221: target_package_id
+ 0x7f,
- // 0x212: types count
- 0x02, 0x00,
+ // 0x222: overlay_package_id
+ 0x7f,
- // DATA BLOCK
- // 0x214: target type
- 0x02, 0x00,
+ // 0x223: target_entry_count
+ 0x03, 0x00, 0x00, 0x00,
- // 0x216: overlay type
- 0x02, 0x00,
+ // 0x227: overlay_entry_count
+ 0x03, 0x00, 0x00, 0x00,
- // 0x218: entry count
- 0x01, 0x00,
-
- // 0x21a: entry offset
- 0x00, 0x00,
-
- // 0x21c: entries
+ // 0x22b: string_pool_offset
0x00, 0x00, 0x00, 0x00,
- // DATA BLOCK
- // 0x220: target type
- 0x03, 0x00,
-
- // 0x222: overlay type
- 0x03, 0x00,
-
- // 0x224: entry count
- 0x03, 0x00,
-
- // 0x226: entry offset
- 0x03, 0x00,
-
- // 0x228, 0x22c, 0x230: entries
+ // 0x22f: string_pool_byte_length
0x00, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xff, 0xff,
+ // TARGET ENTRIES
+ // 0x233: 0x7f020000
+ 0x00, 0x00, 0x02, 0x7f,
- 0x01, 0x00, 0x00, 0x00};
+ // 0x237: TYPE_REFERENCE
+ 0x01,
-const unsigned int idmap_raw_data_len = 565;
+ // 0x238: 0x7f020000
+ 0x00, 0x00, 0x02, 0x7f,
+
+ // 0x23c: 0x7f030000
+ 0x00, 0x00, 0x03, 0x7f,
+
+ // 0x240: TYPE_REFERENCE
+ 0x01,
+
+ // 0x241: 0x7f030000
+ 0x00, 0x00, 0x03, 0x7f,
+
+ // 0x245: 0x7f030002
+ 0x02, 0x00, 0x03, 0x7f,
+
+ // 0x249: TYPE_REFERENCE
+ 0x01,
+
+ // 0x24a: 0x7f030001
+ 0x01, 0x00, 0x03, 0x7f,
+
+ // OVERLAY ENTRIES
+ // 0x24e: 0x7f020000 -> 0x7f020000
+ 0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f,
+
+ // 0x256: 0x7f030000 -> 0x7f030000
+ 0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f,
+
+ // 0x25e: 0x7f030001 -> 0x7f030002
+ 0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f};
+
+const unsigned int idmap_raw_data_len = 0x266;
std::string GetTestDataPath();
class Idmap2Tests : public testing::Test {
protected:
- virtual void SetUp() {
+ void SetUp() override {
#ifdef __ANDROID__
tmp_dir_path_ = "/data/local/tmp/idmap2-tests-XXXXXX";
#else
@@ -136,7 +163,7 @@
idmap_path_ = tmp_dir_path_ + "/a.idmap";
}
- virtual void TearDown() {
+ void TearDown() override {
EXPECT_EQ(rmdir(tmp_dir_path_.c_str()), 0)
<< "Failed to remove temporary directory " << tmp_dir_path_ << ": " << strerror(errno);
}
diff --git a/cmds/idmap2/tests/XmlParserTests.cpp b/cmds/idmap2/tests/XmlParserTests.cpp
new file mode 100644
index 0000000..1a7eaca
--- /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 df63211..0000000
--- 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 619bb6c..cf3691c 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 68b9f50..114b099 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
@@ -51,4 +51,12 @@
-o overlay-static-2.apk \
compiled.flata
+aapt2 link \
+ --no-resource-removal \
+ --shared-lib \
+ -I "$FRAMEWORK_RES_APK" \
+ --manifest AndroidManifest.xml \
+ -o overlay-shared.apk \
+ compiled.flata
+
rm 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 18ee43d..7c25985 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 6425190..c75f3e1 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-shared.apk b/cmds/idmap2/tests/data/overlay/overlay-shared.apk
new file mode 100644
index 0000000..93dcc82
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/overlay-shared.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 642ab90..5b8a6e4 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 2ec5602..698a1fd 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 5842da4..1db303f 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 0000000..edd33f7
--- /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 0000000..aa7fefa
--- /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 0000000..e12b823
--- /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 0000000..5728e67
--- /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/system-overlay-invalid/res/values/values.xml b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
index 9ebfae4..7119d82 100644
--- a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
+++ b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
@@ -25,6 +25,7 @@
<string name="policy_signature">policy_signature</string>
<string name="policy_odm">policy_odm</string>
<string name="policy_oem">policy_oem</string>
+ <string name="policy_actor">policy_actor</string>
<!-- Requests to overlay a resource that is not declared as overlayable. -->
<string name="not_overlayable">not_overlayable</string>
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
index 1456e74..bd99098 100644
--- a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
+++ b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/target/res/values/overlayable.xml b/cmds/idmap2/tests/data/target/res/values/overlayable.xml
index 8389f56..ad4cd48 100644
--- a/cmds/idmap2/tests/data/target/res/values/overlayable.xml
+++ b/cmds/idmap2/tests/data/target/res/values/overlayable.xml
@@ -41,6 +41,10 @@
<item type="string" name="policy_oem" />
</policy>
+ <policy type="actor">
+ <item type="string" name="policy_actor" />
+ </policy>
+
<!-- Resources publicly overlayable -->
<policy type="public">
<item type="string" name="policy_public" />
@@ -63,4 +67,4 @@
<item type="string" name="other" />
</policy>
</overlayable>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/cmds/idmap2/tests/data/target/res/values/values.xml b/cmds/idmap2/tests/data/target/res/values/values.xml
index a892c98..5230e25 100644
--- a/cmds/idmap2/tests/data/target/res/values/values.xml
+++ b/cmds/idmap2/tests/data/target/res/values/values.xml
@@ -36,6 +36,7 @@
<string name="policy_signature">policy_signature</string>
<string name="policy_system">policy_system</string>
<string name="policy_system_vendor">policy_system_vendor</string>
+ <string name="policy_actor">policy_actor</string>
<string name="other">other</string>
</resources>
diff --git a/cmds/idmap2/tests/data/target/res/xml/test.xml b/cmds/idmap2/tests/data/target/res/xml/test.xml
index 0fe21c6..56a3f7f 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 033305a..58504a7 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 9bcd6dc..c80e5eb 100644
--- a/cmds/idmap2/tests/data/target/target.apk
+++ b/cmds/idmap2/tests/data/target/target.apk
Binary files differ
diff --git a/cmds/idmap2/valgrind.sh b/cmds/idmap2/valgrind.sh
new file mode 100755
index 0000000..b4ebab0
--- /dev/null
+++ b/cmds/idmap2/valgrind.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+#
+# 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.
+#
+
+function _log()
+{
+ echo -e "$*" >&2
+}
+
+function _eval()
+{
+ local label="$1"
+ local cmd="$2"
+ local red="\e[31m"
+ local green="\e[32m"
+ local reset="\e[0m"
+ local output
+
+ _log "${green}[ RUN ]${reset} ${label}"
+ output="$(eval "$cmd" 2>&1)"
+ if [[ $? -eq 0 ]]; then
+ _log "${green}[ OK ]${reset} ${label}"
+ return 0
+ else
+ echo "${output}"
+ _log "${red}[ FAILED ]${reset} ${label}"
+ errors=$((errors + 1))
+ return 1
+ fi
+}
+
+errors=0
+script="$(readlink -f "$BASH_SOURCE")"
+prefix="$(dirname "$script")"
+target_path="${prefix}/tests/data/target/target.apk"
+overlay_path="${prefix}/tests/data/overlay/overlay.apk"
+idmap_path="/tmp/a.idmap"
+valgrind="valgrind --error-exitcode=1 -q --track-origins=yes --leak-check=full"
+
+_eval "idmap2 create" "$valgrind idmap2 create --policy public --target-apk-path $target_path --overlay-apk-path $overlay_path --idmap-path $idmap_path"
+_eval "idmap2 dump" "$valgrind idmap2 dump --idmap-path $idmap_path"
+_eval "idmap2 lookup" "$valgrind idmap2 lookup --idmap-path $idmap_path --config '' --resid test.target:string/str1"
+_eval "idmap2 scan" "$valgrind idmap2 scan --input-directory ${prefix}/tests/data/overlay --recursive --target-package-name test.target --target-apk-path $target_path --output-directory /tmp --override-policy public"
+_eval "idmap2 verify" "$valgrind idmap2 verify --idmap-path $idmap_path"
+_eval "idmap2_tests" "$valgrind $ANDROID_HOST_OUT/nativetest64/idmap2_tests/idmap2_tests"
+exit $errors