From efeb7af13be4446ce4a511a2ca707691e9a67c1e Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Wed, 2 Aug 2017 14:57:43 -0700 Subject: AAPT2: Fix windows unicode path issues Mingw64 was being difficult, so instead of defining a wmain entrypoint, the command line parameters are parsed manually using built-in Windows methods that support Unicode. The results are converted to UTF8 and handled just like the rest of the linux/mac version of the code. This also removes dependencies on std::istream in favour of a FileInputStream which calls the appropriate unicode version of open to read a file. No speed regressions found on Linux or MacOS. Bug: 62336414 Bug: 63830502 Test: manual Change-Id: I597da51e33729ed1b98bf246e7e773337fd3fee8 --- tools/aapt2/Main.cpp | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) (limited to 'tools/aapt2/Main.cpp') diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index c5d38abcdf71..89cce5ffca53 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -14,9 +14,17 @@ * limitations under the License. */ +#ifdef _WIN32 +// clang-format off +#include +#include +// clang-format on +#endif + #include #include +#include "android-base/utf8.h" #include "androidfw/StringPiece.h" #include "Diagnostics.h" @@ -43,7 +51,7 @@ extern int Optimize(const std::vector& args); } // namespace aapt -int main(int argc, char** argv) { +int MainImpl(int argc, char** argv) { if (argc >= 2) { argv += 1; argc -= 1; @@ -74,7 +82,31 @@ int main(int argc, char** argv) { std::cerr << "no command specified\n"; } - std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|version] ..." - << std::endl; + std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|version] ..." << std::endl; return 1; } + +int main(int argc, char** argv) { +#ifdef _WIN32 + LPWSTR* wide_argv = CommandLineToArgvW(GetCommandLineW(), &argc); + CHECK(wide_argv != nullptr) << "invalid command line parameters passed to process"; + + std::vector utf8_args; + for (int i = 0; i < argc; i++) { + std::string utf8_arg; + if (!::android::base::WideToUTF8(wide_argv[i], &utf8_arg)) { + std::cerr << "error converting input arguments to UTF-8" << std::endl; + return 1; + } + utf8_args.push_back(std::move(utf8_arg)); + } + LocalFree(wide_argv); + + std::unique_ptr utf8_argv(new char*[utf8_args.size()]); + for (int i = 0; i < argc; i++) { + utf8_argv[i] = const_cast(utf8_args[i].c_str()); + } + argv = utf8_argv.get(); +#endif + return MainImpl(argc, argv); +} -- cgit v1.2.3-59-g8ed1b From 448a15c84f60dc1b7255510ba268b4184e93a0eb Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Tue, 25 Jul 2017 10:59:26 -0700 Subject: AAPT2: Add a daemon mode Gradle and other build tools that need to frequently interact with aapt2 can use the daemon mode to issue regular CLI commands without paying the process start cost for each invocation. Test: manual Change-Id: I543858ed46496278bad643f0dae688c924ec6c3f --- tools/aapt2/Main.cpp | 128 ++++++++++++++++++++++++++++++++++------------- tools/aapt2/util/Files.h | 2 + tools/aapt2/util/Util.h | 14 +++++- 3 files changed, 106 insertions(+), 38 deletions(-) (limited to 'tools/aapt2/Main.cpp') diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index 89cce5ffca53..e4ca7a463df1 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -24,10 +24,16 @@ #include #include +#include "android-base/stringprintf.h" #include "android-base/utf8.h" #include "androidfw/StringPiece.h" #include "Diagnostics.h" +#include "util/Files.h" +#include "util/Util.h" + +using ::android::StringPiece; +using ::android::base::StringPrintf; namespace aapt { @@ -37,53 +43,103 @@ static const char* sMajorVersion = "2"; // Update minor version whenever a feature or flag is added. static const char* sMinorVersion = "18"; -int PrintVersion() { - std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "." - << sMinorVersion << std::endl; - return 0; +static void PrintVersion() { + std::cerr << StringPrintf("Android Asset Packaging Tool (aapt) %s:%s", sMajorVersion, + sMinorVersion) + << std::endl; } -extern int Compile(const std::vector& args, IDiagnostics* diagnostics); -extern int Link(const std::vector& args, IDiagnostics* diagnostics); -extern int Dump(const std::vector& args); -extern int Diff(const std::vector& args); -extern int Optimize(const std::vector& args); +static void PrintUsage() { + std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|version] ..." << std::endl; +} + +extern int Compile(const std::vector& args, IDiagnostics* diagnostics); +extern int Link(const std::vector& args, IDiagnostics* diagnostics); +extern int Dump(const std::vector& args); +extern int Diff(const std::vector& args); +extern int Optimize(const std::vector& args); + +static int ExecuteCommand(const StringPiece& command, const std::vector& args, + IDiagnostics* diagnostics) { + if (command == "compile" || command == "c") { + return Compile(args, diagnostics); + } else if (command == "link" || command == "l") { + return Link(args, diagnostics); + } else if (command == "dump" || command == "d") { + return Dump(args); + } else if (command == "diff") { + return Diff(args); + } else if (command == "optimize") { + return Optimize(args); + } else if (command == "version") { + PrintVersion(); + return 0; + } + diagnostics->Error(DiagMessage() << "unknown command '" << command << "'"); + return -1; +} + +static void RunDaemon(IDiagnostics* diagnostics) { + std::cout << "Ready" << std::endl; + + // Run in daemon mode. Each line of input from stdin is treated as a command line argument + // invocation. This means we need to split the line into a vector of args. + for (std::string line; std::getline(std::cin, line);) { + const util::Tokenizer tokenizer = util::Tokenize(line, file::sPathSep); + auto token_iter = tokenizer.begin(); + if (token_iter == tokenizer.end()) { + diagnostics->Error(DiagMessage() << "no command"); + continue; + } + + const StringPiece command(*token_iter); + if (command == "quit") { + break; + } + + ++token_iter; + + std::vector args; + args.insert(args.end(), token_iter, tokenizer.end()); + ExecuteCommand(command, args, diagnostics); + std::cout << "Done" << std::endl; + } + + std::cout << "Exiting daemon" << std::endl; +} } // namespace aapt int MainImpl(int argc, char** argv) { - if (argc >= 2) { - argv += 1; - argc -= 1; + if (argc < 2) { + std::cerr << "no command specified\n"; + aapt::PrintUsage(); + return -1; + } - std::vector args; - for (int i = 1; i < argc; i++) { - args.push_back(argv[i]); - } + argv += 1; + argc -= 1; - android::StringPiece command(argv[0]); - if (command == "compile" || command == "c") { - aapt::StdErrDiagnostics diagnostics; - return aapt::Compile(args, &diagnostics); - } else if (command == "link" || command == "l") { - aapt::StdErrDiagnostics diagnostics; - return aapt::Link(args, &diagnostics); - } else if (command == "dump" || command == "d") { - return aapt::Dump(args); - } else if (command == "diff") { - return aapt::Diff(args); - } else if (command == "optimize") { - return aapt::Optimize(args); - } else if (command == "version") { - return aapt::PrintVersion(); + aapt::StdErrDiagnostics diagnostics; + + // Collect the arguments starting after the program name and command name. + std::vector args; + for (int i = 1; i < argc; i++) { + args.push_back(argv[i]); + } + + const StringPiece command(argv[0]); + if (command != "daemon" && command != "m") { + // Single execution. + const int result = aapt::ExecuteCommand(command, args, &diagnostics); + if (result < 0) { + aapt::PrintUsage(); } - std::cerr << "unknown command '" << command << "'\n"; - } else { - std::cerr << "no command specified\n"; + return result; } - std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|version] ..." << std::endl; - return 1; + aapt::RunDaemon(&diagnostics); + return 0; } int main(int argc, char** argv) { diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h index b6aa410f9421..b26e4fa26de6 100644 --- a/tools/aapt2/util/Files.h +++ b/tools/aapt2/util/Files.h @@ -34,8 +34,10 @@ namespace file { #ifdef _WIN32 constexpr const char sDirSep = '\\'; +constexpr const char sPathSep = ';'; #else constexpr const char sDirSep = '/'; +constexpr const char sPathSep = ':'; #endif enum class FileType { diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h index f89f8bf9dc44..c928458786e4 100644 --- a/tools/aapt2/util/Util.h +++ b/tools/aapt2/util/Util.h @@ -228,6 +228,12 @@ class Tokenizer { public: class iterator { public: + using reference = android::StringPiece&; + using value_type = android::StringPiece; + using difference_type = size_t; + using pointer = android::StringPiece*; + using iterator_category = std::forward_iterator_tag; + iterator(const iterator&) = default; iterator& operator=(const iterator&) = default; @@ -250,9 +256,13 @@ class Tokenizer { Tokenizer(const android::StringPiece& str, char sep); - iterator begin() { return begin_; } + iterator begin() const { + return begin_; + } - iterator end() { return end_; } + iterator end() const { + return end_; + } private: const iterator begin_; -- cgit v1.2.3-59-g8ed1b From 4a4ebe25f40490306e34171957e17d5d7ac3e0b3 Mon Sep 17 00:00:00 2001 From: Izabela Orlowska Date: Thu, 24 Aug 2017 16:19:45 +0100 Subject: AAPT2: better error handling for daemon mode Test: manual Change-Id: Ia1997800ad94f386dbbb4195011395ecee1ba91f --- tools/aapt2/Main.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'tools/aapt2/Main.cpp') diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index e4ca7a463df1..82977cb66421 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -101,10 +101,12 @@ static void RunDaemon(IDiagnostics* diagnostics) { std::vector args; args.insert(args.end(), token_iter, tokenizer.end()); - ExecuteCommand(command, args, diagnostics); - std::cout << "Done" << std::endl; + int ret = ExecuteCommand(command, args, diagnostics); + if (ret != 0) { + std::cerr << "Error" << std::endl; + } + std::cerr << "Done" << std::endl; } - std::cout << "Exiting daemon" << std::endl; } -- cgit v1.2.3-59-g8ed1b From 43f6f77a7220b38743fc429c19aa83332afb52b4 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Thu, 24 Aug 2017 15:17:05 -0700 Subject: AAPT2: Change the daemon mode to be line based Accept a set of arguments separated by newlines. This avoids path separator conflicts with the argument format for passing splits. Test: manual Change-Id: Ia68122cb77b7dde2292a0fd953e79f02996ac01c --- tools/aapt2/Main.cpp | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'tools/aapt2/Main.cpp') diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index 82977cb66421..86f4c42c72a7 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -82,26 +82,32 @@ static int ExecuteCommand(const StringPiece& command, const std::vectorError(DiagMessage() << "no command"); - continue; + // Run in daemon mode. The first line of input is the command. This can be 'quit' which ends + // the daemon mode. Each subsequent line is a single parameter to the command. The end of a + // invocation is signaled by providing an empty line. At any point, an EOF signal or the + // command 'quit' will end the daemon mode. + while (true) { + std::vector raw_args; + for (std::string line; std::getline(std::cin, line) && !line.empty();) { + raw_args.push_back(line); } - const StringPiece command(*token_iter); - if (command == "quit") { + if (!std::cin) { break; } - ++token_iter; + // An empty command does nothing. + if (raw_args.empty()) { + continue; + } + + if (raw_args[0] == "quit") { + break; + } std::vector args; - args.insert(args.end(), token_iter, tokenizer.end()); - int ret = ExecuteCommand(command, args, diagnostics); + args.insert(args.end(), ++raw_args.begin(), raw_args.end()); + int ret = ExecuteCommand(raw_args[0], args, diagnostics); if (ret != 0) { std::cerr << "Error" << std::endl; } -- cgit v1.2.3-59-g8ed1b From 3420eed6991a94cff158fc5069730f6d50377a7e Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Wed, 13 Sep 2017 14:46:00 -0700 Subject: AAPT2: Bump to version 2.19 Test: manual Bug: 65645766 Change-Id: Id8bdb14e498bd79ff642f9ad56ef05ec7a5825af --- tools/aapt2/Main.cpp | 2 +- tools/aapt2/readme.md | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'tools/aapt2/Main.cpp') diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index 86f4c42c72a7..36ab30c7fb6e 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -41,7 +41,7 @@ namespace aapt { static const char* sMajorVersion = "2"; // Update minor version whenever a feature or flag is added. -static const char* sMinorVersion = "18"; +static const char* sMinorVersion = "19"; static void PrintVersion() { std::cerr << StringPrintf("Android Asset Packaging Tool (aapt) %s:%s", sMajorVersion, diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md index 2645d54867de..8368f9d16af8 100644 --- a/tools/aapt2/readme.md +++ b/tools/aapt2/readme.md @@ -2,6 +2,16 @@ ## Version 2.19 - Added navigation resource type. +- Fixed issue with resource deduplication. (bug 64397629) +- Added a daemon mode for issuing commands. This is invoked with `aapt2 daemon`. + Command line arguments are separated by newlines, with an empty line signalling the + end of a command. Sending `EOF (Ctrl+D)` to the daemon will exit. +- Fixed an issue where multiple permissions defined in AndroidManifest.xml would generate + conflicting definitions for the same Java constant in Manifest.java. Changed the implementation + to match that of `aapt`, which will take the last definition as the sole definition. + A warning is logged if such a scenario occurs. (bug 64472942) +- Made improvements to handling of paths on Windows. This should resolve a lot of issues with + Unicode paths. (bug 62336414, 63830502) ## Version 2.18 ### `aapt2 ...` -- cgit v1.2.3-59-g8ed1b From 8780eb6e4918ae24fb1ae74d631042c32e41dc3d Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Tue, 31 Oct 2017 17:44:39 -0700 Subject: AAPT2: Add convert command This command allows a developer to convert their proto APK (generated from the link phase using --proto-format) into a binary APK suitable for use on device. aapt2 convert -o output.apk input.apk Test: manual + make aapt2_tests Change-Id: I10a7c33bb4b57006d01fe00a8bf92f78e04e7e50 --- tools/aapt2/Android.bp | 1 + tools/aapt2/LoadedApk.cpp | 146 +++++++++---- tools/aapt2/LoadedApk.h | 43 +++- tools/aapt2/Main.cpp | 5 +- tools/aapt2/cmd/Convert.cpp | 228 +++++++++++++++++++++ tools/aapt2/cmd/Diff.cpp | 5 +- tools/aapt2/cmd/Dump.cpp | 7 +- tools/aapt2/cmd/Link.cpp | 162 +++++---------- tools/aapt2/cmd/Optimize.cpp | 9 +- tools/aapt2/cmd/Util.cpp | 74 +++---- tools/aapt2/cmd/Util.h | 3 +- tools/aapt2/format/binary/BinaryResourceParser.cpp | 115 +++++------ tools/aapt2/format/binary/BinaryResourceParser.h | 4 +- tools/aapt2/format/binary/TableFlattener.cpp | 16 +- tools/aapt2/format/binary/TableFlattener_test.cpp | 5 +- tools/aapt2/format/proto/ProtoDeserialize.cpp | 35 ++-- tools/aapt2/format/proto/ProtoDeserialize.h | 11 +- tools/aapt2/format/proto/ProtoSerialize_test.cpp | 18 +- .../integration-tests/NamespaceTest/App/Android.mk | 1 - tools/aapt2/link/TableMerger.cpp | 71 ++----- tools/aapt2/link/TableMerger.h | 25 +-- tools/aapt2/link/TableMerger_test.cpp | 27 +-- tools/aapt2/optimize/MultiApkGenerator.cpp | 8 +- tools/aapt2/optimize/MultiApkGenerator_test.cpp | 6 +- tools/aapt2/test/Builders.cpp | 20 +- tools/aapt2/test/Builders.h | 9 +- tools/aapt2/xml/XmlDom.cpp | 54 +++-- tools/aapt2/xml/XmlDom.h | 20 +- tools/aapt2/xml/XmlDom_test.cpp | 3 +- 29 files changed, 702 insertions(+), 429 deletions(-) create mode 100644 tools/aapt2/cmd/Convert.cpp (limited to 'tools/aapt2/Main.cpp') diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index 058504d7520d..ae67f617d3d7 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -16,6 +16,7 @@ toolSources = [ "cmd/Compile.cpp", + "cmd/Convert.cpp", "cmd/Diff.cpp", "cmd/Dump.cpp", "cmd/Link.cpp", diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp index ae32ee965b48..921d8531cbd3 100644 --- a/tools/aapt2/LoadedApk.cpp +++ b/tools/aapt2/LoadedApk.cpp @@ -21,43 +21,139 @@ #include "format/Archive.h" #include "format/binary/TableFlattener.h" #include "format/binary/XmlFlattener.h" +#include "format/proto/ProtoDeserialize.h" #include "io/BigBufferStream.h" #include "io/Util.h" #include "xml/XmlDom.h" -namespace aapt { +using ::aapt::io::IFile; +using ::aapt::io::IFileCollection; +using ::aapt::xml::XmlResource; +using ::android::StringPiece; +using ::std::unique_ptr; -using xml::XmlResource; +namespace aapt { -std::unique_ptr LoadedApk::LoadApkFromPath(IAaptContext* context, - const android::StringPiece& path) { +std::unique_ptr LoadedApk::LoadApkFromPath(const StringPiece& path, IDiagnostics* diag) { Source source(path); std::string error; std::unique_ptr apk = io::ZipFileCollection::Create(path, &error); - if (!apk) { - context->GetDiagnostics()->Error(DiagMessage(source) << error); + if (apk == nullptr) { + diag->Error(DiagMessage(path) << "failed opening zip: " << error); + return {}; + } + + if (apk->FindFile("resources.arsc") != nullptr) { + return LoadBinaryApkFromFileCollection(source, std::move(apk), diag); + } else if (apk->FindFile("resources.pb") != nullptr) { + return LoadProtoApkFromFileCollection(source, std::move(apk), diag); + } + diag->Error(DiagMessage(path) << "no resource table found"); + return {}; +} + +std::unique_ptr LoadedApk::LoadProtoApkFromFileCollection( + const Source& source, unique_ptr collection, IDiagnostics* diag) { + io::IFile* table_file = collection->FindFile(kProtoResourceTablePath); + if (table_file == nullptr) { + diag->Error(DiagMessage(source) << "failed to find " << kProtoResourceTablePath); return {}; } - io::IFile* file = apk->FindFile("resources.arsc"); - if (!file) { - context->GetDiagnostics()->Error(DiagMessage(source) << "no resources.arsc found"); + std::unique_ptr in = table_file->OpenInputStream(); + if (in == nullptr) { + diag->Error(DiagMessage(source) << "failed to open " << kProtoResourceTablePath); return {}; } - std::unique_ptr data = file->OpenAsData(); - if (!data) { - context->GetDiagnostics()->Error(DiagMessage(source) << "could not open resources.arsc"); + pb::ResourceTable pb_table; + io::ZeroCopyInputAdaptor adaptor(in.get()); + if (!pb_table.ParseFromZeroCopyStream(&adaptor)) { + diag->Error(DiagMessage(source) << "failed to read " << kProtoResourceTablePath); return {}; } + std::string error; std::unique_ptr table = util::make_unique(); - BinaryResourceParser parser(context, table.get(), source, data->data(), data->size(), apk.get()); + if (!DeserializeTableFromPb(pb_table, collection.get(), table.get(), &error)) { + diag->Error(DiagMessage(source) + << "failed to deserialize " << kProtoResourceTablePath << ": " << error); + return {}; + } + + io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath); + if (manifest_file == nullptr) { + diag->Error(DiagMessage(source) << "failed to find " << kAndroidManifestPath); + return {}; + } + + std::unique_ptr manifest_in = manifest_file->OpenInputStream(); + if (manifest_in == nullptr) { + diag->Error(DiagMessage(source) << "failed to open " << kAndroidManifestPath); + return {}; + } + + pb::XmlNode pb_node; + io::ZeroCopyInputAdaptor manifest_adaptor(manifest_in.get()); + if (!pb_node.ParseFromZeroCopyStream(&manifest_adaptor)) { + diag->Error(DiagMessage(source) << "failed to read proto " << kAndroidManifestPath); + return {}; + } + + std::unique_ptr manifest = DeserializeXmlResourceFromPb(pb_node, &error); + if (manifest == nullptr) { + diag->Error(DiagMessage(source) + << "failed to deserialize proto " << kAndroidManifestPath << ": " << error); + return {}; + } + return util::make_unique(source, std::move(collection), std::move(table), + std::move(manifest)); +} + +std::unique_ptr LoadedApk::LoadBinaryApkFromFileCollection( + const Source& source, unique_ptr collection, IDiagnostics* diag) { + io::IFile* table_file = collection->FindFile(kApkResourceTablePath); + if (table_file == nullptr) { + diag->Error(DiagMessage(source) << "failed to find " << kApkResourceTablePath); + + return {}; + } + + std::unique_ptr data = table_file->OpenAsData(); + if (data == nullptr) { + diag->Error(DiagMessage(source) << "failed to open " << kApkResourceTablePath); + return {}; + } + + std::unique_ptr table = util::make_unique(); + BinaryResourceParser parser(diag, table.get(), source, data->data(), data->size(), + collection.get()); if (!parser.Parse()) { return {}; } - return util::make_unique(source, std::move(apk), std::move(table)); + io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath); + if (manifest_file == nullptr) { + diag->Error(DiagMessage(source) << "failed to find " << kAndroidManifestPath); + return {}; + } + + std::unique_ptr manifest_data = manifest_file->OpenAsData(); + if (manifest_data == nullptr) { + diag->Error(DiagMessage(source) << "failed to open " << kAndroidManifestPath); + return {}; + } + + std::string error; + std::unique_ptr manifest = + xml::Inflate(manifest_data->data(), manifest_data->size(), &error); + if (manifest == nullptr) { + diag->Error(DiagMessage(source) + << "failed to parse binary " << kAndroidManifestPath << ": " << error); + return {}; + } + return util::make_unique(source, std::move(collection), std::move(table), + std::move(manifest)); } bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options, @@ -148,26 +244,4 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table return true; } -std::unique_ptr LoadedApk::InflateManifest(IAaptContext* context) { - IDiagnostics* diag = context->GetDiagnostics(); - - io::IFile* manifest_file = GetFileCollection()->FindFile("AndroidManifest.xml"); - if (manifest_file == nullptr) { - diag->Error(DiagMessage(source_) << "no AndroidManifest.xml found"); - return {}; - } - - std::unique_ptr manifest_data = manifest_file->OpenAsData(); - if (manifest_data == nullptr) { - diag->Error(DiagMessage(manifest_file->GetSource()) << "could not open AndroidManifest.xml"); - return {}; - } - - std::unique_ptr manifest = - xml::Inflate(manifest_data->data(), manifest_data->size(), diag, manifest_file->GetSource()); - if (manifest == nullptr) { - diag->Error(DiagMessage() << "failed to read binary AndroidManifest.xml"); - } - return manifest; -} } // namespace aapt diff --git a/tools/aapt2/LoadedApk.h b/tools/aapt2/LoadedApk.h index d2dd5cf2bc67..ef97de301ad5 100644 --- a/tools/aapt2/LoadedApk.h +++ b/tools/aapt2/LoadedApk.h @@ -29,20 +29,41 @@ namespace aapt { +constexpr static const char kApkResourceTablePath[] = "resources.arsc"; +constexpr static const char kProtoResourceTablePath[] = "resources.pb"; +constexpr static const char kAndroidManifestPath[] = "AndroidManifest.xml"; + // Info about an APK loaded in memory. class LoadedApk { public: - LoadedApk( - const Source& source, - std::unique_ptr apk, - std::unique_ptr table) - : source_(source), apk_(std::move(apk)), table_(std::move(table)) {} - virtual ~LoadedApk() = default; + // Loads both binary and proto APKs from disk. + static std::unique_ptr LoadApkFromPath(const ::android::StringPiece& path, + IDiagnostics* diag); + + // Loads a proto APK from the given file collection. + static std::unique_ptr LoadProtoApkFromFileCollection( + const Source& source, std::unique_ptr collection, IDiagnostics* diag); + + // Loads a binary APK from the given file collection. + static std::unique_ptr LoadBinaryApkFromFileCollection( + const Source& source, std::unique_ptr collection, IDiagnostics* diag); + + LoadedApk(const Source& source, std::unique_ptr apk, + std::unique_ptr table, std::unique_ptr manifest) + : source_(source), + apk_(std::move(apk)), + table_(std::move(table)), + manifest_(std::move(manifest)) { + } io::IFileCollection* GetFileCollection() { return apk_.get(); } + const ResourceTable* GetResourceTable() const { + return table_.get(); + } + ResourceTable* GetResourceTable() { return table_.get(); } @@ -51,6 +72,10 @@ class LoadedApk { return source_; } + const xml::XmlResource* GetManifest() const { + return manifest_.get(); + } + /** * Writes the APK on disk at the given path, while also removing the resource * files that are not referenced in the resource table. @@ -71,11 +96,6 @@ class LoadedApk { const TableFlattenerOptions& options, FilterChain* filters, IArchiveWriter* writer, xml::XmlResource* manifest = nullptr); - /** Inflates the AndroidManifest.xml file from the APK. */ - std::unique_ptr InflateManifest(IAaptContext* context); - - static std::unique_ptr LoadApkFromPath(IAaptContext* context, - const android::StringPiece& path); private: DISALLOW_COPY_AND_ASSIGN(LoadedApk); @@ -83,6 +103,7 @@ class LoadedApk { Source source_; std::unique_ptr apk_; std::unique_ptr table_; + std::unique_ptr manifest_; }; } // namespace aapt diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index 36ab30c7fb6e..808b29cfd844 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -50,7 +50,7 @@ static void PrintVersion() { } static void PrintUsage() { - std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|version] ..." << std::endl; + std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|convert|version] ..." << std::endl; } extern int Compile(const std::vector& args, IDiagnostics* diagnostics); @@ -58,6 +58,7 @@ extern int Link(const std::vector& args, IDiagnostics* diagnostics) extern int Dump(const std::vector& args); extern int Diff(const std::vector& args); extern int Optimize(const std::vector& args); +extern int Convert(const std::vector& args); static int ExecuteCommand(const StringPiece& command, const std::vector& args, IDiagnostics* diagnostics) { @@ -71,6 +72,8 @@ static int ExecuteCommand(const StringPiece& command, const std::vector + +#include "android-base/macros.h" +#include "androidfw/StringPiece.h" + +#include "Flags.h" +#include "LoadedApk.h" +#include "ValueVisitor.h" +#include "cmd/Util.h" +#include "format/binary/TableFlattener.h" +#include "format/binary/XmlFlattener.h" +#include "format/proto/ProtoDeserialize.h" +#include "io/BigBufferStream.h" +#include "io/Util.h" +#include "process/IResourceTableConsumer.h" +#include "process/SymbolTable.h" + +using ::android::StringPiece; +using ::std::unique_ptr; +using ::std::vector; + +namespace aapt { + +static bool FlattenXml(IAaptContext* context, const xml::XmlResource& xml, + const std::string& entry_path, bool utf16, IArchiveWriter* writer) { + BigBuffer buffer(4096); + XmlFlattenerOptions options = {}; + options.use_utf16 = utf16; + XmlFlattener flattener(&buffer, options); + if (!flattener.Consume(context, &xml)) { + return false; + } + io::BigBufferInputStream input_stream(&buffer); + return io::CopyInputStreamToArchive(context, &input_stream, entry_path, ArchiveEntry::kCompress, + writer); +} + +bool ConvertProtoApkToBinaryApk(IAaptContext* context, unique_ptr apk, + const TableFlattenerOptions& options, IArchiveWriter* writer) { + if (!FlattenXml(context, *apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, writer)) { + return false; + } + + BigBuffer buffer(4096); + TableFlattener table_flattener(options, &buffer); + if (!table_flattener.Consume(context, apk->GetResourceTable())) { + return false; + } + + io::BigBufferInputStream input_stream(&buffer); + if (!io::CopyInputStreamToArchive(context, &input_stream, kApkResourceTablePath, + ArchiveEntry::kAlign, writer)) { + return false; + } + + for (const auto& package : apk->GetResourceTable()->packages) { + for (const auto& type : package->types) { + for (const auto& entry : type->entries) { + for (const auto& config_value : entry->values) { + const FileReference* file = ValueCast(config_value->value.get()); + if (file != nullptr) { + if (file->file == nullptr) { + context->GetDiagnostics()->Warn(DiagMessage(apk->GetSource()) + << "no file associated with " << *file); + return false; + } + + if (file->type == ResourceFile::Type::kProtoXml) { + unique_ptr in = file->file->OpenInputStream(); + if (in == nullptr) { + context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) + << "failed to open file " << *file->path); + return false; + } + + pb::XmlNode pb_node; + io::ZeroCopyInputAdaptor adaptor(in.get()); + if (!pb_node.ParseFromZeroCopyStream(&adaptor)) { + context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) + << "failed to parse proto XML " << *file->path); + return false; + } + + std::string error; + unique_ptr xml = DeserializeXmlResourceFromPb(pb_node, &error); + if (xml == nullptr) { + context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) + << "failed to deserialize proto XML " + << *file->path << ": " << error); + return false; + } + + if (!FlattenXml(context, *xml, *file->path, false /*utf16*/, writer)) { + context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) + << "failed to serialize XML " << *file->path); + return false; + } + } else { + if (!io::CopyFileToArchive(context, file->file, *file->path, + file->file->WasCompressed() ? ArchiveEntry::kCompress : 0u, + writer)) { + context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) + << "failed to copy file " << *file->path); + return false; + } + } + + } // file + } // config_value + } // entry + } // type + } // package + return true; +} + +class Context : public IAaptContext { + public: + Context() : mangler_({}), symbols_(&mangler_) { + } + + PackageType GetPackageType() override { + return PackageType::kApp; + } + + SymbolTable* GetExternalSymbols() override { + return &symbols_; + } + + IDiagnostics* GetDiagnostics() override { + return &diag_; + } + + const std::string& GetCompilationPackage() override { + return package_; + } + + uint8_t GetPackageId() override { + // Nothing should call this. + UNIMPLEMENTED(FATAL) << "PackageID should not be necessary"; + return 0; + } + + NameMangler* GetNameMangler() override { + UNIMPLEMENTED(FATAL); + return nullptr; + } + + bool IsVerbose() override { + return verbose_; + } + + int GetMinSdkVersion() override { + return 0u; + } + + bool verbose_ = false; + std::string package_; + + private: + DISALLOW_COPY_AND_ASSIGN(Context); + + NameMangler mangler_; + SymbolTable symbols_; + StdErrDiagnostics diag_; +}; + +int Convert(const vector& args) { + Context context; + std::string output_path; + TableFlattenerOptions options; + Flags flags = + Flags() + .RequiredFlag("-o", "Output path", &output_path) + .OptionalSwitch("--enable-sparse-encoding", + "Enables encoding sparse entries using a binary search tree.\n" + "This decreases APK size at the cost of resource retrieval performance.", + &options.use_sparse_entries) + .OptionalSwitch("-v", "Enables verbose logging", &context.verbose_); + if (!flags.Parse("aapt2 convert", args, &std::cerr)) { + return 1; + } + + if (flags.GetArgs().size() != 1) { + std::cerr << "must supply a single proto APK\n"; + flags.Usage("aapt2 convert", &std::cerr); + return 1; + } + + const StringPiece& path = flags.GetArgs()[0]; + unique_ptr apk = LoadedApk::LoadApkFromPath(path, context.GetDiagnostics()); + if (apk == nullptr) { + context.GetDiagnostics()->Error(DiagMessage(path) << "failed to load APK"); + return 1; + } + + Maybe app_info = + ExtractAppInfoFromBinaryManifest(*apk->GetManifest(), context.GetDiagnostics()); + if (!app_info) { + return 1; + } + + context.package_ = app_info.value().package; + + unique_ptr writer = + CreateZipFileArchiveWriter(context.GetDiagnostics(), output_path); + if (writer == nullptr) { + return 1; + } + return ConvertProtoApkToBinaryApk(&context, std::move(apk), options, writer.get()) ? 0 : 1; +} + +} // namespace aapt diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp index 625c47cec939..fc1f1d6342ae 100644 --- a/tools/aapt2/cmd/Diff.cpp +++ b/tools/aapt2/cmd/Diff.cpp @@ -357,8 +357,9 @@ int Diff(const std::vector& args) { return 1; } - std::unique_ptr apk_a = LoadedApk::LoadApkFromPath(&context, flags.GetArgs()[0]); - std::unique_ptr apk_b = LoadedApk::LoadApkFromPath(&context, flags.GetArgs()[1]); + IDiagnostics* diag = context.GetDiagnostics(); + std::unique_ptr apk_a = LoadedApk::LoadApkFromPath(flags.GetArgs()[0], diag); + std::unique_ptr apk_b = LoadedApk::LoadApkFromPath(flags.GetArgs()[1], diag); if (!apk_a || !apk_b) { return 1; } diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp index 090c3fbc5731..bc8f1dcb2289 100644 --- a/tools/aapt2/cmd/Dump.cpp +++ b/tools/aapt2/cmd/Dump.cpp @@ -78,7 +78,7 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path) { return false; } - if (!DeserializeTableFromPb(pb_table, &table, &err)) { + if (!DeserializeTableFromPb(pb_table, zip.get(), &table, &err)) { context->GetDiagnostics()->Error(DiagMessage(file_path) << "failed to parse table: " << err); return false; @@ -90,7 +90,8 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path) { return false; } - BinaryResourceParser parser(context, &table, Source(file_path), data->data(), data->size()); + BinaryResourceParser parser(context->GetDiagnostics(), &table, Source(file_path), + data->data(), data->size()); if (!parser.Parse()) { return false; } @@ -129,7 +130,7 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path) { ResourceTable table; err.clear(); - if (!DeserializeTableFromPb(pb_table, &table, &err)) { + if (!DeserializeTableFromPb(pb_table, nullptr /*files*/, &table, &err)) { context->GetDiagnostics()->Error(DiagMessage(file_path) << "failed to parse table: " << err); continue; diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index 55a4c438755c..26edb762fe63 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -29,6 +29,7 @@ #include "AppInfo.h" #include "Debug.h" #include "Flags.h" +#include "LoadedApk.h" #include "Locale.h" #include "NameMangler.h" #include "ResourceUtils.h" @@ -39,7 +40,6 @@ #include "filter/ConfigFilter.h" #include "format/Archive.h" #include "format/Container.h" -#include "format/binary/BinaryResourceParser.h" #include "format/binary/TableFlattener.h" #include "format/binary/XmlFlattener.h" #include "format/proto/ProtoDeserialize.h" @@ -71,9 +71,6 @@ using ::android::base::StringPrintf; namespace aapt { -constexpr static const char kApkResourceTablePath[] = "resources.arsc"; -constexpr static const char kProtoResourceTablePath[] = "resources.pb"; - enum class OutputFormat { kApk, kProto, @@ -297,23 +294,6 @@ static bool FlattenXml(IAaptContext* context, const xml::XmlResource& xml_res, return false; } -static std::unique_ptr LoadTableFromPb(const Source& source, const void* data, - size_t len, IDiagnostics* diag) { - pb::ResourceTable pb_table; - if (!pb_table.ParseFromArray(data, len)) { - diag->Error(DiagMessage(source) << "invalid compiled table"); - return {}; - } - - std::unique_ptr table = util::make_unique(); - std::string error; - if (!DeserializeTableFromPb(pb_table, table.get(), &error)) { - diag->Error(DiagMessage(source) << "invalid compiled table: " << error); - return {}; - } - return table; -} - // Inflates an XML file from the source path. static std::unique_ptr LoadXml(const std::string& path, IDiagnostics* diag) { FileInputStream fin(path); @@ -584,7 +564,7 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv pb::XmlNode pb_xml_node; if (!pb_xml_node.ParseFromArray(data->data(), static_cast(data->size()))) { context_->GetDiagnostics()->Error(DiagMessage(file->GetSource()) - << "failed to parse proto xml"); + << "failed to parse proto XML"); return false; } @@ -592,13 +572,15 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv file_op.xml_to_flatten = DeserializeXmlResourceFromPb(pb_xml_node, &error); if (file_op.xml_to_flatten == nullptr) { context_->GetDiagnostics()->Error(DiagMessage(file->GetSource()) - << "failed to deserialize proto xml: " << error); + << "failed to deserialize proto XML: " << error); return false; } } else { - file_op.xml_to_flatten = xml::Inflate(data->data(), data->size(), - context_->GetDiagnostics(), file->GetSource()); + std::string error_str; + file_op.xml_to_flatten = xml::Inflate(data->data(), data->size(), &error_str); if (file_op.xml_to_flatten == nullptr) { + context_->GetDiagnostics()->Error(DiagMessage(file->GetSource()) + << "failed to parse binary XML: " << error_str); return false; } } @@ -745,22 +727,29 @@ class LinkCommand { file_collection_(util::make_unique()) { } - /** - * Creates a SymbolTable that loads symbols from the various APKs and caches - * the results for faster lookup. - */ + // Creates a SymbolTable that loads symbols from the various APKs. bool LoadSymbolsFromIncludePaths() { - std::unique_ptr asset_source = - util::make_unique(); + auto asset_source = util::make_unique(); for (const std::string& path : options_.include_paths) { if (context_->IsVerbose()) { context_->GetDiagnostics()->Note(DiagMessage() << "including " << path); } - // First try to load the file as a static lib. - std::string error_str; - std::unique_ptr include_static = LoadStaticLibrary(path, &error_str); - if (include_static) { + std::string error; + auto zip_collection = io::ZipFileCollection::Create(path, &error); + if (zip_collection == nullptr) { + context_->GetDiagnostics()->Error(DiagMessage() << "failed to open APK: " << error); + return false; + } + + if (zip_collection->FindFile(kProtoResourceTablePath) != nullptr) { + // Load this as a static library include. + std::unique_ptr static_apk = LoadedApk::LoadProtoApkFromFileCollection( + Source(path), std::move(zip_collection), context_->GetDiagnostics()); + if (static_apk == nullptr) { + return false; + } + if (context_->GetPackageType() != PackageType::kStaticLib) { // Can't include static libraries when not building a static library (they have no IDs // assigned). @@ -769,13 +758,15 @@ class LinkCommand { return false; } - // If we are using --no-static-lib-packages, we need to rename the - // package of this table to our compilation package. + ResourceTable* table = static_apk->GetResourceTable(); + + // If we are using --no-static-lib-packages, we need to rename the package of this table to + // our compilation package. if (options_.no_static_lib_packages) { // Since package names can differ, and multiple packages can exist in a ResourceTable, // we place the requirement that all static libraries are built with the package // ID 0x7f. So if one is not found, this is an error. - if (ResourceTablePackage* pkg = include_static->FindPackageById(kAppPackageId)) { + if (ResourceTablePackage* pkg = table->FindPackageById(kAppPackageId)) { pkg->name = context_->GetCompilationPackage(); } else { context_->GetDiagnostics()->Error(DiagMessage(path) @@ -785,19 +776,14 @@ class LinkCommand { } context_->GetExternalSymbols()->AppendSource( - util::make_unique(include_static.get())); - - static_table_includes_.push_back(std::move(include_static)); - - } else if (!error_str.empty()) { - // We had an error with reading, so fail. - context_->GetDiagnostics()->Error(DiagMessage(path) << error_str); - return false; - } - - if (!asset_source->AddAssetPath(path)) { - context_->GetDiagnostics()->Error(DiagMessage(path) << "failed to load include path"); - return false; + util::make_unique(table)); + static_library_includes_.push_back(std::move(static_apk)); + } else { + if (!asset_source->AddAssetPath(path)) { + context_->GetDiagnostics()->Error(DiagMessage() + << "failed to load include path " << path); + return false; + } } } @@ -1191,46 +1177,18 @@ class LinkCommand { return true; } - std::unique_ptr LoadStaticLibrary(const std::string& input, - std::string* out_error) { - std::unique_ptr collection = - io::ZipFileCollection::Create(input, out_error); - if (!collection) { - return {}; - } - return LoadTablePbFromCollection(collection.get()); - } - - std::unique_ptr LoadTablePbFromCollection(io::IFileCollection* collection) { - io::IFile* file = collection->FindFile(kProtoResourceTablePath); - if (!file) { - return {}; - } - - std::unique_ptr data = file->OpenAsData(); - return LoadTableFromPb(file->GetSource(), data->data(), data->size(), - context_->GetDiagnostics()); - } - bool MergeStaticLibrary(const std::string& input, bool override) { if (context_->IsVerbose()) { context_->GetDiagnostics()->Note(DiagMessage() << "merging static library " << input); } - std::string error_str; - std::unique_ptr collection = - io::ZipFileCollection::Create(input, &error_str); - if (!collection) { - context_->GetDiagnostics()->Error(DiagMessage(input) << error_str); - return false; - } - - std::unique_ptr table = LoadTablePbFromCollection(collection.get()); - if (!table) { + std::unique_ptr apk = LoadedApk::LoadApkFromPath(input, context_->GetDiagnostics()); + if (apk == nullptr) { context_->GetDiagnostics()->Error(DiagMessage(input) << "invalid static library"); return false; } + ResourceTable* table = apk->GetResourceTable(); ResourceTablePackage* pkg = table->FindPackageById(kAppPackageId); if (!pkg) { context_->GetDiagnostics()->Error(DiagMessage(input) << "static library has no package"); @@ -1251,13 +1209,12 @@ class LinkCommand { // Clear the package name, so as to make the resources look like they are coming from the // local package. pkg->name = ""; - result = table_merger_->Merge(Source(input), table.get(), override, collection.get()); + result = table_merger_->Merge(Source(input), table, override); } else { // This is the proper way to merge libraries, where the package name is // preserved and resource names are mangled. - result = - table_merger_->MergeAndMangle(Source(input), pkg->name, table.get(), collection.get()); + result = table_merger_->MergeAndMangle(Source(input), pkg->name, table); } if (!result) { @@ -1265,31 +1222,10 @@ class LinkCommand { } // Make sure to move the collection into the set of IFileCollections. - collections_.push_back(std::move(collection)); + merged_apks_.push_back(std::move(apk)); return true; } - bool MergeResourceTable(io::IFile* file, bool override) { - if (context_->IsVerbose()) { - context_->GetDiagnostics()->Note(DiagMessage() << "merging resource table " - << file->GetSource()); - } - - std::unique_ptr data = file->OpenAsData(); - if (!data) { - context_->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << "failed to open file"); - return false; - } - - std::unique_ptr table = - LoadTableFromPb(file->GetSource(), data->data(), data->size(), context_->GetDiagnostics()); - if (!table) { - return false; - } - - return table_merger_->Merge(file->GetSource(), table.get(), override); - } - bool MergeCompiledFile(const ResourceFile& compiled_file, io::IFile* file, bool override) { if (context_->IsVerbose()) { context_->GetDiagnostics()->Note(DiagMessage() @@ -1420,7 +1356,7 @@ class LinkCommand { ResourceTable table; std::string error; - if (!DeserializeTableFromPb(pb_table, &table, &error)) { + if (!DeserializeTableFromPb(pb_table, nullptr /*files*/, &table, &error)) { context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to deserialize resource table: " << error); return false; @@ -1866,13 +1802,15 @@ class LinkCommand { // A pointer to the FileCollection representing the filesystem (not archives). std::unique_ptr file_collection_; - // A vector of IFileCollections. This is mainly here to keep ownership of the + // A vector of IFileCollections. This is mainly here to retain ownership of the // collections. std::vector> collections_; - // A vector of ResourceTables. This is here to retain ownership, so that the - // SymbolTable can use these. - std::vector> static_table_includes_; + // The set of merged APKs. This is mainly here to retain ownership of the APKs. + std::vector> merged_apks_; + + // The set of included APKs (not merged). This is mainly here to retain ownership of the APKs. + std::vector> static_library_includes_; // The set of shared libraries being used, mapping their assigned package ID to package name. std::map shared_libs_; diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp index 44e148ee9732..688b6a7c3b3c 100644 --- a/tools/aapt2/cmd/Optimize.cpp +++ b/tools/aapt2/cmd/Optimize.cpp @@ -281,15 +281,14 @@ class OptimizeCommand { OptimizeContext* context_; }; -bool ExtractAppDataFromManifest(OptimizeContext* context, LoadedApk* apk, +bool ExtractAppDataFromManifest(OptimizeContext* context, const LoadedApk* apk, OptimizeOptions* out_options) { - std::unique_ptr manifest = apk->InflateManifest(context); + const xml::XmlResource* manifest = apk->GetManifest(); if (manifest == nullptr) { return false; } - Maybe app_info = - ExtractAppInfoFromBinaryManifest(manifest.get(), context->GetDiagnostics()); + Maybe app_info = ExtractAppInfoFromBinaryManifest(*manifest, context->GetDiagnostics()); if (!app_info) { context->GetDiagnostics()->Error(DiagMessage() << "failed to extract data from AndroidManifest.xml"); @@ -355,7 +354,7 @@ int Optimize(const std::vector& args) { } const std::string& apk_path = flags.GetArgs()[0]; - std::unique_ptr apk = LoadedApk::LoadApkFromPath(&context, apk_path); + std::unique_ptr apk = LoadedApk::LoadApkFromPath(apk_path, context.GetDiagnostics()); if (!apk) { return 1; } diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp index 708bed80555b..d39f43e8bf08 100644 --- a/tools/aapt2/cmd/Util.cpp +++ b/tools/aapt2/cmd/Util.cpp @@ -214,9 +214,10 @@ std::unique_ptr GenerateSplitManifest(const AppInfo& app_info, return doc; } -static Maybe ExtractCompiledString(xml::Attribute* attr, std::string* out_error) { - if (attr->compiled_value != nullptr) { - String* compiled_str = ValueCast(attr->compiled_value.get()); +static Maybe ExtractCompiledString(const xml::Attribute& attr, + std::string* out_error) { + if (attr.compiled_value != nullptr) { + const String* compiled_str = ValueCast(attr.compiled_value.get()); if (compiled_str != nullptr) { if (!compiled_str->value->empty()) { return *compiled_str->value; @@ -230,16 +231,16 @@ static Maybe ExtractCompiledString(xml::Attribute* attr, std::strin } // Fallback to the plain text value if there is one. - if (!attr->value.empty()) { - return attr->value; + if (!attr.value.empty()) { + return attr.value; } *out_error = "value is an empty string"; return {}; } -static Maybe ExtractCompiledInt(xml::Attribute* attr, std::string* out_error) { - if (attr->compiled_value != nullptr) { - BinaryPrimitive* compiled_prim = ValueCast(attr->compiled_value.get()); +static Maybe ExtractCompiledInt(const xml::Attribute& attr, std::string* out_error) { + if (attr.compiled_value != nullptr) { + const BinaryPrimitive* compiled_prim = ValueCast(attr.compiled_value.get()); if (compiled_prim != nullptr) { if (compiled_prim->value.dataType >= android::Res_value::TYPE_FIRST_INT && compiled_prim->value.dataType <= android::Res_value::TYPE_LAST_INT) { @@ -251,19 +252,19 @@ static Maybe ExtractCompiledInt(xml::Attribute* attr, std::string* out } // Fallback to the plain text value if there is one. - Maybe integer = ResourceUtils::ParseInt(attr->value); + Maybe integer = ResourceUtils::ParseInt(attr.value); if (integer) { return integer; } std::stringstream error_msg; - error_msg << "'" << attr->value << "' is not a valid integer"; + error_msg << "'" << attr.value << "' is not a valid integer"; *out_error = error_msg.str(); return {}; } -static Maybe ExtractSdkVersion(xml::Attribute* attr, std::string* out_error) { - if (attr->compiled_value != nullptr) { - BinaryPrimitive* compiled_prim = ValueCast(attr->compiled_value.get()); +static Maybe ExtractSdkVersion(const xml::Attribute& attr, std::string* out_error) { + if (attr.compiled_value != nullptr) { + const BinaryPrimitive* compiled_prim = ValueCast(attr.compiled_value.get()); if (compiled_prim != nullptr) { if (compiled_prim->value.dataType >= android::Res_value::TYPE_FIRST_INT && compiled_prim->value.dataType <= android::Res_value::TYPE_LAST_INT) { @@ -273,7 +274,7 @@ static Maybe ExtractSdkVersion(xml::Attribute* attr, std::string* out_error return {}; } - String* compiled_str = ValueCast(attr->compiled_value.get()); + const String* compiled_str = ValueCast(attr.compiled_value.get()); if (compiled_str != nullptr) { Maybe sdk_version = ResourceUtils::ParseSdkVersion(*compiled_str->value); if (sdk_version) { @@ -288,19 +289,20 @@ static Maybe ExtractSdkVersion(xml::Attribute* attr, std::string* out_error } // Fallback to the plain text value if there is one. - Maybe sdk_version = ResourceUtils::ParseSdkVersion(attr->value); + Maybe sdk_version = ResourceUtils::ParseSdkVersion(attr.value); if (sdk_version) { return sdk_version; } std::stringstream error_msg; - error_msg << "'" << attr->value << "' is not a valid SDK version"; + error_msg << "'" << attr.value << "' is not a valid SDK version"; *out_error = error_msg.str(); return {}; } -Maybe ExtractAppInfoFromBinaryManifest(xml::XmlResource* xml_res, IDiagnostics* diag) { +Maybe ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res, + IDiagnostics* diag) { // Make sure the first element is with package attribute. - xml::Element* manifest_el = xml_res->root.get(); + const xml::Element* manifest_el = xml_res.root.get(); if (manifest_el == nullptr) { return {}; } @@ -308,63 +310,63 @@ Maybe ExtractAppInfoFromBinaryManifest(xml::XmlResource* xml_res, IDiag AppInfo app_info; if (!manifest_el->namespace_uri.empty() || manifest_el->name != "manifest") { - diag->Error(DiagMessage(xml_res->file.source) << "root tag must be "); + diag->Error(DiagMessage(xml_res.file.source) << "root tag must be "); return {}; } - xml::Attribute* package_attr = manifest_el->FindAttribute({}, "package"); + const xml::Attribute* package_attr = manifest_el->FindAttribute({}, "package"); if (!package_attr) { - diag->Error(DiagMessage(xml_res->file.source) << " must have a 'package' attribute"); + diag->Error(DiagMessage(xml_res.file.source) << " must have a 'package' attribute"); return {}; } std::string error_msg; - Maybe maybe_package = ExtractCompiledString(package_attr, &error_msg); + Maybe maybe_package = ExtractCompiledString(*package_attr, &error_msg); if (!maybe_package) { - diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number)) + diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number)) << "invalid package name: " << error_msg); return {}; } app_info.package = maybe_package.value(); - if (xml::Attribute* version_code_attr = + if (const xml::Attribute* version_code_attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) { - Maybe maybe_code = ExtractCompiledInt(version_code_attr, &error_msg); + Maybe maybe_code = ExtractCompiledInt(*version_code_attr, &error_msg); if (!maybe_code) { - diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number)) + diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number)) << "invalid android:versionCode: " << error_msg); return {}; } app_info.version_code = maybe_code.value(); } - if (xml::Attribute* revision_code_attr = + if (const xml::Attribute* revision_code_attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) { - Maybe maybe_code = ExtractCompiledInt(revision_code_attr, &error_msg); + Maybe maybe_code = ExtractCompiledInt(*revision_code_attr, &error_msg); if (!maybe_code) { - diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number)) + diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number)) << "invalid android:revisionCode: " << error_msg); return {}; } app_info.revision_code = maybe_code.value(); } - if (xml::Attribute* split_name_attr = manifest_el->FindAttribute({}, "split")) { - Maybe maybe_split_name = ExtractCompiledString(split_name_attr, &error_msg); + if (const xml::Attribute* split_name_attr = manifest_el->FindAttribute({}, "split")) { + Maybe maybe_split_name = ExtractCompiledString(*split_name_attr, &error_msg); if (!maybe_split_name) { - diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number)) + diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number)) << "invalid split name: " << error_msg); return {}; } app_info.split_name = maybe_split_name.value(); } - if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) { - if (xml::Attribute* min_sdk = + if (const xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) { + if (const xml::Attribute* min_sdk = uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) { - Maybe maybe_sdk = ExtractSdkVersion(min_sdk, &error_msg); + Maybe maybe_sdk = ExtractSdkVersion(*min_sdk, &error_msg); if (!maybe_sdk) { - diag->Error(DiagMessage(xml_res->file.source.WithLine(uses_sdk_el->line_number)) + diag->Error(DiagMessage(xml_res.file.source.WithLine(uses_sdk_el->line_number)) << "invalid android:minSdkVersion: " << error_msg); return {}; } diff --git a/tools/aapt2/cmd/Util.h b/tools/aapt2/cmd/Util.h index fd9b39c67380..7611c1526104 100644 --- a/tools/aapt2/cmd/Util.h +++ b/tools/aapt2/cmd/Util.h @@ -57,7 +57,8 @@ std::unique_ptr GenerateSplitManifest(const AppInfo& app_info, const SplitConstraints& constraints); // Extracts relevant info from the AndroidManifest.xml. -Maybe ExtractAppInfoFromBinaryManifest(xml::XmlResource* xml_res, IDiagnostics* diag); +Maybe ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res, + IDiagnostics* diag); } // namespace aapt diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp index 95eec4ae8248..66510b0e3a9b 100644 --- a/tools/aapt2/format/binary/BinaryResourceParser.cpp +++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp @@ -73,29 +73,22 @@ class ReferenceIdToNameVisitor : public DescendingValueVisitor { } // namespace -BinaryResourceParser::BinaryResourceParser(IAaptContext* context, ResourceTable* table, +BinaryResourceParser::BinaryResourceParser(IDiagnostics* diag, ResourceTable* table, const Source& source, const void* data, size_t len, io::IFileCollection* files) - : context_(context), - table_(table), - source_(source), - data_(data), - data_len_(len), - files_(files) { + : diag_(diag), table_(table), source_(source), data_(data), data_len_(len), files_(files) { } bool BinaryResourceParser::Parse() { ResChunkPullParser parser(data_, data_len_); if (!ResChunkPullParser::IsGoodEvent(parser.Next())) { - context_->GetDiagnostics()->Error(DiagMessage(source_) - << "corrupt resources.arsc: " << parser.error()); + diag_->Error(DiagMessage(source_) << "corrupt resources.arsc: " << parser.error()); return false; } if (parser.chunk()->type != android::RES_TABLE_TYPE) { - context_->GetDiagnostics()->Error(DiagMessage(source_) - << StringPrintf("unknown chunk of type 0x%02x", + diag_->Error(DiagMessage(source_) << StringPrintf("unknown chunk of type 0x%02x", static_cast(parser.chunk()->type))); return false; } @@ -106,13 +99,12 @@ bool BinaryResourceParser::Parse() { if (parser.Next() != ResChunkPullParser::Event::kEndDocument) { if (parser.event() == ResChunkPullParser::Event::kBadDocument) { - context_->GetDiagnostics()->Warn( - DiagMessage(source_) << "invalid chunk trailing RES_TABLE_TYPE: " << parser.error()); + diag_->Warn(DiagMessage(source_) + << "invalid chunk trailing RES_TABLE_TYPE: " << parser.error()); } else { - context_->GetDiagnostics()->Warn( - DiagMessage(source_) << StringPrintf( - "unexpected chunk of type 0x%02x trailing RES_TABLE_TYPE", - static_cast(parser.chunk()->type))); + diag_->Warn(DiagMessage(source_) + << StringPrintf("unexpected chunk of type 0x%02x trailing RES_TABLE_TYPE", + static_cast(parser.chunk()->type))); } } return true; @@ -122,7 +114,7 @@ bool BinaryResourceParser::Parse() { bool BinaryResourceParser::ParseTable(const ResChunk_header* chunk) { const ResTable_header* table_header = ConvertTo(chunk); if (!table_header) { - context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_header chunk"); + diag_->Error(DiagMessage(source_) << "corrupt ResTable_header chunk"); return false; } @@ -135,17 +127,15 @@ bool BinaryResourceParser::ParseTable(const ResChunk_header* chunk) { status_t err = value_pool_.setTo(parser.chunk(), util::DeviceToHost32(parser.chunk()->size)); if (err != NO_ERROR) { - context_->GetDiagnostics()->Error(DiagMessage(source_) - << "corrupt string pool in ResTable: " - << value_pool_.getError()); + diag_->Error(DiagMessage(source_) + << "corrupt string pool in ResTable: " << value_pool_.getError()); return false; } // Reserve some space for the strings we are going to add. table_->string_pool.HintWillAdd(value_pool_.size(), value_pool_.styleCount()); } else { - context_->GetDiagnostics()->Warn(DiagMessage(source_) - << "unexpected string pool in ResTable"); + diag_->Warn(DiagMessage(source_) << "unexpected string pool in ResTable"); } break; @@ -156,16 +146,15 @@ bool BinaryResourceParser::ParseTable(const ResChunk_header* chunk) { break; default: - context_->GetDiagnostics()->Warn( - DiagMessage(source_) << "unexpected chunk type " - << static_cast(util::DeviceToHost16(parser.chunk()->type))); + diag_->Warn(DiagMessage(source_) + << "unexpected chunk type " + << static_cast(util::DeviceToHost16(parser.chunk()->type))); break; } } if (parser.event() == ResChunkPullParser::Event::kBadDocument) { - context_->GetDiagnostics()->Error(DiagMessage(source_) - << "corrupt resource table: " << parser.error()); + diag_->Error(DiagMessage(source_) << "corrupt resource table: " << parser.error()); return false; } return true; @@ -176,14 +165,13 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) { sizeof(ResTable_package) - sizeof(ResTable_package::typeIdOffset); const ResTable_package* package_header = ConvertTo(chunk); if (!package_header) { - context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_package chunk"); + diag_->Error(DiagMessage(source_) << "corrupt ResTable_package chunk"); return false; } uint32_t package_id = util::DeviceToHost32(package_header->id); if (package_id > std::numeric_limits::max()) { - context_->GetDiagnostics()->Error(DiagMessage(source_) - << "package ID is too big (" << package_id << ")"); + diag_->Error(DiagMessage(source_) << "package ID is too big (" << package_id << ")"); return false; } @@ -198,9 +186,8 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) { ResourceTablePackage* package = table_->CreatePackage(util::Utf16ToUtf8(package_name), static_cast(package_id)); if (!package) { - context_->GetDiagnostics()->Error(DiagMessage(source_) - << "incompatible package '" << package_name << "' with ID " - << package_id); + diag_->Error(DiagMessage(source_) + << "incompatible package '" << package_name << "' with ID " << package_id); return false; } @@ -218,8 +205,7 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) { status_t err = type_pool_.setTo(parser.chunk(), util::DeviceToHost32(parser.chunk()->size)); if (err != NO_ERROR) { - context_->GetDiagnostics()->Error(DiagMessage(source_) - << "corrupt type string pool in " + diag_->Error(DiagMessage(source_) << "corrupt type string pool in " << "ResTable_package: " << type_pool_.getError()); return false; } @@ -227,13 +213,12 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) { status_t err = key_pool_.setTo(parser.chunk(), util::DeviceToHost32(parser.chunk()->size)); if (err != NO_ERROR) { - context_->GetDiagnostics()->Error(DiagMessage(source_) - << "corrupt key string pool in " + diag_->Error(DiagMessage(source_) << "corrupt key string pool in " << "ResTable_package: " << key_pool_.getError()); return false; } } else { - context_->GetDiagnostics()->Warn(DiagMessage(source_) << "unexpected string pool"); + diag_->Warn(DiagMessage(source_) << "unexpected string pool"); } break; @@ -256,16 +241,15 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) { break; default: - context_->GetDiagnostics()->Warn( - DiagMessage(source_) << "unexpected chunk type " - << static_cast(util::DeviceToHost16(parser.chunk()->type))); + diag_->Warn(DiagMessage(source_) + << "unexpected chunk type " + << static_cast(util::DeviceToHost16(parser.chunk()->type))); break; } } if (parser.event() == ResChunkPullParser::Event::kBadDocument) { - context_->GetDiagnostics()->Error(DiagMessage(source_) - << "corrupt ResTable_package: " << parser.error()); + diag_->Error(DiagMessage(source_) << "corrupt ResTable_package: " << parser.error()); return false; } @@ -278,19 +262,18 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) { bool BinaryResourceParser::ParseTypeSpec(const ResChunk_header* chunk) { if (type_pool_.getError() != NO_ERROR) { - context_->GetDiagnostics()->Error(DiagMessage(source_) << "missing type string pool"); + diag_->Error(DiagMessage(source_) << "missing type string pool"); return false; } const ResTable_typeSpec* type_spec = ConvertTo(chunk); if (!type_spec) { - context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_typeSpec chunk"); + diag_->Error(DiagMessage(source_) << "corrupt ResTable_typeSpec chunk"); return false; } if (type_spec->id == 0) { - context_->GetDiagnostics()->Error(DiagMessage(source_) - << "ResTable_typeSpec has invalid id: " << type_spec->id); + diag_->Error(DiagMessage(source_) << "ResTable_typeSpec has invalid id: " << type_spec->id); return false; } return true; @@ -299,12 +282,12 @@ bool BinaryResourceParser::ParseTypeSpec(const ResChunk_header* chunk) { bool BinaryResourceParser::ParseType(const ResourceTablePackage* package, const ResChunk_header* chunk) { if (type_pool_.getError() != NO_ERROR) { - context_->GetDiagnostics()->Error(DiagMessage(source_) << "missing type string pool"); + diag_->Error(DiagMessage(source_) << "missing type string pool"); return false; } if (key_pool_.getError() != NO_ERROR) { - context_->GetDiagnostics()->Error(DiagMessage(source_) << "missing key string pool"); + diag_->Error(DiagMessage(source_) << "missing key string pool"); return false; } @@ -312,13 +295,12 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package, // a lot and has its own code to handle variable size. const ResTable_type* type = ConvertTo(chunk); if (!type) { - context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_type chunk"); + diag_->Error(DiagMessage(source_) << "corrupt ResTable_type chunk"); return false; } if (type->id == 0) { - context_->GetDiagnostics()->Error(DiagMessage(source_) - << "ResTable_type has invalid id: " << (int)type->id); + diag_->Error(DiagMessage(source_) << "ResTable_type has invalid id: " << (int)type->id); return false; } @@ -329,9 +311,8 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package, const ResourceType* parsed_type = ParseResourceType(type_str); if (!parsed_type) { - context_->GetDiagnostics()->Error(DiagMessage(source_) - << "invalid type name '" << type_str << "' for type with ID " - << (int)type->id); + diag_->Error(DiagMessage(source_) + << "invalid type name '" << type_str << "' for type with ID " << (int)type->id); return false; } @@ -360,14 +341,13 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package, } if (!resource_value) { - context_->GetDiagnostics()->Error(DiagMessage(source_) - << "failed to parse value for resource " << name << " (" + diag_->Error(DiagMessage(source_) << "failed to parse value for resource " << name << " (" << res_id << ") with configuration '" << config << "'"); return false; } if (!table_->AddResourceAllowMangled(name, res_id, config, {}, std::move(resource_value), - context_->GetDiagnostics())) { + diag_)) { return false; } @@ -375,7 +355,7 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package, Symbol symbol; symbol.state = SymbolState::kPublic; symbol.source = source_.WithLine(0); - if (!table_->SetSymbolStateAllowMangled(name, res_id, symbol, context_->GetDiagnostics())) { + if (!table_->SetSymbolStateAllowMangled(name, res_id, symbol, diag_)) { return false; } } @@ -410,15 +390,14 @@ std::unique_ptr BinaryResourceParser::ParseValue(const ResourceNameRef& na const android::Res_value& value) { std::unique_ptr item = ResourceUtils::ParseBinaryResValue(name.type, config, value_pool_, value, &table_->string_pool); - if (files_ != nullptr && item != nullptr) { + if (files_ != nullptr) { FileReference* file_ref = ValueCast(item.get()); if (file_ref != nullptr) { file_ref->file = files_->FindFile(*file_ref->path); if (file_ref->file == nullptr) { - context_->GetDiagnostics()->Warn(DiagMessage() - << "resource " << name << " for config '" << config - << "' is a file reference to '" << *file_ref->path - << "' but no such path exists"); + diag_->Warn(DiagMessage() << "resource " << name << " for config '" << config + << "' is a file reference to '" << *file_ref->path + << "' but no such path exists"); } } } @@ -432,7 +411,7 @@ std::unique_ptr BinaryResourceParser::ParseMapEntry(const ResourceNameRef case ResourceType::kStyle: return ParseStyle(name, config, map); case ResourceType::kAttrPrivate: - // fallthrough + // fallthrough case ResourceType::kAttr: return ParseAttr(name, config, map); case ResourceType::kArray: @@ -445,8 +424,8 @@ std::unique_ptr BinaryResourceParser::ParseMapEntry(const ResourceNameRef // We can ignore the value here. return util::make_unique(); default: - context_->GetDiagnostics()->Error(DiagMessage() << "illegal map type '" << ToString(name.type) - << "' (" << (int)name.type << ")"); + diag_->Error(DiagMessage() << "illegal map type '" << ToString(name.type) << "' (" + << (int)name.type << ")"); break; } return {}; diff --git a/tools/aapt2/format/binary/BinaryResourceParser.h b/tools/aapt2/format/binary/BinaryResourceParser.h index dc9a3847a9c6..052f806e3b95 100644 --- a/tools/aapt2/format/binary/BinaryResourceParser.h +++ b/tools/aapt2/format/binary/BinaryResourceParser.h @@ -39,7 +39,7 @@ class BinaryResourceParser { public: // Creates a parser, which will read `len` bytes from `data`, and add any resources parsed to // `table`. `source` is for logging purposes. - BinaryResourceParser(IAaptContext* context, ResourceTable* table, const Source& source, + BinaryResourceParser(IDiagnostics* diag, ResourceTable* table, const Source& source, const void* data, size_t data_len, io::IFileCollection* files = nullptr); // Parses the binary resource table and returns true if successful. @@ -80,7 +80,7 @@ class BinaryResourceParser { */ bool CollectMetaData(const android::ResTable_map& map_entry, Value* value); - IAaptContext* context_; + IDiagnostics* diag_; ResourceTable* table_; const Source source_; diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp index 57565a53794b..259f2e9cfcc8 100644 --- a/tools/aapt2/format/binary/TableFlattener.cpp +++ b/tools/aapt2/format/binary/TableFlattener.cpp @@ -568,14 +568,6 @@ bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) { ResTable_header* table_header = table_writer.StartChunk(RES_TABLE_TYPE); table_header->packageCount = util::HostToDevice32(table->packages.size()); - // Write a self mapping entry for this package if the ID is non-standard (0x7f). - if (context->GetPackageType() == PackageType::kApp) { - const uint8_t package_id = context->GetPackageId(); - if (package_id != kFrameworkPackageId && package_id != kAppPackageId) { - table->included_packages_[package_id] = context->GetCompilationPackage(); - } - } - // Flatten the values string pool. StringPool::FlattenUtf8(table_writer.buffer(), table->string_pool); @@ -583,6 +575,14 @@ bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) { // Flatten each package. for (auto& package : table->packages) { + if (context->GetPackageType() == PackageType::kApp) { + // Write a self mapping entry for this package if the ID is non-standard (0x7f). + const uint8_t package_id = package->id.value(); + if (package_id != kFrameworkPackageId && package_id != kAppPackageId) { + table->included_packages_[package_id] = package->name; + } + } + PackageFlattener flattener(context, package.get(), &table->included_packages_, options_.use_sparse_entries); if (!flattener.FlattenPackage(&package_buffer)) { diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp index 6d7597372234..e11890bbd078 100644 --- a/tools/aapt2/format/binary/TableFlattener_test.cpp +++ b/tools/aapt2/format/binary/TableFlattener_test.cpp @@ -71,7 +71,8 @@ class TableFlattenerTest : public ::testing::Test { return result; } - BinaryResourceParser parser(context, out_table, {}, content.data(), content.size()); + BinaryResourceParser parser(context->GetDiagnostics(), out_table, {}, content.data(), + content.size()); if (!parser.Parse()) { return ::testing::AssertionFailure() << "flattened ResTable is corrupt"; } @@ -278,7 +279,7 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkO) { // Attempt to parse the sparse contents. ResourceTable sparse_table; - BinaryResourceParser parser(context.get(), &sparse_table, Source("test.arsc"), + BinaryResourceParser parser(context->GetDiagnostics(), &sparse_table, Source("test.arsc"), sparse_contents.data(), sparse_contents.size()); ASSERT_TRUE(parser.Parse()); diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp index 86bd86536cc4..0f0bce8bf5a7 100644 --- a/tools/aapt2/format/proto/ProtoDeserialize.cpp +++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp @@ -371,7 +371,8 @@ static SymbolState DeserializeVisibilityFromPb(const pb::SymbolStatus_Visibility } static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStringPool& src_pool, - ResourceTable* out_table, std::string* out_error) { + io::IFileCollection* files, ResourceTable* out_table, + std::string* out_error) { Maybe id; if (pb_package.has_package_id()) { id = static_cast(pb_package.package_id().id()); @@ -444,7 +445,7 @@ static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStr } config_value->value = DeserializeValueFromPb(pb_config_value.value(), src_pool, config, - &out_table->string_pool, out_error); + &out_table->string_pool, files, out_error); if (config_value->value == nullptr) { return false; } @@ -457,8 +458,8 @@ static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStr return true; } -bool DeserializeTableFromPb(const pb::ResourceTable& pb_table, ResourceTable* out_table, - std::string* out_error) { +bool DeserializeTableFromPb(const pb::ResourceTable& pb_table, io::IFileCollection* files, + ResourceTable* out_table, std::string* out_error) { // We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which // causes errors when qualifying it with android:: using namespace android; @@ -474,7 +475,7 @@ bool DeserializeTableFromPb(const pb::ResourceTable& pb_table, ResourceTable* ou } for (const pb::Package& pb_package : pb_table.package()) { - if (!DeserializePackageFromPb(pb_package, source_pool, out_table, out_error)) { + if (!DeserializePackageFromPb(pb_package, source_pool, files, out_table, out_error)) { return false; } } @@ -600,10 +601,11 @@ static size_t DeserializePluralEnumFromPb(const pb::Plural_Arity& arity) { std::unique_ptr DeserializeValueFromPb(const pb::Value& pb_value, const android::ResStringPool& src_pool, const ConfigDescription& config, - StringPool* value_pool, std::string* out_error) { + StringPool* value_pool, io::IFileCollection* files, + std::string* out_error) { std::unique_ptr value; if (pb_value.has_item()) { - value = DeserializeItemFromPb(pb_value.item(), src_pool, config, value_pool, out_error); + value = DeserializeItemFromPb(pb_value.item(), src_pool, config, value_pool, files, out_error); if (value == nullptr) { return {}; } @@ -651,8 +653,8 @@ std::unique_ptr DeserializeValueFromPb(const pb::Value& pb_value, return {}; } DeserializeItemMetaDataFromPb(pb_entry, src_pool, &entry.key); - entry.value = - DeserializeItemFromPb(pb_entry.item(), src_pool, config, value_pool, out_error); + entry.value = DeserializeItemFromPb(pb_entry.item(), src_pool, config, value_pool, files, + out_error); if (entry.value == nullptr) { return {}; } @@ -680,8 +682,8 @@ std::unique_ptr DeserializeValueFromPb(const pb::Value& pb_value, const pb::Array& pb_array = pb_compound_value.array(); std::unique_ptr array = util::make_unique(); for (const pb::Array_Element& pb_entry : pb_array.element()) { - std::unique_ptr item = - DeserializeItemFromPb(pb_entry.item(), src_pool, config, value_pool, out_error); + std::unique_ptr item = DeserializeItemFromPb(pb_entry.item(), src_pool, config, + value_pool, files, out_error); if (item == nullptr) { return {}; } @@ -697,8 +699,8 @@ std::unique_ptr DeserializeValueFromPb(const pb::Value& pb_value, std::unique_ptr plural = util::make_unique(); for (const pb::Plural_Entry& pb_entry : pb_plural.entry()) { size_t plural_idx = DeserializePluralEnumFromPb(pb_entry.arity()); - plural->values[plural_idx] = - DeserializeItemFromPb(pb_entry.item(), src_pool, config, value_pool, out_error); + plural->values[plural_idx] = DeserializeItemFromPb(pb_entry.item(), src_pool, config, + value_pool, files, out_error); if (!plural->values[plural_idx]) { return {}; } @@ -727,7 +729,7 @@ std::unique_ptr DeserializeValueFromPb(const pb::Value& pb_value, std::unique_ptr DeserializeItemFromPb(const pb::Item& pb_item, const android::ResStringPool& src_pool, const ConfigDescription& config, StringPool* value_pool, - std::string* out_error) { + io::IFileCollection* files, std::string* out_error) { switch (pb_item.value_case()) { case pb::Item::kRef: { const pb::Reference& pb_ref = pb_item.ref(); @@ -774,6 +776,9 @@ std::unique_ptr DeserializeItemFromPb(const pb::Item& pb_item, util::make_unique(value_pool->MakeRef( pb_file.path(), StringPool::Context(StringPool::Context::kHighPriority, config))); file_ref->type = DeserializeFileReferenceTypeFromPb(pb_file.type()); + if (files != nullptr) { + file_ref->file = files->FindFile(*file_ref->path); + } return std::move(file_ref); } break; @@ -825,7 +830,7 @@ bool DeserializeXmlFromPb(const pb::XmlNode& pb_node, xml::Element* out_el, Stri } if (pb_attr.has_compiled_item()) { attr.compiled_value = - DeserializeItemFromPb(pb_attr.compiled_item(), {}, {}, value_pool, out_error); + DeserializeItemFromPb(pb_attr.compiled_item(), {}, {}, value_pool, nullptr, out_error); if (attr.compiled_value == nullptr) { return {}; } diff --git a/tools/aapt2/format/proto/ProtoDeserialize.h b/tools/aapt2/format/proto/ProtoDeserialize.h index 7dc54f242d94..0c581a17fa2e 100644 --- a/tools/aapt2/format/proto/ProtoDeserialize.h +++ b/tools/aapt2/format/proto/ProtoDeserialize.h @@ -27,6 +27,7 @@ #include "Resources.pb.h" #include "ResourcesInternal.pb.h" #include "StringPool.h" +#include "io/File.h" #include "xml/XmlDom.h" namespace aapt { @@ -34,12 +35,13 @@ namespace aapt { std::unique_ptr DeserializeValueFromPb(const pb::Value& pb_value, const android::ResStringPool& src_pool, const ConfigDescription& config, - StringPool* value_pool, std::string* out_error); + StringPool* value_pool, io::IFileCollection* files, + std::string* out_error); std::unique_ptr DeserializeItemFromPb(const pb::Item& pb_item, const android::ResStringPool& src_pool, const ConfigDescription& config, StringPool* value_pool, - std::string* out_error); + io::IFileCollection* files, std::string* out_error); std::unique_ptr DeserializeXmlResourceFromPb(const pb::XmlNode& pb_node, std::string* out_error); @@ -50,8 +52,9 @@ bool DeserializeXmlFromPb(const pb::XmlNode& pb_node, xml::Element* out_el, Stri bool DeserializeConfigFromPb(const pb::Configuration& pb_config, ConfigDescription* out_config, std::string* out_error); -bool DeserializeTableFromPb(const pb::ResourceTable& pb_table, ResourceTable* out_table, - std::string* out_error); +// Optional io::IFileCollection used to lookup references to files in the ResourceTable. +bool DeserializeTableFromPb(const pb::ResourceTable& pb_table, io::IFileCollection* files, + ResourceTable* out_table, std::string* out_error); bool DeserializeCompiledFileFromPb(const pb::internal::CompiledFile& pb_file, ResourceFile* out_file, std::string* out_error); diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp index 8efac8ae6ffd..9649a4de2fe4 100644 --- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp @@ -29,6 +29,12 @@ using ::testing::StrEq; namespace aapt { +class MockFileCollection : public io::IFileCollection { + public: + MOCK_METHOD1(FindFile, io::IFile*(const StringPiece& path)); + MOCK_METHOD0(Iterator, std::unique_ptr()); +}; + TEST(ProtoSerializeTest, SerializeSinglePackage) { std::unique_ptr context = test::ContextBuilder().Build(); std::unique_ptr table = @@ -86,9 +92,14 @@ TEST(ProtoSerializeTest, SerializeSinglePackage) { pb::ResourceTable pb_table; SerializeTableToPb(*table, &pb_table); + test::TestFile file_a("res/layout/main.xml"); + MockFileCollection files; + EXPECT_CALL(files, FindFile(Eq("res/layout/main.xml"))) + .WillRepeatedly(::testing::Return(&file_a)); + ResourceTable new_table; std::string error; - ASSERT_TRUE(DeserializeTableFromPb(pb_table, &new_table, &error)); + ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error)); EXPECT_THAT(error, IsEmpty()); Id* new_id = test::GetValue(&new_table, "com.app.a:id/foo"); @@ -124,6 +135,11 @@ TEST(ProtoSerializeTest, SerializeSinglePackage) { ASSERT_TRUE(actual_ref->id); EXPECT_THAT(*actual_ref, Eq(expected_ref)); + FileReference* actual_file_ref = + test::GetValue(&new_table, "com.app.a:layout/main"); + ASSERT_THAT(actual_file_ref, NotNull()); + EXPECT_THAT(actual_file_ref->file, Eq(&file_a)); + StyledString* actual_styled_str = test::GetValue(&new_table, "com.app.a:string/styled"); ASSERT_THAT(actual_styled_str, NotNull()); diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk index 6ed07b0c5c73..94686c095bd8 100644 --- a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk +++ b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk @@ -25,5 +25,4 @@ LOCAL_SRC_FILES := $(call all-java-files-under,src) LOCAL_STATIC_ANDROID_LIBRARIES := \ AaptTestNamespace_LibOne \ AaptTestNamespace_LibTwo -LOCAL_AAPT_FLAGS := -v include $(BUILD_PACKAGE) diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp index 21c6b111b855..58d0607ed7b3 100644 --- a/tools/aapt2/link/TableMerger.cpp +++ b/tools/aapt2/link/TableMerger.cpp @@ -37,17 +37,14 @@ TableMerger::TableMerger(IAaptContext* context, ResourceTable* out_table, CHECK(master_package_ != nullptr) << "package name or ID already taken"; } -bool TableMerger::Merge(const Source& src, ResourceTable* table, bool overlay, - io::IFileCollection* collection) { +bool TableMerger::Merge(const Source& src, ResourceTable* table, bool overlay) { // We allow adding new resources if this is not an overlay, or if the options allow overlays // to add new resources. - return MergeImpl(src, table, collection, overlay, - options_.auto_add_overlay || !overlay /*allow_new*/); + return MergeImpl(src, table, overlay, options_.auto_add_overlay || !overlay /*allow_new*/); } // This will merge packages with the same package name (or no package name). -bool TableMerger::MergeImpl(const Source& src, ResourceTable* table, - io::IFileCollection* collection, bool overlay, bool allow_new) { +bool TableMerger::MergeImpl(const Source& src, ResourceTable* table, bool overlay, bool allow_new) { bool error = false; for (auto& package : table->packages) { // Only merge an empty package or the package we're building. @@ -55,37 +52,20 @@ bool TableMerger::MergeImpl(const Source& src, ResourceTable* table, // This is because at compile time it is unknown if the attributes are // simply uses of the attribute or definitions. if (package->name.empty() || context_->GetCompilationPackage() == package->name) { - FileMergeCallback callback; - if (collection) { - callback = [&](const ResourceNameRef& name, const ConfigDescription& config, - FileReference* new_file, FileReference* old_file) -> bool { - // The old file's path points inside the APK, so we can use it as is. - io::IFile* f = collection->FindFile(*old_file->path); - if (!f) { - context_->GetDiagnostics()->Error(DiagMessage(src) - << "file '" << *old_file->path << "' not found"); - return false; - } - - new_file->file = f; - return true; - }; - } - // Merge here. Once the entries are merged and mangled, any references to them are still // valid. This is because un-mangled references are mangled, then looked up at resolution // time. Also, when linking, we convert references with no package name to use the compilation // package name. - error |= - !DoMerge(src, table, package.get(), false /* mangle */, overlay, allow_new, callback); + error |= !DoMerge(src, table, package.get(), false /*mangle*/, overlay, allow_new); } } return !error; } -// This will merge and mangle resources from a static library. +// This will merge and mangle resources from a static library. It is assumed that all FileReferences +// have correctly set their io::IFile*. bool TableMerger::MergeAndMangle(const Source& src, const StringPiece& package_name, - ResourceTable* table, io::IFileCollection* collection) { + ResourceTable* table) { bool error = false; for (auto& package : table->packages) { // Warn of packages with an unrelated ID. @@ -96,23 +76,7 @@ bool TableMerger::MergeAndMangle(const Source& src, const StringPiece& package_n bool mangle = package_name != context_->GetCompilationPackage(); merged_packages_.insert(package->name); - - auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config, - FileReference* new_file, FileReference* old_file) -> bool { - // The old file's path points inside the APK, so we can use it as is. - io::IFile* f = collection->FindFile(*old_file->path); - if (!f) { - context_->GetDiagnostics()->Error(DiagMessage(src) - << "file '" << *old_file->path << "' not found"); - return false; - } - - new_file->file = f; - return true; - }; - - error |= !DoMerge(src, table, package.get(), mangle, false /*overlay*/, true /*allow_new*/, - callback); + error |= !DoMerge(src, table, package.get(), mangle, false /*overlay*/, true /*allow_new*/); } return !error; } @@ -187,7 +151,7 @@ static ResourceTable::CollisionResult ResolveMergeCollision(Value* existing, Val static ResourceTable::CollisionResult MergeConfigValue(IAaptContext* context, const ResourceNameRef& res_name, - const bool overlay, + bool overlay, ResourceConfigValue* dst_config_value, ResourceConfigValue* src_config_value, StringPool* pool) { @@ -220,10 +184,8 @@ static ResourceTable::CollisionResult MergeConfigValue(IAaptContext* context, } bool TableMerger::DoMerge(const Source& src, ResourceTable* src_table, - ResourceTablePackage* src_package, - const bool mangle_package, const bool overlay, - const bool allow_new_resources, - const FileMergeCallback& callback) { + ResourceTablePackage* src_package, bool mangle_package, bool overlay, + bool allow_new_resources) { bool error = false; for (auto& src_type : src_package->types) { @@ -292,13 +254,6 @@ bool TableMerger::DoMerge(const Source& src, ResourceTable* src_table, } else { new_file_ref = std::unique_ptr(f->Clone(&master_table_->string_pool)); } - - if (callback) { - if (!callback(res_name, src_config_value->config, new_file_ref.get(), f)) { - error = true; - continue; - } - } dst_config_value->value = std::move(new_file_ref); } else { @@ -343,8 +298,8 @@ bool TableMerger::MergeFile(const ResourceFile& file_desc, bool overlay, io::IFi ->FindOrCreateValue(file_desc.config, {}) ->value = std::move(file_ref); - return DoMerge(file->GetSource(), &table, pkg, false /* mangle */, overlay /* overlay */, - true /* allow_new */, {}); + return DoMerge(file->GetSource(), &table, pkg, false /*mangle*/, overlay /*overlay*/, + true /*allow_new*/); } } // namespace aapt diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h index d024aa47d952..47e23dded26f 100644 --- a/tools/aapt2/link/TableMerger.h +++ b/tools/aapt2/link/TableMerger.h @@ -40,6 +40,9 @@ struct TableMergerOptions { // TableMerger takes resource tables and merges all packages within the tables that have the same // package ID. // +// It is assumed that any FileReference values have their io::IFile pointer set to point to the +// file they represent. +// // If a package has a different name, all the entries in that table have their names mangled // to include the package name. This way there are no collisions. In order to do this correctly, // the TableMerger needs to also mangle any FileReference paths. Once these are mangled, the @@ -60,14 +63,11 @@ class TableMerger { // Merges resources from the same or empty package. This is for local sources. // If overlay is true, the resources are treated as overlays. - // An io::IFileCollection is optional and used to find the referenced Files and process them. - bool Merge(const Source& src, ResourceTable* table, bool overlay, - io::IFileCollection* collection = nullptr); + bool Merge(const Source& src, ResourceTable* table, bool overlay); // Merges resources from the given package, mangling the name. This is for static libraries. - // An io::IFileCollection is needed in order to find the referenced Files and process them. - bool MergeAndMangle(const Source& src, const android::StringPiece& package, ResourceTable* table, - io::IFileCollection* collection); + // All FileReference values must have their io::IFile set. + bool MergeAndMangle(const Source& src, const android::StringPiece& package, ResourceTable* table); // Merges a compiled file that belongs to this same or empty package. bool MergeFile(const ResourceFile& fileDesc, bool overlay, io::IFile* file); @@ -75,23 +75,16 @@ class TableMerger { private: DISALLOW_COPY_AND_ASSIGN(TableMerger); - using FileMergeCallback = std::function; - IAaptContext* context_; ResourceTable* master_table_; TableMergerOptions options_; ResourceTablePackage* master_package_; std::set merged_packages_; - bool MergeImpl(const Source& src, ResourceTable* src_table, - io::IFileCollection* collection, bool overlay, bool allow_new); + bool MergeImpl(const Source& src, ResourceTable* src_table, bool overlay, bool allow_new); - bool DoMerge(const Source& src, ResourceTable* src_table, - ResourceTablePackage* src_package, const bool mangle_package, - const bool overlay, const bool allow_new_resources, - const FileMergeCallback& callback); + bool DoMerge(const Source& src, ResourceTable* src_table, ResourceTablePackage* src_package, + bool mangle_package, bool overlay, bool allow_new_resources); std::unique_ptr CloneAndMangleFile(const std::string& package, const FileReference& value); diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp index 3499809e5a83..6aab8ded24a5 100644 --- a/tools/aapt2/link/TableMerger_test.cpp +++ b/tools/aapt2/link/TableMerger_test.cpp @@ -22,11 +22,12 @@ using ::aapt::test::ValueEq; using ::testing::Contains; +using ::testing::Eq; +using ::testing::Field; using ::testing::NotNull; -using ::testing::UnorderedElementsAreArray; using ::testing::Pointee; -using ::testing::Field; -using ::testing::Eq; +using ::testing::StrEq; +using ::testing::UnorderedElementsAreArray; namespace aapt { @@ -67,10 +68,9 @@ TEST_F(TableMergerTest, SimpleMerge) { ResourceTable final_table; TableMerger merger(context_.get(), &final_table, TableMergerOptions{}); - io::FileCollection collection; ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/)); - ASSERT_TRUE(merger.MergeAndMangle({}, "com.app.b", table_b.get(), &collection)); + ASSERT_TRUE(merger.MergeAndMangle({}, "com.app.b", table_b.get())); EXPECT_TRUE(merger.merged_packages().count("com.app.b") != 0); @@ -122,32 +122,35 @@ TEST_F(TableMergerTest, MergeFileOverlay) { } TEST_F(TableMergerTest, MergeFileReferences) { + test::TestFile file_a("res/xml/file.xml"); + test::TestFile file_b("res/xml/file.xml"); + std::unique_ptr table_a = test::ResourceTableBuilder() .SetPackageId("com.app.a", 0x7f) - .AddFileReference("com.app.a:xml/file", "res/xml/file.xml") + .AddFileReference("com.app.a:xml/file", "res/xml/file.xml", &file_a) .Build(); std::unique_ptr table_b = test::ResourceTableBuilder() .SetPackageId("com.app.b", 0x7f) - .AddFileReference("com.app.b:xml/file", "res/xml/file.xml") + .AddFileReference("com.app.b:xml/file", "res/xml/file.xml", &file_b) .Build(); ResourceTable final_table; TableMerger merger(context_.get(), &final_table, TableMergerOptions{}); - io::FileCollection collection; - collection.InsertFile("res/xml/file.xml"); ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/)); - ASSERT_TRUE(merger.MergeAndMangle({}, "com.app.b", table_b.get(), &collection)); + ASSERT_TRUE(merger.MergeAndMangle({}, "com.app.b", table_b.get())); FileReference* f = test::GetValue(&final_table, "com.app.a:xml/file"); ASSERT_THAT(f, NotNull()); - EXPECT_EQ(std::string("res/xml/file.xml"), *f->path); + EXPECT_THAT(*f->path, StrEq("res/xml/file.xml")); + EXPECT_THAT(f->file, Eq(&file_a)); f = test::GetValue(&final_table, "com.app.a:xml/com.app.b$file"); ASSERT_THAT(f, NotNull()); - EXPECT_EQ(std::string("res/xml/com.app.b$file.xml"), *f->path); + EXPECT_THAT(*f->path, StrEq("res/xml/com.app.b$file.xml")); + EXPECT_THAT(f->file, Eq(&file_b)); } TEST_F(TableMergerTest, OverrideResourceWithOverlay) { diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp index 680308871af7..473693ce4917 100644 --- a/tools/aapt2/optimize/MultiApkGenerator.cpp +++ b/tools/aapt2/optimize/MultiApkGenerator.cpp @@ -265,12 +265,14 @@ bool MultiApkGenerator::UpdateManifest(const Artifact& artifact, const PostProcessingConfiguration& config, std::unique_ptr* updated_manifest, IDiagnostics* diag) { - *updated_manifest = apk_->InflateManifest(context_); - XmlResource* manifest = updated_manifest->get(); - if (manifest == nullptr) { + const xml::XmlResource* apk_manifest = apk_->GetManifest(); + if (apk_manifest == nullptr) { return false; } + *updated_manifest = apk_manifest->Clone(); + XmlResource* manifest = updated_manifest->get(); + // Make sure the first element is with package attribute. xml::Element* manifest_el = manifest->root.get(); if (manifest_el == nullptr) { diff --git a/tools/aapt2/optimize/MultiApkGenerator_test.cpp b/tools/aapt2/optimize/MultiApkGenerator_test.cpp index c8f3524748b6..30c9146b5444 100644 --- a/tools/aapt2/optimize/MultiApkGenerator_test.cpp +++ b/tools/aapt2/optimize/MultiApkGenerator_test.cpp @@ -106,7 +106,7 @@ class MultiApkGeneratorTest : public ::testing::Test { TEST_F(MultiApkGeneratorTest, VersionFilterNewerVersion) { std::unique_ptr table = BuildTable(); - LoadedApk apk = {{"test.apk"}, {}, std::move(table)}; + LoadedApk apk = {{"test.apk"}, {}, std::move(table), {}}; std::unique_ptr ctx = test::ContextBuilder().SetMinSdkVersion(19).Build(); PostProcessingConfiguration empty_config; TableFlattenerOptions table_flattener_options; @@ -147,7 +147,7 @@ TEST_F(MultiApkGeneratorTest, VersionFilterNewerVersion) { TEST_F(MultiApkGeneratorTest, VersionFilterOlderVersion) { std::unique_ptr table = BuildTable(); - LoadedApk apk = {{"test.apk"}, {}, std::move(table)}; + LoadedApk apk = {{"test.apk"}, {}, std::move(table), {}}; std::unique_ptr ctx = test::ContextBuilder().SetMinSdkVersion(1).Build(); PostProcessingConfiguration empty_config; TableFlattenerOptions table_flattener_options; @@ -186,7 +186,7 @@ TEST_F(MultiApkGeneratorTest, VersionFilterOlderVersion) { TEST_F(MultiApkGeneratorTest, VersionFilterNoVersion) { std::unique_ptr table = BuildTable(); - LoadedApk apk = {{"test.apk"}, {}, std::move(table)}; + LoadedApk apk = {{"test.apk"}, {}, std::move(table), {}}; std::unique_ptr ctx = test::ContextBuilder().SetMinSdkVersion(1).Build(); PostProcessingConfiguration empty_config; TableFlattenerOptions table_flattener_options; diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp index 5a62e9777065..ecec63f3cf31 100644 --- a/tools/aapt2/test/Builders.cpp +++ b/tools/aapt2/test/Builders.cpp @@ -78,21 +78,27 @@ ResourceTableBuilder& ResourceTableBuilder::AddString(const StringPiece& name, c } ResourceTableBuilder& ResourceTableBuilder::AddFileReference(const StringPiece& name, - const StringPiece& path) { - return AddFileReference(name, {}, path); + const StringPiece& path, + io::IFile* file) { + return AddFileReference(name, {}, path, file); } ResourceTableBuilder& ResourceTableBuilder::AddFileReference(const StringPiece& name, const ResourceId& id, - const StringPiece& path) { - return AddValue(name, id, util::make_unique(table_->string_pool.MakeRef(path))); + const StringPiece& path, + io::IFile* file) { + auto file_ref = util::make_unique(table_->string_pool.MakeRef(path)); + file_ref->file = file; + return AddValue(name, id, std::move(file_ref)); } ResourceTableBuilder& ResourceTableBuilder::AddFileReference(const StringPiece& name, const StringPiece& path, - const ConfigDescription& config) { - return AddValue(name, config, {}, - util::make_unique(table_->string_pool.MakeRef(path))); + const ConfigDescription& config, + io::IFile* file) { + auto file_ref = util::make_unique(table_->string_pool.MakeRef(path)); + file_ref->file = file; + return AddValue(name, config, {}, std::move(file_ref)); } ResourceTableBuilder& ResourceTableBuilder::AddValue(const StringPiece& name, diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h index 263fb5562b25..4cdfc33e92e3 100644 --- a/tools/aapt2/test/Builders.h +++ b/tools/aapt2/test/Builders.h @@ -52,12 +52,15 @@ class ResourceTableBuilder { ResourceTableBuilder& AddString(const android::StringPiece& name, const ResourceId& id, const ConfigDescription& config, const android::StringPiece& str); ResourceTableBuilder& AddFileReference(const android::StringPiece& name, - const android::StringPiece& path); + const android::StringPiece& path, + io::IFile* file = nullptr); ResourceTableBuilder& AddFileReference(const android::StringPiece& name, const ResourceId& id, - const android::StringPiece& path); + const android::StringPiece& path, + io::IFile* file = nullptr); ResourceTableBuilder& AddFileReference(const android::StringPiece& name, const android::StringPiece& path, - const ConfigDescription& config); + const ConfigDescription& config, + io::IFile* file = nullptr); ResourceTableBuilder& AddValue(const android::StringPiece& name, std::unique_ptr value); ResourceTableBuilder& AddValue(const android::StringPiece& name, const ResourceId& id, std::unique_ptr value); diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp index 35225066633c..b0cf44accafa 100644 --- a/tools/aapt2/xml/XmlDom.cpp +++ b/tools/aapt2/xml/XmlDom.cpp @@ -258,8 +258,7 @@ static void CopyAttributes(Element* el, android::ResXMLParser* parser, StringPoo } } -std::unique_ptr Inflate(const void* data, size_t data_len, IDiagnostics* diag, - const Source& source) { +std::unique_ptr Inflate(const void* data, size_t len, std::string* out_error) { // We import the android namespace because on Windows NO_ERROR is a macro, not // an enum, which causes errors when qualifying it with android:: using namespace android; @@ -270,7 +269,10 @@ std::unique_ptr Inflate(const void* data, size_t data_len, IDiagnos std::unique_ptr pending_element; ResXMLTree tree; - if (tree.setTo(data, data_len) != NO_ERROR) { + if (tree.setTo(data, len) != NO_ERROR) { + if (out_error != nullptr) { + *out_error = "failed to initialize ResXMLTree"; + } return {}; } @@ -361,6 +363,27 @@ std::unique_ptr Inflate(const void* data, size_t data_len, IDiagnos return util::make_unique(ResourceFile{}, std::move(string_pool), std::move(root)); } +std::unique_ptr XmlResource::Clone() const { + std::unique_ptr cloned = util::make_unique(file); + if (root != nullptr) { + cloned->root = root->CloneElement([&](const xml::Element& src, xml::Element* dst) { + dst->attributes.reserve(src.attributes.size()); + for (const xml::Attribute& attr : src.attributes) { + xml::Attribute cloned_attr; + cloned_attr.name = attr.name; + cloned_attr.namespace_uri = attr.namespace_uri; + cloned_attr.value = attr.value; + cloned_attr.compiled_attribute = attr.compiled_attribute; + if (attr.compiled_value != nullptr) { + cloned_attr.compiled_value.reset(attr.compiled_value->Clone(&cloned->string_pool)); + } + dst->attributes.push_back(std::move(cloned_attr)); + } + }); + } + return cloned; +} + Element* FindRootElement(Node* node) { if (node == nullptr) { return nullptr; @@ -383,12 +406,7 @@ void Element::InsertChild(size_t index, std::unique_ptr child) { } Attribute* Element::FindAttribute(const StringPiece& ns, const StringPiece& name) { - for (auto& attr : attributes) { - if (ns == attr.namespace_uri && name == attr.name) { - return &attr; - } - } - return nullptr; + return const_cast(static_cast(this)->FindAttribute(ns, name)); } const Attribute* Element::FindAttribute(const StringPiece& ns, const StringPiece& name) const { @@ -404,17 +422,29 @@ Element* Element::FindChild(const StringPiece& ns, const StringPiece& name) { return FindChildWithAttribute(ns, name, {}, {}, {}); } +const Element* Element::FindChild(const StringPiece& ns, const StringPiece& name) const { + return FindChildWithAttribute(ns, name, {}, {}, {}); +} + Element* Element::FindChildWithAttribute(const StringPiece& ns, const StringPiece& name, const StringPiece& attr_ns, const StringPiece& attr_name, const StringPiece& attr_value) { - for (auto& child : children) { - if (Element* el = NodeCast(child.get())) { + return const_cast(static_cast(this)->FindChildWithAttribute( + ns, name, attr_ns, attr_name, attr_value)); +} + +const Element* Element::FindChildWithAttribute(const StringPiece& ns, const StringPiece& name, + const StringPiece& attr_ns, + const StringPiece& attr_name, + const StringPiece& attr_value) const { + for (const auto& child : children) { + if (const Element* el = NodeCast(child.get())) { if (ns == el->namespace_uri && name == el->name) { if (attr_ns.empty() && attr_name.empty()) { return el; } - Attribute* attr = el->FindAttribute(attr_ns, attr_name); + const Attribute* attr = el->FindAttribute(attr_ns, attr_name); if (attr && attr_value == attr->value) { return el; } diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h index 063d7b9210c1..cf06ba5cc50e 100644 --- a/tools/aapt2/xml/XmlDom.h +++ b/tools/aapt2/xml/XmlDom.h @@ -100,11 +100,21 @@ class Element : public Node { Attribute* FindAttribute(const android::StringPiece& ns, const android::StringPiece& name); const Attribute* FindAttribute(const android::StringPiece& ns, const android::StringPiece& name) const; + Element* FindChild(const android::StringPiece& ns, const android::StringPiece& name); + const Element* FindChild(const android::StringPiece& ns, const android::StringPiece& name) const; + Element* FindChildWithAttribute(const android::StringPiece& ns, const android::StringPiece& name, const android::StringPiece& attr_ns, const android::StringPiece& attr_name, const android::StringPiece& attr_value); + + const Element* FindChildWithAttribute(const android::StringPiece& ns, + const android::StringPiece& name, + const android::StringPiece& attr_ns, + const android::StringPiece& attr_name, + const android::StringPiece& attr_value) const; + std::vector GetChildElements(); // Due to overriding of subtypes not working with unique_ptr, define a convenience Clone method @@ -139,16 +149,16 @@ class XmlResource { StringPool string_pool; std::unique_ptr root; + + std::unique_ptr Clone() const; }; // Inflates an XML DOM from an InputStream, logging errors to the logger. -// Returns the root node on success, or nullptr on failure. std::unique_ptr Inflate(io::InputStream* in, IDiagnostics* diag, const Source& source); -// Inflates an XML DOM from a binary ResXMLTree, logging errors to the logger. -// Returns the root node on success, or nullptr on failure. -std::unique_ptr Inflate(const void* data, size_t data_len, IDiagnostics* diag, - const Source& source); +// Inflates an XML DOM from a binary ResXMLTree. +std::unique_ptr Inflate(const void* data, size_t len, + std::string* out_error = nullptr); Element* FindRootElement(Node* node); diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp index 34e6d3f54f56..e5012d67163d 100644 --- a/tools/aapt2/xml/XmlDom_test.cpp +++ b/tools/aapt2/xml/XmlDom_test.cpp @@ -70,8 +70,7 @@ TEST(XmlDomTest, BinaryInflate) { ASSERT_TRUE(flattener.Consume(context.get(), doc.get())); auto block = util::Copy(buffer); - std::unique_ptr new_doc = - Inflate(block.get(), buffer.size(), context->GetDiagnostics(), Source("test.xml")); + std::unique_ptr new_doc = Inflate(block.get(), buffer.size(), nullptr); ASSERT_THAT(new_doc, NotNull()); EXPECT_THAT(new_doc->root->name, StrEq("Layout")); -- cgit v1.2.3-59-g8ed1b