diff options
29 files changed, 702 insertions, 429 deletions
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> LoadedApk::LoadApkFromPath(IAaptContext* context, - const android::StringPiece& path) { +std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(const StringPiece& path, IDiagnostics* diag) { Source source(path); std::string error; std::unique_ptr<io::ZipFileCollection> 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> LoadedApk::LoadProtoApkFromFileCollection( + const Source& source, unique_ptr<io::IFileCollection> 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<io::InputStream> in = table_file->OpenInputStream(); + if (in == nullptr) { + diag->Error(DiagMessage(source) << "failed to open " << kProtoResourceTablePath); return {}; } - std::unique_ptr<io::IData> 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<ResourceTable> table = util::make_unique<ResourceTable>(); - 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<io::InputStream> 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<xml::XmlResource> manifest = DeserializeXmlResourceFromPb(pb_node, &error); + if (manifest == nullptr) { + diag->Error(DiagMessage(source) + << "failed to deserialize proto " << kAndroidManifestPath << ": " << error); + return {}; + } + return util::make_unique<LoadedApk>(source, std::move(collection), std::move(table), + std::move(manifest)); +} + +std::unique_ptr<LoadedApk> LoadedApk::LoadBinaryApkFromFileCollection( + const Source& source, unique_ptr<io::IFileCollection> 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<io::IData> data = table_file->OpenAsData(); + if (data == nullptr) { + diag->Error(DiagMessage(source) << "failed to open " << kApkResourceTablePath); + return {}; + } + + std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>(); + BinaryResourceParser parser(diag, table.get(), source, data->data(), data->size(), + collection.get()); if (!parser.Parse()) { return {}; } - return util::make_unique<LoadedApk>(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<io::IData> manifest_data = manifest_file->OpenAsData(); + if (manifest_data == nullptr) { + diag->Error(DiagMessage(source) << "failed to open " << kAndroidManifestPath); + return {}; + } + + std::string error; + std::unique_ptr<xml::XmlResource> 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<LoadedApk>(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<xml::XmlResource> 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<io::IData> manifest_data = manifest_file->OpenAsData(); - if (manifest_data == nullptr) { - diag->Error(DiagMessage(manifest_file->GetSource()) << "could not open AndroidManifest.xml"); - return {}; - } - - std::unique_ptr<xml::XmlResource> 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<io::IFileCollection> apk, - std::unique_ptr<ResourceTable> 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<LoadedApk> LoadApkFromPath(const ::android::StringPiece& path, + IDiagnostics* diag); + + // Loads a proto APK from the given file collection. + static std::unique_ptr<LoadedApk> LoadProtoApkFromFileCollection( + const Source& source, std::unique_ptr<io::IFileCollection> collection, IDiagnostics* diag); + + // Loads a binary APK from the given file collection. + static std::unique_ptr<LoadedApk> LoadBinaryApkFromFileCollection( + const Source& source, std::unique_ptr<io::IFileCollection> collection, IDiagnostics* diag); + + LoadedApk(const Source& source, std::unique_ptr<io::IFileCollection> apk, + std::unique_ptr<ResourceTable> table, std::unique_ptr<xml::XmlResource> 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<xml::XmlResource> InflateManifest(IAaptContext* context); - - static std::unique_ptr<LoadedApk> LoadApkFromPath(IAaptContext* context, - const android::StringPiece& path); private: DISALLOW_COPY_AND_ASSIGN(LoadedApk); @@ -83,6 +103,7 @@ class LoadedApk { Source source_; std::unique_ptr<io::IFileCollection> apk_; std::unique_ptr<ResourceTable> table_; + std::unique_ptr<xml::XmlResource> 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<StringPiece>& args, IDiagnostics* diagnostics); @@ -58,6 +58,7 @@ extern int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) extern int Dump(const std::vector<StringPiece>& args); extern int Diff(const std::vector<StringPiece>& args); extern int Optimize(const std::vector<StringPiece>& args); +extern int Convert(const std::vector<StringPiece>& args); static int ExecuteCommand(const StringPiece& command, const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { @@ -71,6 +72,8 @@ static int ExecuteCommand(const StringPiece& command, const std::vector<StringPi return Diff(args); } else if (command == "optimize") { return Optimize(args); + } else if (command == "convert") { + return Convert(args); } else if (command == "version") { PrintVersion(); return 0; diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp new file mode 100644 index 000000000000..89ae9e82f603 --- /dev/null +++ b/tools/aapt2/cmd/Convert.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <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<LoadedApk> 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<FileReference>(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<io::InputStream> 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::XmlResource> 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<StringPiece>& 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<LoadedApk> apk = LoadedApk::LoadApkFromPath(path, context.GetDiagnostics()); + if (apk == nullptr) { + context.GetDiagnostics()->Error(DiagMessage(path) << "failed to load APK"); + return 1; + } + + Maybe<AppInfo> app_info = + ExtractAppInfoFromBinaryManifest(*apk->GetManifest(), context.GetDiagnostics()); + if (!app_info) { + return 1; + } + + context.package_ = app_info.value().package; + + unique_ptr<IArchiveWriter> 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<StringPiece>& args) { return 1; } - std::unique_ptr<LoadedApk> apk_a = LoadedApk::LoadApkFromPath(&context, flags.GetArgs()[0]); - std::unique_ptr<LoadedApk> apk_b = LoadedApk::LoadApkFromPath(&context, flags.GetArgs()[1]); + IDiagnostics* diag = context.GetDiagnostics(); + std::unique_ptr<LoadedApk> apk_a = LoadedApk::LoadApkFromPath(flags.GetArgs()[0], diag); + std::unique_ptr<LoadedApk> 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<ResourceTable> 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<ResourceTable> table = util::make_unique<ResourceTable>(); - 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<xml::XmlResource> 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<int>(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<io::FileCollection>()) { } - /** - * 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<AssetManagerSymbolSource> asset_source = - util::make_unique<AssetManagerSymbolSource>(); + auto asset_source = util::make_unique<AssetManagerSymbolSource>(); 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<ResourceTable> 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<LoadedApk> 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<ResourceTableSymbolSource>(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<ResourceTableSymbolSource>(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<ResourceTable> LoadStaticLibrary(const std::string& input, - std::string* out_error) { - std::unique_ptr<io::ZipFileCollection> collection = - io::ZipFileCollection::Create(input, out_error); - if (!collection) { - return {}; - } - return LoadTablePbFromCollection(collection.get()); - } - - std::unique_ptr<ResourceTable> LoadTablePbFromCollection(io::IFileCollection* collection) { - io::IFile* file = collection->FindFile(kProtoResourceTablePath); - if (!file) { - return {}; - } - - std::unique_ptr<io::IData> 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<io::ZipFileCollection> collection = - io::ZipFileCollection::Create(input, &error_str); - if (!collection) { - context_->GetDiagnostics()->Error(DiagMessage(input) << error_str); - return false; - } - - std::unique_ptr<ResourceTable> table = LoadTablePbFromCollection(collection.get()); - if (!table) { + std::unique_ptr<LoadedApk> 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<io::IData> data = file->OpenAsData(); - if (!data) { - context_->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << "failed to open file"); - return false; - } - - std::unique_ptr<ResourceTable> 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<io::FileCollection> 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<std::unique_ptr<io::IFileCollection>> collections_; - // A vector of ResourceTables. This is here to retain ownership, so that the - // SymbolTable can use these. - std::vector<std::unique_ptr<ResourceTable>> static_table_includes_; + // The set of merged APKs. This is mainly here to retain ownership of the APKs. + std::vector<std::unique_ptr<LoadedApk>> merged_apks_; + + // The set of included APKs (not merged). This is mainly here to retain ownership of the APKs. + std::vector<std::unique_ptr<LoadedApk>> static_library_includes_; // The set of shared libraries being used, mapping their assigned package ID to package name. std::map<size_t, std::string> 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<xml::XmlResource> manifest = apk->InflateManifest(context); + const xml::XmlResource* manifest = apk->GetManifest(); if (manifest == nullptr) { return false; } - Maybe<AppInfo> app_info = - ExtractAppInfoFromBinaryManifest(manifest.get(), context->GetDiagnostics()); + Maybe<AppInfo> 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<StringPiece>& args) { } const std::string& apk_path = flags.GetArgs()[0]; - std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(&context, apk_path); + std::unique_ptr<LoadedApk> 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<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info, return doc; } -static Maybe<std::string> ExtractCompiledString(xml::Attribute* attr, std::string* out_error) { - if (attr->compiled_value != nullptr) { - String* compiled_str = ValueCast<String>(attr->compiled_value.get()); +static Maybe<std::string> ExtractCompiledString(const xml::Attribute& attr, + std::string* out_error) { + if (attr.compiled_value != nullptr) { + const String* compiled_str = ValueCast<String>(attr.compiled_value.get()); if (compiled_str != nullptr) { if (!compiled_str->value->empty()) { return *compiled_str->value; @@ -230,16 +231,16 @@ static Maybe<std::string> 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<uint32_t> ExtractCompiledInt(xml::Attribute* attr, std::string* out_error) { - if (attr->compiled_value != nullptr) { - BinaryPrimitive* compiled_prim = ValueCast<BinaryPrimitive>(attr->compiled_value.get()); +static Maybe<uint32_t> ExtractCompiledInt(const xml::Attribute& attr, std::string* out_error) { + if (attr.compiled_value != nullptr) { + const BinaryPrimitive* compiled_prim = ValueCast<BinaryPrimitive>(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<uint32_t> ExtractCompiledInt(xml::Attribute* attr, std::string* out } // Fallback to the plain text value if there is one. - Maybe<uint32_t> integer = ResourceUtils::ParseInt(attr->value); + Maybe<uint32_t> 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<int> ExtractSdkVersion(xml::Attribute* attr, std::string* out_error) { - if (attr->compiled_value != nullptr) { - BinaryPrimitive* compiled_prim = ValueCast<BinaryPrimitive>(attr->compiled_value.get()); +static Maybe<int> ExtractSdkVersion(const xml::Attribute& attr, std::string* out_error) { + if (attr.compiled_value != nullptr) { + const BinaryPrimitive* compiled_prim = ValueCast<BinaryPrimitive>(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<int> ExtractSdkVersion(xml::Attribute* attr, std::string* out_error return {}; } - String* compiled_str = ValueCast<String>(attr->compiled_value.get()); + const String* compiled_str = ValueCast<String>(attr.compiled_value.get()); if (compiled_str != nullptr) { Maybe<int> sdk_version = ResourceUtils::ParseSdkVersion(*compiled_str->value); if (sdk_version) { @@ -288,19 +289,20 @@ static Maybe<int> ExtractSdkVersion(xml::Attribute* attr, std::string* out_error } // Fallback to the plain text value if there is one. - Maybe<int> sdk_version = ResourceUtils::ParseSdkVersion(attr->value); + Maybe<int> 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<AppInfo> ExtractAppInfoFromBinaryManifest(xml::XmlResource* xml_res, IDiagnostics* diag) { +Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res, + IDiagnostics* diag) { // Make sure the first element is <manifest> 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<AppInfo> 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 <manifest>"); + diag->Error(DiagMessage(xml_res.file.source) << "root tag must be <manifest>"); 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) << "<manifest> must have a 'package' attribute"); + diag->Error(DiagMessage(xml_res.file.source) << "<manifest> must have a 'package' attribute"); return {}; } std::string error_msg; - Maybe<std::string> maybe_package = ExtractCompiledString(package_attr, &error_msg); + Maybe<std::string> 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<uint32_t> maybe_code = ExtractCompiledInt(version_code_attr, &error_msg); + Maybe<uint32_t> 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<uint32_t> maybe_code = ExtractCompiledInt(revision_code_attr, &error_msg); + Maybe<uint32_t> 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<std::string> maybe_split_name = ExtractCompiledString(split_name_attr, &error_msg); + if (const xml::Attribute* split_name_attr = manifest_el->FindAttribute({}, "split")) { + Maybe<std::string> 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<int> maybe_sdk = ExtractSdkVersion(min_sdk, &error_msg); + Maybe<int> 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<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info, const SplitConstraints& constraints); // Extracts relevant info from the AndroidManifest.xml. -Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(xml::XmlResource* xml_res, IDiagnostics* diag); +Maybe<AppInfo> 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<int>(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<int>(parser.chunk()->type))); + diag_->Warn(DiagMessage(source_) + << StringPrintf("unexpected chunk of type 0x%02x trailing RES_TABLE_TYPE", + static_cast<int>(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<ResTable_header>(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<int>(util::DeviceToHost16(parser.chunk()->type))); + diag_->Warn(DiagMessage(source_) + << "unexpected chunk type " + << static_cast<int>(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<ResTable_package, kMinPackageSize>(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<uint8_t>::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<uint8_t>(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<int>(util::DeviceToHost16(parser.chunk()->type))); + diag_->Warn(DiagMessage(source_) + << "unexpected chunk type " + << static_cast<int>(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<ResTable_typeSpec>(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<ResTable_type, kResTableTypeMinSize>(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<Item> BinaryResourceParser::ParseValue(const ResourceNameRef& na const android::Res_value& value) { std::unique_ptr<Item> item = ResourceUtils::ParseBinaryResValue(name.type, config, value_pool_, value, &table_->string_pool); - if (files_ != nullptr && item != nullptr) { + if (files_ != nullptr) { FileReference* file_ref = ValueCast<FileReference>(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<Value> 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<Value> BinaryResourceParser::ParseMapEntry(const ResourceNameRef // We can ignore the value here. return util::make_unique<Id>(); 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<ResTable_header>(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<uint8_t> id; if (pb_package.has_package_id()) { id = static_cast<uint8_t>(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<Value> 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> 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<Value> 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<Value> DeserializeValueFromPb(const pb::Value& pb_value, const pb::Array& pb_array = pb_compound_value.array(); std::unique_ptr<Array> array = util::make_unique<Array>(); for (const pb::Array_Element& pb_entry : pb_array.element()) { - std::unique_ptr<Item> item = - DeserializeItemFromPb(pb_entry.item(), src_pool, config, value_pool, out_error); + std::unique_ptr<Item> item = DeserializeItemFromPb(pb_entry.item(), src_pool, config, + value_pool, files, out_error); if (item == nullptr) { return {}; } @@ -697,8 +699,8 @@ std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value, std::unique_ptr<Plural> plural = util::make_unique<Plural>(); 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<Value> DeserializeValueFromPb(const pb::Value& pb_value, std::unique_ptr<Item> 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<Item> DeserializeItemFromPb(const pb::Item& pb_item, util::make_unique<FileReference>(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<Value> 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<Item> 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<xml::XmlResource> 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<io::IFileCollectionIterator>()); +}; + TEST(ProtoSerializeTest, SerializeSinglePackage) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); std::unique_ptr<ResourceTable> 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<Id>(&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<FileReference>(&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<StyledString>(&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<FileReference>(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<bool(const ResourceNameRef&, - const ConfigDescription& config, - FileReference*, FileReference*)>; - IAaptContext* context_; ResourceTable* master_table_; TableMergerOptions options_; ResourceTablePackage* master_package_; std::set<std::string> 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<FileReference> 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<ResourceTable> 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<ResourceTable> 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<FileReference>(&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<FileReference>(&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<XmlResource>* 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 <manifest> 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<ResourceTable> table = BuildTable(); - LoadedApk apk = {{"test.apk"}, {}, std::move(table)}; + LoadedApk apk = {{"test.apk"}, {}, std::move(table), {}}; std::unique_ptr<IAaptContext> 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<ResourceTable> table = BuildTable(); - LoadedApk apk = {{"test.apk"}, {}, std::move(table)}; + LoadedApk apk = {{"test.apk"}, {}, std::move(table), {}}; std::unique_ptr<IAaptContext> 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<ResourceTable> table = BuildTable(); - LoadedApk apk = {{"test.apk"}, {}, std::move(table)}; + LoadedApk apk = {{"test.apk"}, {}, std::move(table), {}}; std::unique_ptr<IAaptContext> 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<FileReference>(table_->string_pool.MakeRef(path))); + const StringPiece& path, + io::IFile* file) { + auto file_ref = util::make_unique<FileReference>(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<FileReference>(table_->string_pool.MakeRef(path))); + const ConfigDescription& config, + io::IFile* file) { + auto file_ref = util::make_unique<FileReference>(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> value); ResourceTableBuilder& AddValue(const android::StringPiece& name, const ResourceId& id, std::unique_ptr<Value> 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<XmlResource> Inflate(const void* data, size_t data_len, IDiagnostics* diag, - const Source& source) { +std::unique_ptr<XmlResource> 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<XmlResource> Inflate(const void* data, size_t data_len, IDiagnos std::unique_ptr<Element> 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<XmlResource> Inflate(const void* data, size_t data_len, IDiagnos return util::make_unique<XmlResource>(ResourceFile{}, std::move(string_pool), std::move(root)); } +std::unique_ptr<XmlResource> XmlResource::Clone() const { + std::unique_ptr<XmlResource> cloned = util::make_unique<XmlResource>(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<Node> 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<Attribute*>(static_cast<const Element*>(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<Element>(child.get())) { + return const_cast<Element*>(static_cast<const Element*>(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<Element>(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<Element*> 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<xml::Element> root; + + std::unique_ptr<XmlResource> 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<XmlResource> 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<XmlResource> Inflate(const void* data, size_t data_len, IDiagnostics* diag, - const Source& source); +// Inflates an XML DOM from a binary ResXMLTree. +std::unique_ptr<XmlResource> 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<XmlResource> new_doc = - Inflate(block.get(), buffer.size(), context->GetDiagnostics(), Source("test.xml")); + std::unique_ptr<XmlResource> new_doc = Inflate(block.get(), buffer.size(), nullptr); ASSERT_THAT(new_doc, NotNull()); EXPECT_THAT(new_doc->root->name, StrEq("Layout")); |