diff options
author | Xin Li <delphij@google.com> | 2020-08-31 21:21:38 -0700 |
---|---|---|
committer | Xin Li <delphij@google.com> | 2020-08-31 21:21:38 -0700 |
commit | 628590d7ec80e10a3fc24b1c18a1afb55cca10a8 (patch) | |
tree | 4b1c3f52d86d7fb53afbe9e9438468588fa489f8 /cmds/idmap2 | |
parent | b11b8ec3aec8bb42f2c07e1c5ac7942da293baa8 (diff) | |
parent | d2d3a20624d968199353ccf6ddbae6f3ac39c9af (diff) |
Merge Android R (rvc-dev-plus-aosp-without-vendor@6692709)
Bug: 166295507
Merged-In: I3d92a6de21a938f6b352ec26dc23420c0fe02b27
Change-Id: Ifdb80563ef042738778ebb8a7581a97c4e3d96e2
Diffstat (limited to 'cmds/idmap2')
72 files changed, 3525 insertions, 1470 deletions
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index 4e57e884ee3e..878cef94b674 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -43,17 +43,7 @@ cc_library { ], 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 @@ cc_library { "libcutils", "libutils", "libziparchive", + "libidmap2_policies", ], }, host: { @@ -79,6 +70,37 @@ cc_library { "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 @@ cc_test { "tests/PoliciesTests.cpp", "tests/PrettyPrintVisitorTests.cpp", "tests/RawPrintVisitorTests.cpp", + "tests/ResourceMappingTests.cpp", "tests/ResourceUtilsTests.cpp", "tests/ResultTests.cpp", - "tests/XmlTests.cpp", + "tests/XmlParserTests.cpp", "tests/ZipFileTests.cpp", ], required: [ @@ -123,6 +146,7 @@ cc_test { "libutils", "libz", "libziparchive", + "libidmap2_policies", ], }, host: { @@ -134,6 +158,7 @@ cc_test { "liblog", "libutils", "libziparchive", + "libidmap2_policies", ], shared_libs: [ "libz", @@ -150,12 +175,13 @@ cc_binary { ], 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 @@ cc_binary { "libidmap2", "libutils", "libziparchive", + "libidmap2_policies", ], }, host: { @@ -177,12 +204,14 @@ cc_binary { "liblog", "libutils", "libziparchive", + "libidmap2_policies", ], shared_libs: [ "libz", ], }, }, + } cc_binary { @@ -203,6 +232,7 @@ cc_binary { "libidmap2", "libutils", "libziparchive", + "libidmap2_policies", ], static_libs: [ "libidmap2daidl", @@ -235,3 +265,17 @@ filegroup { ], 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 9dc6b4a77380..20ed43c2a76a 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 26ccf038cba2..9e0fb84c7949 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 index 9cb67b33e6cf..8f5845bf2e53 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 @@ Result<Unit> Verify(const std::vector<std::string>& args) { 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 000000000000..e717e046d15d --- /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 718e361b38ab..69eea8d262d2 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 f482191b09a8..9682b6ead293 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::BinaryStreamVisitor; 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 @@ Result<Unit> Create(const std::vector<std::string>& args) { } 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 @@ Result<Unit> Create(const std::vector<std::string>& args) { } 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 @@ Result<Unit> Create(const std::vector<std::string>& args) { return Error("failed to load apk %s", overlay_apk_path.c_str()); } - const auto idmap = Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, - *overlay_apk, fulfilled_policies, !ignore_overlayable); + const auto idmap = + Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, !ignore_overlayable); if (!idmap) { return Error(idmap.GetError(), "failed to create idmap"); } diff --git a/cmds/idmap2/idmap2/CreateMultiple.cpp b/cmds/idmap2/idmap2/CreateMultiple.cpp new file mode 100644 index 000000000000..abdfaf4dccab --- /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 b7ae9d090cee..c44170928992 100644 --- a/cmds/idmap2/idmap2/Lookup.cpp +++ b/cmds/idmap2/idmap2/Lookup.cpp @@ -33,9 +33,10 @@ #include "androidfw/Util.h" #include "idmap2/CommandLineOptions.h" #include "idmap2/Idmap.h" +#include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" #include "idmap2/SysTrace.h" -#include "idmap2/Xml.h" +#include "idmap2/XmlParser.h" #include "idmap2/ZipFile.h" #include "utils/String16.h" #include "utils/String8.h" @@ -57,8 +58,7 @@ using android::idmap2::IdmapHeader; using android::idmap2::ResourceId; using android::idmap2::Result; using android::idmap2::Unit; -using android::idmap2::Xml; -using android::idmap2::ZipFile; +using android::idmap2::utils::ExtractOverlayManifestInfo; using android::util::Utf16ToUtf8; namespace { @@ -85,76 +85,90 @@ Result<ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am, const return Error("failed to obtain resource id for %s", res.c_str()); } -Result<std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) { - Res_value value; - ResTable_config config; - uint32_t flags; - ApkAssetsCookie cookie = am.GetResource(resid, false, 0, &value, &config, &flags); - if (cookie == kInvalidCookie) { - return Error("no resource 0x%08x in asset manager", resid); - } - - std::string out; - - // TODO(martenkongstad): use optional parameter GetResource(..., std::string* - // stacktrace = NULL) instead - out.append(StringPrintf("cookie=%d ", cookie)); - - out.append("config='"); - out.append(config.toString().c_str()); - out.append("' value="); - +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)); + out->append(StringPrintf("%d", value.data)); break; case Res_value::TYPE_INT_HEX: - out.append(StringPrintf("0x%08x", value.data)); + out->append(StringPrintf("0x%08x", value.data)); break; case Res_value::TYPE_INT_BOOLEAN: - out.append(value.data != 0 ? "true" : "false"); + out->append(value.data != 0 ? "true" : "false"); break; case Res_value::TYPE_STRING: { - const ResStringPool* pool = am.GetStringPoolForCookie(cookie); + 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); + out->append(str, len); } else { const char16_t* str16 = pool->stringAt(value.data, &len); - out += Utf16ToUtf8(StringPiece16(str16, len)); + out->append(Utf16ToUtf8(StringPiece16(str16, len))); } + out->append("\""); } break; default: - out.append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data)); + out->append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data)); break; } - 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"); +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, true, 0, &value, &config, &flags); + if (cookie == kInvalidCookie) { + return Error("no resource 0x%08x in asset manager", resid); } - const auto iter = tag->find("targetPackage"); - if (iter == tag->end()) { - return Error("failed to find targetPackage attribute"); + + std::string out; + + // TODO(martenkongstad): use optional parameter GetResource(..., std::string* + // stacktrace = NULL) instead + out.append(StringPrintf("cookie=%d ", cookie)); + + out.append("config='"); + out.append(config.toString().c_str()); + out.append("' value="); + + 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)); + 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 iter->second; + + return out; } + } // namespace Result<Unit> Lookup(const std::vector<std::string>& args) { @@ -202,12 +216,12 @@ Result<Unit> Lookup(const std::vector<std::string>& args) { } apk_assets.push_back(std::move(target_apk)); - const Result<std::string> package_name = - GetTargetPackageNameFromManifest(idmap_header->GetOverlayPath().to_string()); - if (!package_name) { - return Error("failed to parse android:targetPackage from overlay manifest"); + auto manifest_info = ExtractOverlayManifestInfo(idmap_header->GetOverlayPath().to_string(), + true /* assert_overlay */); + if (!manifest_info) { + return manifest_info.GetError(); } - target_package_name = *package_name; + target_package_name = (*manifest_info).target_package; } else if (target_path != idmap_header->GetTargetPath()) { return Error("different target APKs (expected target APK %s but %s has target APK %s)", target_path.c_str(), idmap_path.c_str(), @@ -235,7 +249,7 @@ Result<Unit> Lookup(const std::vector<std::string>& args) { 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 87949085cf1d..fb093f0f22a4 100644 --- a/cmds/idmap2/idmap2/Main.cpp +++ b/cmds/idmap2/idmap2/Main.cpp @@ -53,7 +53,8 @@ void PrintUsage(const NameToFunctionMap& commands, std::ostream& out) { 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 da8c06e5f74f..36250450cc74 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 @@ Result<std::unique_ptr<std::vector<std::string>>> FindApkFiles(const std::vector } 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 @@ std::vector<std::string> PoliciesForPath(const std::string& apk_path) { {"/system_ext/", kPolicySystem}, {"/vendor/", kPolicyVendor}, }; + // clang-format on std::vector<std::string> fulfilled_policies = {kPolicyPublic}; for (auto const& pair : values) { @@ -178,11 +182,11 @@ Result<Unit> Scan(const std::vector<std::string>& args) { // 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 @@ Result<Unit> Scan(const std::vector<std::string>& args) { 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 d7237763a2e5..15e22a3410cf 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 @@ PolicyBitmask ConvertAidlArgToPolicyBitmask(int32_t arg) { 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 @@ Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path, 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; + } + + // 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; + } + } - return ok(); + 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 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path, return error("failed to load apk " + overlay_apk_path); } - const auto idmap = Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, - *overlay_apk, policy_bitmask, enforce_overlayable); + const auto idmap = + Idmap::FromApkAssets(*target_apk, *overlay_apk, policy_bitmask, enforce_overlayable); if (!idmap) { return error(idmap.GetErrorMessage()); } + // 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 73a236a961af..abee999dd2b2 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 @@ class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 { } 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 cd474c0fe056..156f1d70e015 100644 --- a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl +++ b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl @@ -20,18 +20,13 @@ package android.os; * @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 000000000000..02b27a8800b6 --- /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 2c3e9d321881..ff45b1407dea 100644 --- a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h +++ b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h @@ -29,16 +29,19 @@ class BinaryStreamVisitor : public Visitor { 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 ebbb5ffc989d..0f05592b70f3 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 @@ static constexpr const uint32_t kIdmapCurrentVersion = android::kIdmapCurrentVer // 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 @@ class IdmapHeader { 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 @@ class IdmapHeader { 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 @@ class IdmapHeader { 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 @@ class IdmapData { return target_package_id_; } - inline uint16_t GetTypeCount() const { - return type_count_; - } - - void accept(Visitor* v) const; - - private: - Header() { - } - - PackageId target_package_id_; - uint16_t type_count_; - - friend Idmap; - DISALLOW_COPY_AND_ASSIGN(Header); - }; - - class TypeEntry { - public: - static std::unique_ptr<const TypeEntry> FromBinaryStream(std::istream& stream); - - inline TypeId GetTargetTypeId() const { - return target_type_id_; + inline PackageId GetOverlayPackageId() const { + return overlay_package_id_; } - inline TypeId GetOverlayTypeId() const { - return overlay_type_id_; + inline uint32_t GetTargetEntryCount() const { + return target_entry_count; } - inline uint16_t GetEntryCount() const { - return entries_.size(); + inline uint32_t GetOverlayEntryCount() const { + return overlay_entry_count; } - inline uint16_t GetEntryOffset() const { - return entry_offset_; + inline uint32_t GetStringPoolIndexOffset() const { + return string_pool_index_offset; } - inline EntryId GetEntry(size_t i) const { - return i < entries_.size() ? entries_[i] : 0xffffu; + inline uint32_t GetStringPoolLength() const { + return string_pool_len; } void accept(Visitor* v) const; private: - TypeEntry() { - } - - TypeId target_type_id_; - TypeId overlay_type_id_; - uint16_t entry_offset_; - std::vector<EntryId> entries_; + PackageId target_package_id_; + 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; - DISALLOW_COPY_AND_ASSIGN(TypeEntry); + friend IdmapData; + DISALLOW_COPY_AND_ASSIGN(Header); + }; + + struct TargetEntry { + ResourceId target_id; + TargetValue::DataType data_type; + TargetValue::DataValue data_value; + }; + + 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 @@ class IdmapData { } 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 @@ class Idmap { // file is used; change this in the next version of idmap to use a named // package instead; also update FromApkAssets to take additional parameters: // the target and overlay package names - static Result<std::unique_ptr<const Idmap>> FromApkAssets(const std::string& target_apk_path, - const ApkAssets& target_apk_assets, - const std::string& overlay_apk_path, + static Result<std::unique_ptr<const Idmap>> FromApkAssets(const ApkAssets& target_apk_assets, const ApkAssets& overlay_apk_assets, const PolicyBitmask& fulfilled_policies, bool enforce_overlayable); @@ -267,7 +302,6 @@ class Visitor { 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 000000000000..a6237e6f6ba9 --- /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 87edd3506d33..000000000000 --- 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 000000000000..b95b8b420658 --- /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 5111bb2eaab2..5dcf217e2aa3 100644 --- a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h +++ b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h @@ -33,17 +33,16 @@ class PrettyPrintVisitor : public Visitor { 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 2e543d4fabdd..92c186453611 100644 --- a/cmds/idmap2/include/idmap2/RawPrintVisitor.h +++ b/cmds/idmap2/include/idmap2/RawPrintVisitor.h @@ -34,22 +34,25 @@ class RawPrintVisitor : public Visitor { 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 000000000000..5869409e7db9 --- /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 9a0c2abced5a..c643b0e8800c 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<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path, Result<std::string> ResToTypeEntryName(const AssetManager2& am, ResourceId resid); -} // namespace android::idmap2::utils +} // namespace utils + +} // namespace android::idmap2 #endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_ diff --git a/cmds/idmap2/include/idmap2/Xml.h b/cmds/idmap2/include/idmap2/Xml.h deleted file mode 100644 index dd89dee0f64f..000000000000 --- a/cmds/idmap2/include/idmap2/Xml.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef IDMAP2_INCLUDE_IDMAP2_XML_H_ -#define IDMAP2_INCLUDE_IDMAP2_XML_H_ - -#include <map> -#include <memory> -#include <string> - -#include "android-base/macros.h" -#include "androidfw/ResourceTypes.h" -#include "utils/String16.h" - -namespace android::idmap2 { - -class Xml { - public: - static std::unique_ptr<const Xml> Create(const uint8_t* data, size_t size, bool copyData = false); - - std::unique_ptr<std::map<std::string, std::string>> FindTag(const std::string& name) const; - - ~Xml(); - - private: - Xml() { - } - - mutable ResXMLTree xml_; - - DISALLOW_COPY_AND_ASSIGN(Xml); -}; - -} // namespace android::idmap2 - -#endif // IDMAP2_INCLUDE_IDMAP2_XML_H_ diff --git a/cmds/idmap2/include/idmap2/XmlParser.h b/cmds/idmap2/include/idmap2/XmlParser.h new file mode 100644 index 000000000000..972a6d7e3427 --- /dev/null +++ b/cmds/idmap2/include/idmap2/XmlParser.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_ +#define IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_ + +#include <iostream> +#include <map> +#include <memory> +#include <string> + +#include "Result.h" +#include "android-base/macros.h" +#include "androidfw/ResourceTypes.h" +#include "utils/String16.h" + +namespace android::idmap2 { + +class XmlParser { + public: + using Event = ResXMLParser::event_code_t; + class iterator; + + class Node { + public: + Event event() const; + std::string name() const; + + Result<std::string> GetAttributeStringValue(const std::string& name) const; + Result<Res_value> GetAttributeValue(const std::string& name) const; + + bool operator==(const Node& rhs) const; + bool operator!=(const Node& rhs) const; + + private: + explicit Node(const ResXMLTree& tree); + Node(const ResXMLTree& tree, const ResXMLParser::ResXMLPosition& pos); + + // Retrieves/Sets the position of the position of the xml parser in the xml tree. + ResXMLParser::ResXMLPosition get_position() const; + void set_position(const ResXMLParser::ResXMLPosition& pos); + + // If `inner_child` is true, seek advances the parser to the first inner child of the current + // node. Otherwise, seek advances the parser to the following node. Returns false if there is + // no node to seek to. + bool Seek(bool inner_child); + + ResXMLParser parser_; + friend iterator; + }; + + class iterator { + public: + iterator(const iterator& other) : iterator(other.tree_, other.iter_) { + } + + inline iterator& operator=(const iterator& rhs) { + iter_.set_position(rhs.iter_.get_position()); + return *this; + } + + inline bool operator==(const iterator& rhs) const { + return iter_ == rhs.iter_; + } + + inline bool operator!=(const iterator& rhs) const { + return !(*this == rhs); + } + + inline iterator operator++() { + // Seek to the following xml node. + iter_.Seek(false /* inner_child */); + return *this; + } + + iterator begin() const { + iterator child_it(*this); + // Seek to the first inner child of the current node. + child_it.iter_.Seek(true /* inner_child */); + return child_it; + } + + iterator end() const { + iterator child_it = begin(); + while (child_it.iter_.Seek(false /* inner_child */)) { + // Continue iterating until the end tag is found. + } + + return child_it; + } + + inline const Node operator*() { + return Node(tree_, iter_.get_position()); + } + + inline const Node* operator->() { + return &iter_; + } + + private: + explicit iterator(const ResXMLTree& tree) : tree_(tree), iter_(Node(tree)) { + } + iterator(const ResXMLTree& tree, const Node& node) + : tree_(tree), iter_(Node(tree, node.get_position())) { + } + + const ResXMLTree& tree_; + Node iter_; + friend XmlParser; + }; + + // Creates a new xml parser beginning at the first tag. + static Result<std::unique_ptr<const XmlParser>> Create(const void* data, size_t size, + bool copy_data = false); + ~XmlParser(); + + inline iterator tree_iterator() const { + return iterator(tree_); + } + + inline const ResStringPool& get_strings() const { + return tree_.getStrings(); + } + + private: + XmlParser() = default; + mutable ResXMLTree tree_; + + DISALLOW_COPY_AND_ASSIGN(XmlParser); +}; + +} // namespace android::idmap2 + +#endif // IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_ diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp index dee2d219cbe1..255212ad4c66 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 @@ void BinaryStreamVisitor::Write32(uint32_t value) { 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 @@ void BinaryStreamVisitor::visit(const IdmapHeader& header) { 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); + } -void BinaryStreamVisitor::visit(const IdmapData::Header& header) { - Write16(header.GetTargetPackageId()); - Write16(header.GetTypeCount()); -} + for (const auto& overlay_entry : data.GetOverlayEntries()) { + Write32(overlay_entry.overlay_id); + Write32(overlay_entry.target_id); + } -void BinaryStreamVisitor::visit(const IdmapData::TypeEntry& type_entry) { - const uint16_t entryCount = type_entry.GetEntryCount(); + Write(data.GetStringPoolData(), data.GetHeader()->GetStringPoolLength()); +} - 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); - } +void BinaryStreamVisitor::visit(const IdmapData::Header& header) { + 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 4649675965db..23c25a7089de 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 android::idmap2 { namespace { -#define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16) - -#define EXTRACT_ENTRY(resid) (0x0000ffff & (resid)) - -class MatchingResources { - public: - void Add(ResourceId target_resid, ResourceId overlay_resid) { - 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 @@ bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) { 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 @@ bool WARN_UNUSED ReadString(std::istream& stream, char out[kIdmapStringLength]) return true; } -ResourceId NameToResid(const AssetManager2& am, const std::string& name) { - return am.GetResourceId(name); -} - -// TODO(martenkongstad): scan for package name instead of assuming package at index 0 -// -// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package -// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so -// this assumption tends to work out. That said, the correct thing to do is to scan -// resources.arsc for a package with a given name as read from the package manifest instead of -// relying on a hard-coded index. This however requires storing the package name in the idmap -// header, which in turn requires incrementing the idmap version. Because the initial version of -// idmap2 is compatible with idmap, this will have to wait for now. -const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) { - const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages(); - if (packages.empty()) { - return nullptr; +Result<std::string> ReadString(std::istream& stream) { + uint32_t size; + if (!Read32(stream, &size)) { + return Error("failed to read string size"); + } + if (size == 0) { + return std::string(""); } - int id = packages[0]->GetPackageId(); - return loaded_arsc.GetPackageById(id); + 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 @@ Result<uint32_t> GetCrc(const ZipFile& zip) { : 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 @@ Result<Unit> IdmapHeader::IsUpToDate() const { 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()); + if (target_crc_ != target_crc) { + return Error("bad target crc: idmap version 0x%08x, file system version 0x%08x", target_crc_, + target_crc); } - Result<uint32_t> target_crc = GetCrc(*target_zip); - if (!target_crc) { - return Error("failed to get target crc"); + if (overlay_crc_ != overlay_crc) { + return Error("bad overlay crc: idmap version 0x%08x, file system version 0x%08x", overlay_crc_, + overlay_crc); } - if (target_crc_ != *target_crc) { - return Error("bad target crc: idmap version 0x%08x, file system version 0x%08x", target_crc_, - *target_crc); + if (fulfilled_policies_ != fulfilled_policies) { + return Error("bad fulfilled policies: idmap version 0x%08x, file system version 0x%08x", + fulfilled_policies, fulfilled_policies_); } - 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()); + 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"); } - Result<uint32_t> overlay_crc = GetCrc(*overlay_zip); - if (!overlay_crc) { - return Error("failed to get overlay crc"); + if (strcmp(target_path, target_path_) != 0) { + return Error("bad target path: idmap version %s, file system version %s", target_path, + target_path_); } - if (overlay_crc_ != *overlay_crc) { - return Error("bad overlay crc: idmap version 0x%08x, file system version 0x%08x", overlay_crc_, - *overlay_crc); + 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 @@ Result<Unit> IdmapHeader::IsUpToDate() const { 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 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromBinaryStream(std::istream& strea return {std::move(idmap)}; } -std::string ConcatPolicies(const std::vector<std::string>& policies) { - std::string message; - for (const std::string& policy : policies) { - if (!message.empty()) { - message.append("|"); - } - message.append(policy); +Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping( + const ResourceMapping& resource_mapping) { + if (resource_mapping.GetTargetToOverlayMap().empty()) { + return Error("no resources were overlaid"); } - return message; -} - -Result<Unit> CheckOverlayable(const LoadedPackage& target_package, - const utils::OverlayManifestInfo& overlay_info, - const PolicyBitmask& fulfilled_policies, const ResourceId& resid) { - static constexpr const PolicyBitmask sDefaultPolicies = - PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION | - PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION | - PolicyFlags::POLICY_PRODUCT_PARTITION | PolicyFlags::POLICY_SIGNATURE; - - // If the resource does not have an overlayable definition, allow the resource to be overlaid if - // the overlay is preinstalled or signed with the same signature as the target. - if (!target_package.DefinesOverlayable()) { - return (sDefaultPolicies & fulfilled_policies) != 0 - ? Result<Unit>({}) - : Error( - "overlay must be preinstalled or signed with the same signature as the " - "target"); + 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}); } - 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"); + for (const auto& mappings : resource_mapping.GetOverlayToTargetMap()) { + data->overlay_entries_.emplace_back(IdmapData::OverlayEntry{mappings.first, mappings.second}); } - 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()); - } + 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(); - // 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()); - } + 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); - return Result<Unit>({}); + data->header_ = std::move(data_header); + return {std::move(data)}; } -Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& target_apk_path, - const ApkAssets& target_apk_assets, - const std::string& overlay_apk_path, +Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const ApkAssets& target_apk_assets, const ApkAssets& overlay_apk_assets, const PolicyBitmask& fulfilled_policies, bool enforce_overlayable) { SYSTRACE << "Idmap::FromApkAssets"; - AssetManager2 target_asset_manager; - if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true, false)) { - return Error("failed to create target asset manager"); - } - - AssetManager2 overlay_asset_manager; - if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets}, true, false)) { - return Error("failed to create overlay asset manager"); - } - - const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc(); - if (target_arsc == nullptr) { - return Error("failed to load target resources.arsc"); - } - - const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc(); - if (overlay_arsc == nullptr) { - return Error("failed to load overlay resources.arsc"); - } - - const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc); - if (target_pkg == nullptr) { - return Error("failed to load target package from resources.arsc"); - } - - const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc); - if (overlay_pkg == nullptr) { - return Error("failed to load overlay package from resources.arsc"); - } + const std::string& target_apk_path = target_apk_assets.GetPath(); + const std::string& overlay_apk_path = overlay_apk_assets.GetPath(); const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_apk_path); if (!target_zip) { @@ -366,27 +333,25 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& tar return Error("failed to open overlay as zip"); } - auto overlay_info = utils::ExtractOverlayManifestInfo(overlay_apk_path); - if (!overlay_info) { - return overlay_info.GetError(); - } - std::unique_ptr<IdmapHeader> header(new IdmapHeader()); header->magic_ = kIdmapMagic; header->version_ = kIdmapCurrentVersion; - 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 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& tar memcpy(header->target_path_, target_apk_path.data(), target_apk_path.size()); if (overlay_apk_path.size() > sizeof(header->overlay_path_)) { - return Error("overlay apk path \"%s\" longer than maximum size %zu", target_apk_path.c_str(), + return Error("overlay apk path \"%s\" longer than maximum size %zu", overlay_apk_path.c_str(), sizeof(header->target_path_)); } memset(header->overlay_path_, 0, sizeof(header->overlay_path_)); memcpy(header->overlay_path_, overlay_apk_path.data(), overlay_apk_path.size()); - std::unique_ptr<Idmap> idmap(new Idmap()); - idmap->header_ = std::move(header); - - // find the resources that exist in both packages - MatchingResources matching_resources; - const auto end = overlay_pkg->end(); - for (auto iter = overlay_pkg->begin(); iter != end; ++iter) { - const ResourceId overlay_resid = *iter; - Result<std::string> name = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid); - if (!name) { - continue; - } - // prepend "<package>:" to turn name into "<package>:<type>/<name>" - const std::string full_name = - base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name->c_str()); - const ResourceId target_resid = NameToResid(target_asset_manager, full_name); - if (target_resid == 0) { - continue; - } - - if (enforce_overlayable) { - Result<Unit> success = - CheckOverlayable(*target_pkg, *overlay_info, fulfilled_policies, target_resid); - if (!success) { - LOG(WARNING) << "overlay \"" << overlay_apk_path - << "\" is not allowed to overlay resource \"" << full_name - << "\": " << success.GetErrorMessage(); - continue; - } - } - - matching_resources.Add(target_resid, overlay_resid); + auto overlay_info = utils::ExtractOverlayManifestInfo(overlay_apk_path); + if (!overlay_info) { + return overlay_info.GetError(); } - if (matching_resources.Map().empty()) { - return Error("overlay \"%s\" does not successfully overlay any resource", - overlay_apk_path.c_str()); + 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(); } - // encode idmap data - std::unique_ptr<IdmapData> data(new IdmapData()); - const auto types_end = matching_resources.Map().cend(); - for (auto ti = matching_resources.Map().cbegin(); ti != types_end; ++ti) { - auto ei = ti->second.cbegin(); - std::unique_ptr<IdmapData::TypeEntry> type(new IdmapData::TypeEntry()); - type->target_type_id_ = EXTRACT_TYPE(ei->first); - type->overlay_type_id_ = EXTRACT_TYPE(ei->second); - type->entry_offset_ = EXTRACT_ENTRY(ei->first); - EntryId last_target_entry = kNoEntry; - for (; ei != ti->second.cend(); ++ei) { - if (last_target_entry != kNoEntry) { - int count = EXTRACT_ENTRY(ei->first) - last_target_entry - 1; - type->entries_.insert(type->entries_.end(), count, kNoEntry); - } - type->entries_.push_back(EXTRACT_ENTRY(ei->second)); - last_target_entry = EXTRACT_ENTRY(ei->first); - } - data->type_entries_.push_back(std::move(type)); + auto idmap_data = IdmapData::FromResourceMapping(*resource_mapping); + if (!idmap_data) { + return idmap_data.GetError(); } - std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header()); - data_header->target_package_id_ = target_pkg->GetPackageId(); - data_header->type_count_ = data->type_entries_.size(); - data->header_ = std::move(data_header); - - idmap->data_.push_back(std::move(data)); + std::unique_ptr<Idmap> idmap(new Idmap()); + header->debug_info_ = log_info.GetString(); + idmap->header_ = std::move(header); + idmap->data_.push_back(std::move(*idmap_data)); return {std::move(idmap)}; } @@ -481,25 +402,16 @@ void IdmapData::Header::accept(Visitor* v) const { 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 495fe615a91f..000000000000 --- 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 000000000000..fc5182af61c1 --- /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 fbf2c777be9a..63ee8a648352 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 @@ namespace android::idmap2 { #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 dd14fd47aea8..3f62a2ae2029 100644 --- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp @@ -16,21 +16,30 @@ #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 android::idmap2 { +namespace { + +size_t StringSizeWhenEncoded(const std::string& s) { + size_t null_bytes = 4 - (s.size() % 4); + return sizeof(uint32_t) + s.size() + null_bytes; +} -// verbatim copy fomr PrettyPrintVisitor.cpp, move to common utils -#define RESID(pkg, type, entry) (((pkg) << 24) | ((type) << 16) | (entry)) +} // namespace + +namespace android::idmap2 { void RawPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) { } @@ -40,53 +49,105 @@ void RawPrintVisitor::visit(const IdmapHeader& header) { 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(); -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(); -} + 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"); + } -void RawPrintVisitor::visit(const IdmapData::TypeEntry& type_entry) { - const bool target_package_loaded = !target_am_.GetApkAssets().empty(); + 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"); + } + } - 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"); + 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); + } - for (uint16_t i = 0; i < type_entry.GetEntryCount(); i++) { - const EntryId entry = type_entry.GetEntry(i); - if (entry == kNoEntry) { - print(kPadding, "no entry"); + if (target_name) { + print(overlay_entry.target_id, "target id: %s", target_name->c_str()); } 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); - } + 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(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"); +} + +// 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); + + stream_ << base::StringPrintf("%08zx: %02x", offset_, value) << " " << comment + << std::endl; + + offset_ += sizeof(uint8_t); } // NOLINTNEXTLINE(cert-dcl50-cpp) @@ -116,17 +177,30 @@ void RawPrintVisitor::print(uint32_t value, const char* fmt, ...) { } // 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 000000000000..34589a1c39dc --- /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 dce83e35978d..98d026bc70dc 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 @@ Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path, return Error("failed to uncompress AndroidManifest.xml from %s", path.c_str()); } - std::unique_ptr<const Xml> xml = Xml::Create(entry->buf, entry->size); + Result<std::unique_ptr<const XmlParser>> xml = XmlParser::Create(entry->buf, entry->size); if (!xml) { return Error("failed to parse AndroidManifest.xml from %s", path.c_str()); } + auto manifest_it = (*xml)->tree_iterator(); + if (manifest_it->event() != XmlParser::Event::START_TAG || manifest_it->name() != "manifest") { + return Error("root element tag is not <manifest> in AndroidManifest.xml of %s", path.c_str()); + } + + auto overlay_it = std::find_if(manifest_it.begin(), manifest_it.end(), [](const auto& it) { + return it.event() == XmlParser::Event::START_TAG && it.name() == "overlay"; + }); + OverlayManifestInfo info{}; - const auto tag = xml->FindTag("overlay"); - if (!tag) { - if (assert_overlay) { - return Error("<overlay> missing from AndroidManifest.xml of %s", path.c_str()); + if (overlay_it == manifest_it.end()) { + if (!assert_overlay) { + return info; } - return info; + return Error("<overlay> missing from AndroidManifest.xml of %s", path.c_str()); } - auto iter = tag->find("targetPackage"); - if (iter == tag->end()) { - if (assert_overlay) { - return Error("android:targetPackage missing from <overlay> of %s", path.c_str()); - } + if (auto result_str = overlay_it->GetAttributeStringValue("targetPackage")) { + info.target_package = *result_str; } else { - info.target_package = iter->second; + return Error("android:targetPackage missing from <overlay> of %s: %s", path.c_str(), + result_str.GetErrorMessage().c_str()); } - iter = tag->find("targetName"); - if (iter != tag->end()) { - info.target_name = iter->second; + if (auto result_str = overlay_it->GetAttributeStringValue("targetName")) { + info.target_name = *result_str; } - iter = tag->find("isStatic"); - if (iter != tag->end()) { - info.is_static = std::stoul(iter->second) != 0U; + if (auto result_value = overlay_it->GetAttributeValue("resourcesMap")) { + if (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()); + } + } + + if (auto result_value = overlay_it->GetAttributeValue("priority")) { + if ((*result_value).dataType >= Res_value::TYPE_FIRST_INT && + (*result_value).dataType <= Res_value::TYPE_LAST_INT) { + info.priority = (*result_value).data; + } else { + return Error("android:priority is not an integer in AndroidManifest.xml of %s", path.c_str()); + } } - iter = tag->find("requiredSystemPropertyName"); - if (iter != tag->end()) { - info.requiredSystemPropertyName = iter->second; + if (auto result_str = overlay_it->GetAttributeStringValue("requiredSystemPropertyName")) { + info.requiredSystemPropertyName = *result_str; } - iter = tag->find("requiredSystemPropertyValue"); - if (iter != tag->end()) { - info.requiredSystemPropertyValue = iter->second; + if (auto result_str = overlay_it->GetAttributeStringValue("requiredSystemPropertyValue")) { + info.requiredSystemPropertyValue = *result_str; } return info; diff --git a/cmds/idmap2/libidmap2/Xml.cpp b/cmds/idmap2/libidmap2/Xml.cpp deleted file mode 100644 index 264586829c47..000000000000 --- a/cmds/idmap2/libidmap2/Xml.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "idmap2/Xml.h" - -#include <map> -#include <memory> -#include <string> -#include <utility> - -namespace android::idmap2 { - -std::unique_ptr<const Xml> Xml::Create(const uint8_t* data, size_t size, bool copyData) { - std::unique_ptr<Xml> xml(new Xml()); - if (xml->xml_.setTo(data, size, copyData) != NO_ERROR) { - return nullptr; - } - return xml; -} - -std::unique_ptr<std::map<std::string, std::string>> Xml::FindTag(const std::string& name) const { - const String16 tag_to_find(name.c_str(), name.size()); - xml_.restart(); - ResXMLParser::event_code_t type; - do { - type = xml_.next(); - if (type == ResXMLParser::START_TAG) { - size_t len; - const String16 tag(xml_.getElementName(&len)); - if (tag == tag_to_find) { - std::unique_ptr<std::map<std::string, std::string>> map( - new std::map<std::string, std::string>()); - for (size_t i = 0; i < xml_.getAttributeCount(); i++) { - const String16 key16(xml_.getAttributeName(i, &len)); - std::string key = String8(key16).c_str(); - - std::string value; - switch (xml_.getAttributeDataType(i)) { - case Res_value::TYPE_STRING: { - const String16 value16(xml_.getAttributeStringValue(i, &len)); - value = String8(value16).c_str(); - } break; - case Res_value::TYPE_INT_DEC: - case Res_value::TYPE_INT_HEX: - case Res_value::TYPE_INT_BOOLEAN: { - Res_value resValue; - xml_.getAttributeValue(i, &resValue); - value = std::to_string(resValue.data); - } break; - default: - return nullptr; - } - - map->emplace(std::make_pair(key, value)); - } - return map; - } - } - } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT); - return nullptr; -} - -Xml::~Xml() { - xml_.uninit(); -} - -} // namespace android::idmap2 diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp new file mode 100644 index 000000000000..526a560907aa --- /dev/null +++ b/cmds/idmap2/libidmap2/XmlParser.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "idmap2/XmlParser.h" + +#include <iostream> +#include <map> +#include <memory> +#include <string> +#include <utility> + +namespace android::idmap2 { + +template <typename T> +ResXMLParser::ResXMLPosition get_tree_position(const T& tree) { + ResXMLParser::ResXMLPosition pos{}; + tree.getPosition(&pos); + return pos; +} + +XmlParser::Node::Node(const ResXMLTree& tree) : Node(tree, get_tree_position(tree)) { +} +XmlParser::Node::Node(const ResXMLTree& tree, const ResXMLParser::ResXMLPosition& pos) + : parser_(tree) { + set_position(pos); +} + +bool XmlParser::Node::operator==(const XmlParser::Node& rhs) const { + ResXMLParser::ResXMLPosition pos = get_position(); + ResXMLParser::ResXMLPosition rhs_pos = rhs.get_position(); + return pos.curExt == rhs_pos.curExt && pos.curNode == rhs_pos.curNode && + pos.eventCode == rhs_pos.eventCode; +} + +bool XmlParser::Node::operator!=(const XmlParser::Node& rhs) const { + return !(*this == rhs); +} + +ResXMLParser::ResXMLPosition XmlParser::Node::get_position() const { + return get_tree_position(parser_); +} + +void XmlParser::Node::set_position(const ResXMLParser::ResXMLPosition& pos) { + parser_.setPosition(pos); +} + +bool XmlParser::Node::Seek(bool inner_child) { + if (parser_.getEventType() == XmlParser::Event::END_TAG) { + return false; + } + + ssize_t depth = 0; + XmlParser::Event code; + while ((code = parser_.next()) != XmlParser::Event::BAD_DOCUMENT && + code != XmlParser::Event::END_DOCUMENT) { + if (code == XmlParser::Event::START_TAG) { + if (++depth == (inner_child ? 1 : 0)) { + return true; + } + } else if (code == XmlParser::Event::END_TAG) { + if (--depth == (inner_child ? -1 : -2)) { + return false; + } + } + } + + return false; +} + +XmlParser::Event XmlParser::Node::event() const { + return parser_.getEventType(); +} + +std::string XmlParser::Node::name() const { + size_t len; + const String16 key16(parser_.getElementName(&len)); + return String8(key16).c_str(); +} + +Result<std::string> XmlParser::Node::GetAttributeStringValue(const std::string& name) const { + auto value = GetAttributeValue(name); + if (!value) { + return value.GetError(); + } + + switch ((*value).dataType) { + case Res_value::TYPE_STRING: { + size_t len; + const String16 value16(parser_.getStrings().stringAt((*value).data, &len)); + return std::string(String8(value16).c_str()); + } + case Res_value::TYPE_INT_DEC: + case Res_value::TYPE_INT_HEX: + case Res_value::TYPE_INT_BOOLEAN: { + return std::to_string((*value).data); + } + default: + return Error(R"(Failed to convert attribute "%s" value to a string)", name.c_str()); + } +} + +Result<Res_value> XmlParser::Node::GetAttributeValue(const std::string& name) const { + size_t len; + for (size_t i = 0; i < parser_.getAttributeCount(); i++) { + const String16 key16(parser_.getAttributeName(i, &len)); + std::string key = String8(key16).c_str(); + if (key != name) { + continue; + } + + Res_value res_value{}; + if (parser_.getAttributeValue(i, &res_value) == BAD_TYPE) { + return Error(R"(Bad value for attribute "%s")", name.c_str()); + } + + return res_value; + } + + return Error(R"(Failed to find attribute "%s")", name.c_str()); +} + +Result<std::unique_ptr<const XmlParser>> XmlParser::Create(const void* data, size_t size, + bool copy_data) { + auto parser = std::unique_ptr<const XmlParser>(new XmlParser()); + if (parser->tree_.setTo(data, size, copy_data) != NO_ERROR) { + return Error("Malformed xml block"); + } + + // Find the beginning of the first tag. + XmlParser::Event event; + while ((event = parser->tree_.next()) != XmlParser::Event::BAD_DOCUMENT && + event != XmlParser::Event::END_DOCUMENT && event != XmlParser::Event::START_TAG) { + } + + if (event == XmlParser::Event::END_DOCUMENT) { + return Error("Root tag was not be found"); + } + + if (event == XmlParser::Event::BAD_DOCUMENT) { + return Error("Bad xml document"); + } + + return parser; +} + +XmlParser::~XmlParser() { + tree_.uninit(); +} + +} // namespace android::idmap2 diff --git a/cmds/idmap2/libidmap2/ZipFile.cpp b/cmds/idmap2/libidmap2/ZipFile.cpp index 4f5e3a45f183..1e1a218163f0 100644 --- a/cmds/idmap2/libidmap2/ZipFile.cpp +++ b/cmds/idmap2/libidmap2/ZipFile.cpp @@ -34,6 +34,7 @@ std::unique_ptr<const ZipFile> ZipFile::Open(const std::string& path) { ::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 000000000000..5bd353af4ad3 --- /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 41d3c69540b2..a372abdaa146 100755 --- a/cmds/idmap2/static-checks.sh +++ b/cmds/idmap2/static-checks.sh @@ -27,10 +27,11 @@ function _eval() 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 9348ab721493..5fea7bcdaac5 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 @@ TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) { 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]; - - 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)); -} - -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 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()); - - const auto idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, - PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true); - ASSERT_TRUE(idmap); - - 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 IdmapEntry_header* header = loaded_idmap->GetEntryMapForType(0x01); - ASSERT_THAT(header, NotNull()); - - 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); + 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()); - success = LoadedIdmap::Lookup(header, 0x000c, &entry); // string/str1 - ASSERT_TRUE(success); - ASSERT_EQ(entry, 0x0000); + 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()); - success = LoadedIdmap::Lookup(header, 0x000d, &entry); // string/str2 - ASSERT_FALSE(success); + 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); - success = LoadedIdmap::Lookup(header, 0x000e, &entry); // string/str3 - ASSERT_TRUE(success); - ASSERT_EQ(entry, 0x0001); + ASSERT_EQ(target_entries1[1].target_id, target_entries2[1].target_id); + ASSERT_EQ(target_entries1[1].data_value, target_entries2[1].data_value); - success = LoadedIdmap::Lookup(header, 0x000f, &entry); // string/str4 - ASSERT_TRUE(success); - ASSERT_EQ(entry, 0x0002); + ASSERT_EQ(target_entries1[2].target_id, target_entries2[2].target_id); + ASSERT_EQ(target_entries1[2].data_value, target_entries2[2].data_value); - success = LoadedIdmap::Lookup(header, 0x0010, &entry); // string/x - ASSERT_FALSE(success); + 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); - success = LoadedIdmap::Lookup(header, 0x0011, &entry); // string/y - ASSERT_FALSE(success); + ASSERT_EQ(overlay_entries1[1].overlay_id, overlay_entries2[1].overlay_id); + ASSERT_EQ(overlay_entries1[1].target_id, overlay_entries2[1].target_id); - 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 f55acee029dc..8af4037be954 100644 --- a/cmds/idmap2/tests/FileUtilsTests.cpp +++ b/cmds/idmap2/tests/FileUtilsTests.cpp @@ -56,12 +56,12 @@ TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) { 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 499eb99af290..d896cf9c11ba 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 @@ TEST_F(Idmap2BinaryTests, Dump) { // 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 @@ TEST_F(Idmap2BinaryTests, Dump) { 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 @@ TEST_F(Idmap2BinaryTests, Lookup) { "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 0f47f1e77d7b..6fab5e0f8ae1 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 @@ TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) { 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, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) { } 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); - - 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& 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); + + 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 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) { 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_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); - - 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); + ASSERT_THAT(data, NotNull()); + + 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); + + 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 @@ TEST(IdmapTests, GracefullyFailToCreateIdmapFromCorruptBinaryStream) { 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()); +TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) { + std::string target_apk_path = GetTestDataPath() + "/target/target.apk"; + 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.to_string()); + std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); 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; - 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); + 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; - 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); - ASSERT_THAT(idmap, NotNull()); - - const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData(); - ASSERT_EQ(dataBlocks.size(), 1U); +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::unique_ptr<const IdmapData>& data = dataBlocks[0]; + 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()); + } - ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU); - ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U); + LogInfo log_info; + auto mapping = ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, overlay_info, + fulfilled_policies, enforce_overlayable, log_info); - const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries(); - ASSERT_EQ(types.size(), 1U); + if (!mapping) { + return mapping.GetError(); + } - 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 + return IdmapData::FromResourceMapping(*mapping); } -TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySignature) { - std::unique_ptr<const Idmap> idmap; +TEST(IdmapTests, CreateIdmapDataFromApkAssets) { 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); - 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(), 1U); + std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk"; - const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries(); - ASSERT_EQ(types.size(), 1U); + std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); + ASSERT_THAT(target_apk, NotNull()); - 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 -} + std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); + ASSERT_THAT(overlay_apk, NotNull()); -// 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); + 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 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& 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 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); } -// Overlays should ignore all overlayable restrictions if enforcement of overlayable is disabled. -TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) { - std::unique_ptr<const Idmap> idmap; +TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) { 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()); + std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay-shared.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]; + std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); + ASSERT_THAT(target_apk, NotNull()); - 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 -} + std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); + ASSERT_THAT(overlay_apk, NotNull()); -// 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); + 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(), 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 + 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 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 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()); +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); + + ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage(); + auto& data = *idmap_data; + + 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); + + 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 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(); +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); + + ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage(); + auto& data = *idmap_data; + + 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" + + const auto& overlay_entries = data->GetOverlayEntries(); + ASSERT_EQ(overlay_entries.size(), 0U); } TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) { @@ -480,9 +368,8 @@ TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) { std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); ASSERT_THAT(overlay_apk, NotNull()); - const auto result = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, - PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true); + const auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC, + /* enforce_overlayable */ true); ASSERT_FALSE(result); } @@ -497,8 +384,7 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); ASSERT_THAT(overlay_apk, NotNull()); - auto result = Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, - PolicyFlags::POLICY_PUBLIC, + auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC, /* enforce_overlayable */ true); ASSERT_TRUE(result); const auto idmap = std::move(*result); @@ -509,7 +395,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { 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 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { 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 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { 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 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { 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 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { 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()); - - // target path: bytes (0x10, 0x10f) + ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(), + PolicyFlags::PUBLIC, /* enforce_overlayable */ true)); + + // 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 @@ class TestVisitor : public Visitor { 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 @@ TEST(IdmapTests, TestVisitor) { (*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 eca74045f428..1b2775939886 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 c41250457678..9a10079772bf 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 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) { std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); ASSERT_THAT(overlay_apk, NotNull()); - const auto idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, - PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true); + const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC, + /* enforce_overlayable */ true); ASSERT_TRUE(idmap); std::stringstream stream; @@ -54,7 +56,8 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) { 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 000000000000..aed263a49aa3 --- /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 26951763cd66..b268d5add141 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 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); ASSERT_THAT(overlay_apk, NotNull()); - const auto idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, - PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true); + const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::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 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) { 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 000000000000..de039f440e33 --- /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 000000000000..6bc924e5ac3c --- /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 adea3293534d..b599dcb0069a 100644 --- a/cmds/idmap2/tests/TestHelpers.h +++ b/cmds/idmap2/tests/TestHelpers.h @@ -30,7 +30,7 @@ const unsigned char idmap_raw_data[] = { 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 @@ const unsigned char idmap_raw_data[] = { // 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 @@ const unsigned char idmap_raw_data[] = { 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 @@ const unsigned char idmap_raw_data[] = { 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, - // DATA HEADER - // 0x210: target package id - 0x7f, 0x00, + // 0x215: debug string + // string length, including terminating null + 0x08, 0x00, 0x00, 0x00, - // 0x212: types count - 0x02, 0x00, + // string contents "debug\0\0\0" (padded to word alignment) + 0x64, 0x65, 0x62, 0x75, 0x67, 0x00, 0x00, 0x00, - // DATA BLOCK - // 0x214: target type - 0x02, 0x00, + // DATA HEADER + // 0x221: target_package_id + 0x7f, - // 0x216: overlay type - 0x02, 0x00, + // 0x222: overlay_package_id + 0x7f, - // 0x218: entry count - 0x01, 0x00, + // 0x223: target_entry_count + 0x03, 0x00, 0x00, 0x00, - // 0x21a: entry offset - 0x00, 0x00, + // 0x227: overlay_entry_count + 0x03, 0x00, 0x00, 0x00, - // 0x21c: entries + // 0x22b: string_pool_offset 0x00, 0x00, 0x00, 0x00, - // DATA BLOCK - // 0x220: target type - 0x03, 0x00, + // 0x22f: string_pool_byte_length + 0x00, 0x00, 0x00, 0x00, - // 0x222: overlay type - 0x03, 0x00, + // TARGET ENTRIES + // 0x233: 0x7f020000 + 0x00, 0x00, 0x02, 0x7f, - // 0x224: entry count - 0x03, 0x00, + // 0x237: TYPE_REFERENCE + 0x01, - // 0x226: entry offset - 0x03, 0x00, + // 0x238: 0x7f020000 + 0x00, 0x00, 0x02, 0x7f, - // 0x228, 0x22c, 0x230: entries - 0x00, 0x00, 0x00, 0x00, + // 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, - 0xff, 0xff, 0xff, 0xff, + // 0x256: 0x7f030000 -> 0x7f030000 + 0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f, - 0x01, 0x00, 0x00, 0x00}; + // 0x25e: 0x7f030001 -> 0x7f030002 + 0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f}; -const unsigned int idmap_raw_data_len = 565; +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 @@ class Idmap2Tests : public testing::Test { 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 000000000000..1a7eaca4d67b --- /dev/null +++ b/cmds/idmap2/tests/XmlParserTests.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <cstdio> // fclose +#include <memory> +#include <string> + +#include "TestHelpers.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "idmap2/XmlParser.h" +#include "idmap2/ZipFile.h" + +namespace android::idmap2 { + +Result<std::unique_ptr<const XmlParser>> CreateTestParser(const std::string& test_file) { + auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk"); + if (zip == nullptr) { + return Error("Failed to open zip file"); + } + + auto data = zip->Uncompress(test_file); + if (data == nullptr) { + return Error("Failed to open xml file"); + } + + return XmlParser::Create(data->buf, data->size, /* copy_data */ true); +} + +TEST(XmlParserTests, Create) { + auto xml = CreateTestParser("AndroidManifest.xml"); + ASSERT_TRUE(xml) << xml.GetErrorMessage(); + + fclose(stderr); // silence expected warnings from libandroidfw + const char* not_xml = "foo"; + auto fail = XmlParser::Create(reinterpret_cast<const uint8_t*>(not_xml), strlen(not_xml)); + ASSERT_FALSE(fail); +} + +TEST(XmlParserTests, NextChild) { + auto xml = CreateTestParser("res/xml/test.xml"); + ASSERT_TRUE(xml) << xml.GetErrorMessage(); + + auto root_iter = (*xml)->tree_iterator(); + ASSERT_EQ(root_iter->event(), XmlParser::Event::START_TAG); + ASSERT_EQ(root_iter->name(), "a"); + + auto a_iter = root_iter.begin(); + ASSERT_EQ(a_iter->event(), XmlParser::Event::START_TAG); + ASSERT_EQ(a_iter->name(), "b"); + + auto c_iter = a_iter.begin(); + ASSERT_EQ(c_iter->event(), XmlParser::Event::START_TAG); + ASSERT_EQ(c_iter->name(), "c"); + + ++c_iter; + ASSERT_EQ(c_iter->event(), XmlParser::Event::END_TAG); + ASSERT_EQ(c_iter, a_iter.end()); + + ++a_iter; + ASSERT_EQ(a_iter->event(), XmlParser::Event::START_TAG); + ASSERT_EQ(a_iter->name(), "d"); + + // Skip the <e> tag. + ++a_iter; + ASSERT_EQ(a_iter->event(), XmlParser::Event::END_TAG); + ASSERT_EQ(a_iter, root_iter.end()); +} + +TEST(XmlParserTests, AttributeValues) { + auto xml = CreateTestParser("res/xml/test.xml"); + ASSERT_TRUE(xml) << xml.GetErrorMessage(); + + // Start at the <a> tag. + auto root_iter = (*xml)->tree_iterator(); + + // Start at the <b> tag. + auto a_iter = root_iter.begin(); + auto attribute_str = a_iter->GetAttributeStringValue("type_string"); + ASSERT_TRUE(attribute_str); + ASSERT_EQ(*attribute_str, "fortytwo"); + + auto attribute_value = a_iter->GetAttributeValue("type_int_dec"); + ASSERT_TRUE(attribute_value); + ASSERT_EQ(attribute_value->data, 42); + + attribute_value = a_iter->GetAttributeValue("type_int_hex"); + ASSERT_TRUE(attribute_value); + ASSERT_EQ(attribute_value->data, 42); + + attribute_value = a_iter->GetAttributeValue("type_int_boolean"); + ASSERT_TRUE(attribute_value); + ASSERT_EQ(attribute_value->data, 0xffffffff); +} + +TEST(XmlParserTests, IteratorEquality) { + auto xml = CreateTestParser("res/xml/test.xml"); + ASSERT_TRUE(xml) << xml.GetErrorMessage(); + + // Start at the <a> tag. + auto root_iter_1 = (*xml)->tree_iterator(); + auto root_iter_2 = (*xml)->tree_iterator(); + ASSERT_EQ(root_iter_1, root_iter_2); + ASSERT_EQ(*root_iter_1, *root_iter_2); + + // Start at the <b> tag. + auto a_iter_1 = root_iter_1.begin(); + auto a_iter_2 = root_iter_2.begin(); + ASSERT_NE(a_iter_1, root_iter_1.end()); + ASSERT_NE(a_iter_2, root_iter_2.end()); + ASSERT_EQ(a_iter_1, a_iter_2); + ASSERT_EQ(*a_iter_1, *a_iter_2); + + // Move to the <d> tag. + ++a_iter_1; + ++a_iter_2; + ASSERT_NE(a_iter_1, root_iter_1.end()); + ASSERT_NE(a_iter_2, root_iter_2.end()); + ASSERT_EQ(a_iter_1, a_iter_2); + ASSERT_EQ(*a_iter_1, *a_iter_2); + + // Move to the end of the <a> tag. + ++a_iter_1; + ++a_iter_2; + ASSERT_EQ(a_iter_1, root_iter_1.end()); + ASSERT_EQ(a_iter_2, root_iter_2.end()); + ASSERT_EQ(a_iter_1, a_iter_2); + ASSERT_EQ(*a_iter_1, *a_iter_2); +} + +TEST(XmlParserTests, Backtracking) { + auto xml = CreateTestParser("res/xml/test.xml"); + ASSERT_TRUE(xml) << xml.GetErrorMessage(); + + // Start at the <a> tag. + auto root_iter_1 = (*xml)->tree_iterator(); + + // Start at the <b> tag. + auto a_iter_1 = root_iter_1.begin(); + + // Start a second iterator at the <a> tag. + auto root_iter_2 = root_iter_1; + ASSERT_EQ(root_iter_1, root_iter_2); + ASSERT_EQ(*root_iter_1, *root_iter_2); + + // Move the first iterator to the end of the <a> tag. + auto root_iter_end_1 = root_iter_1.end(); + ++root_iter_1; + ASSERT_NE(root_iter_1, root_iter_2); + ASSERT_NE(*root_iter_1, *root_iter_2); + + // Move to the <d> tag. + ++a_iter_1; + ASSERT_NE(a_iter_1, root_iter_end_1); + + // Move to the end of the <a> tag. + ++a_iter_1; + ASSERT_EQ(a_iter_1, root_iter_end_1); +} + +} // namespace android::idmap2 diff --git a/cmds/idmap2/tests/XmlTests.cpp b/cmds/idmap2/tests/XmlTests.cpp deleted file mode 100644 index df63211a9209..000000000000 --- a/cmds/idmap2/tests/XmlTests.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <cstdio> // fclose - -#include "TestHelpers.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "idmap2/Xml.h" -#include "idmap2/ZipFile.h" - -using ::testing::IsNull; -using ::testing::NotNull; - -namespace android::idmap2 { - -TEST(XmlTests, Create) { - auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk"); - ASSERT_THAT(zip, NotNull()); - - auto data = zip->Uncompress("AndroidManifest.xml"); - ASSERT_THAT(data, NotNull()); - - auto xml = Xml::Create(data->buf, data->size); - ASSERT_THAT(xml, NotNull()); - - fclose(stderr); // silence expected warnings from libandroidfw - const char* not_xml = "foo"; - auto fail = Xml::Create(reinterpret_cast<const uint8_t*>(not_xml), strlen(not_xml)); - ASSERT_THAT(fail, IsNull()); -} - -TEST(XmlTests, FindTag) { - auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk"); - ASSERT_THAT(zip, NotNull()); - - auto data = zip->Uncompress("res/xml/test.xml"); - ASSERT_THAT(data, NotNull()); - - auto xml = Xml::Create(data->buf, data->size); - ASSERT_THAT(xml, NotNull()); - - auto attrs = xml->FindTag("c"); - ASSERT_THAT(attrs, NotNull()); - ASSERT_EQ(attrs->size(), 4U); - ASSERT_EQ(attrs->at("type_string"), "fortytwo"); - ASSERT_EQ(std::stoi(attrs->at("type_int_dec")), 42); - ASSERT_EQ(std::stoi(attrs->at("type_int_hex")), 42); - ASSERT_NE(std::stoul(attrs->at("type_int_boolean")), 0U); - - auto fail = xml->FindTag("does-not-exist"); - ASSERT_THAT(fail, IsNull()); -} - -} // namespace android::idmap2 diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifest.xml b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml index 619bb6ce0f25..cf3691c3b3cf 100644 --- a/cmds/idmap2/tests/data/overlay/AndroidManifest.xml +++ b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml @@ -16,8 +16,11 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="test.overlay"> + <application android:hasCode="false"/> + <overlay android:targetPackage="test.target" - android:targetName="TestResources"/> + android:targetName="TestResources" + android:resourcesMap="@xml/overlays"/> </manifest> diff --git a/cmds/idmap2/tests/data/overlay/build b/cmds/idmap2/tests/data/overlay/build index 68b9f507a11d..114b099598fa 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 @@ aapt2 link \ -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 Binary files differindex 18ee43dc57a4..7c25985e5a61 100644 --- a/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk +++ b/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk diff --git a/cmds/idmap2/tests/data/overlay/overlay-no-name.apk b/cmds/idmap2/tests/data/overlay/overlay-no-name.apk Binary files differindex 642519008b15..c75f3e1dbddf 100644 --- a/cmds/idmap2/tests/data/overlay/overlay-no-name.apk +++ b/cmds/idmap2/tests/data/overlay/overlay-no-name.apk diff --git a/cmds/idmap2/tests/data/overlay/overlay-shared.apk b/cmds/idmap2/tests/data/overlay/overlay-shared.apk Binary files differnew file mode 100644 index 000000000000..93dcc82f9358 --- /dev/null +++ b/cmds/idmap2/tests/data/overlay/overlay-shared.apk diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-1.apk b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk Binary files differindex 642ab90d00ae..5b8a6e4a90ed 100644 --- a/cmds/idmap2/tests/data/overlay/overlay-static-1.apk +++ b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-2.apk b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk Binary files differindex 2ec56020c4aa..698a1fd6e702 100644 --- a/cmds/idmap2/tests/data/overlay/overlay-static-2.apk +++ b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk diff --git a/cmds/idmap2/tests/data/overlay/overlay.apk b/cmds/idmap2/tests/data/overlay/overlay.apk Binary files differindex 5842da4f432e..1db303ff05b5 100644 --- a/cmds/idmap2/tests/data/overlay/overlay.apk +++ b/cmds/idmap2/tests/data/overlay/overlay.apk diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays.xml new file mode 100644 index 000000000000..edd33f7dc90d --- /dev/null +++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<overlay> + <item target="string/str1" value="@string/str1"/> + <item target="string/str3" value="@string/str3" /> + <item target="string/str4" value="@string/str4" /> + <item target="integer/int1" value="@integer/int1" /> + <item target="integer/not_in_target" value="@integer/not_in_target" /> +</overlay> + diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml new file mode 100644 index 000000000000..aa7fefaa305e --- /dev/null +++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<overlay> + <item target="string/str1" value="@android:string/ok"/> + <item target="string/str3" value="@string/str3" /> +</overlay> + diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml new file mode 100644 index 000000000000..e12b823ff50d --- /dev/null +++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<overlay> + <item target="string/str1" value="Hello World"/> + <item target="integer/int1" value="73" /> +</overlay> + diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml new file mode 100644 index 000000000000..5728e672d94a --- /dev/null +++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<overlay> + <item target="string/str1" value="@string/str4"/> + <item target="string/str3" value="@string/str1" /> + <item target="string/str4" value="@string/str3" /> + <item target="integer/int_not_in_target" value="@integer/int1" /> +</overlay> diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml index 9ebfae41e4c5..7119d8283061 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 Binary files differindex 1456e749e796..bd990983693c 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 diff --git a/cmds/idmap2/tests/data/target/res/values/overlayable.xml b/cmds/idmap2/tests/data/target/res/values/overlayable.xml index 8389f5635e15..ad4cd4882632 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 a892c98f0fbb..5230e25e626b 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 0fe21c6b6d0a..56a3f7f0b13a 100644 --- a/cmds/idmap2/tests/data/target/res/xml/test.xml +++ b/cmds/idmap2/tests/data/target/res/xml/test.xml @@ -14,12 +14,15 @@ limitations under the License. --> <a> - <b> - <c - type_string="fortytwo" - type_int_dec="42" - type_int_hex="0x2a" - type_int_boolean="true" - /> + <b type_string="fortytwo" + type_int_dec="42" + type_int_hex="0x2a" + type_int_boolean="true"> + + <c /> </b> -</a> + + <d> + <e /> + </d> +</a>
\ No newline at end of file diff --git a/cmds/idmap2/tests/data/target/target-no-overlayable.apk b/cmds/idmap2/tests/data/target/target-no-overlayable.apk Binary files differindex 033305aaed4f..58504a74a83a 100644 --- a/cmds/idmap2/tests/data/target/target-no-overlayable.apk +++ b/cmds/idmap2/tests/data/target/target-no-overlayable.apk diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk Binary files differindex 9bcd6dcabcde..c80e5eb65ff2 100644 --- a/cmds/idmap2/tests/data/target/target.apk +++ b/cmds/idmap2/tests/data/target/target.apk diff --git a/cmds/idmap2/valgrind.sh b/cmds/idmap2/valgrind.sh new file mode 100755 index 000000000000..b4ebab0c7ffe --- /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 |