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
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 058504d..ae67f61 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 ae32ee9..921d853 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"
+using ::aapt::io::IFile;
+using ::aapt::io::IFileCollection;
+using ::aapt::xml::XmlResource;
+using ::android::StringPiece;
+using ::std::unique_ptr;
+
namespace aapt {
-using xml::XmlResource;
-
-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 {};
}
- io::IFile* file = apk->FindFile("resources.arsc");
- if (!file) {
- context->GetDiagnostics()->Error(DiagMessage(source) << "no resources.arsc found");
+ 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 {};
}
- std::unique_ptr<io::IData> data = file->OpenAsData();
- if (!data) {
- context->GetDiagnostics()->Error(DiagMessage(source) << "could not open resources.arsc");
+ std::unique_ptr<io::InputStream> in = table_file->OpenInputStream();
+ if (in == nullptr) {
+ diag->Error(DiagMessage(source) << "failed to open " << kProtoResourceTablePath);
+ return {};
+ }
+
+ 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>();
+ 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(context, table.get(), source, data->data(), data->size(), apk.get());
+ 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 @@
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 d2dd5cf..ef97de3 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 @@
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 @@
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 @@
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 36ab30c..808b29c 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -50,7 +50,7 @@
}
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 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 @@
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 0000000..89ae9e8
--- /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 625c47c..fc1f1d6 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -357,8 +357,9 @@
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 090c3fb..bc8f1dc 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -78,7 +78,7 @@
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 @@
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 @@
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 55a4c43..26edb76 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
// 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 @@
}
// 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 @@
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 @@
// 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 44e148e..688b6a7 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -281,15 +281,14 @@
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 @@
}
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 708bed8..d39f43e8 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -214,9 +214,10 @@
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 @@
}
// 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 @@
}
// 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 @@
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 @@
}
// 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 @@
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 fd9b39c..7611c15 100644
--- a/tools/aapt2/cmd/Util.h
+++ b/tools/aapt2/cmd/Util.h
@@ -57,7 +57,8 @@
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 95eec4a..66510b0 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -73,29 +73,22 @@
} // 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 @@
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::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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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::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::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 @@
// 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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
// 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 dc9a384..052f806 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.h
+++ b/tools/aapt2/format/binary/BinaryResourceParser.h
@@ -39,7 +39,7 @@
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 @@
*/
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 57565a5..259f2e9 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -568,14 +568,6 @@
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 @@
// 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 6d75973..e11890b 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -71,7 +71,8 @@
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 @@
// 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 86bd865..0f0bce8 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -371,7 +371,8 @@
}
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 @@
}
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 @@
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 @@
}
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 @@
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 @@
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 @@
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<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<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 @@
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 @@
}
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 7dc54f2..0c581a1 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 @@
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 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 8efac8a..9649a4d 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -29,6 +29,12 @@
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 @@
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 @@
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 6ed07b0..94686c0 100644
--- a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
@@ -25,5 +25,4 @@
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 21c6b11..58d0607 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -37,17 +37,14 @@
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 @@
// 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 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 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 @@
}
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 @@
} 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 @@
->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 d024aa4..47e23dd 100644
--- a/tools/aapt2/link/TableMerger.h
+++ b/tools/aapt2/link/TableMerger.h
@@ -40,6 +40,9 @@
// 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 @@
// 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 @@
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 3499809..6aab8de 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::NotNull;
-using ::testing::UnorderedElementsAreArray;
-using ::testing::Pointee;
-using ::testing::Field;
using ::testing::Eq;
+using ::testing::Field;
+using ::testing::NotNull;
+using ::testing::Pointee;
+using ::testing::StrEq;
+using ::testing::UnorderedElementsAreArray;
namespace aapt {
@@ -67,10 +68,9 @@
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, 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 6803088..473693c 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator.cpp
@@ -265,12 +265,14 @@
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 c8f3524..30c9146 100644
--- a/tools/aapt2/optimize/MultiApkGenerator_test.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator_test.cpp
@@ -106,7 +106,7 @@
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, 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, 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 5a62e97..ecec63f 100644
--- a/tools/aapt2/test/Builders.cpp
+++ b/tools/aapt2/test/Builders.cpp
@@ -78,21 +78,27 @@
}
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 263fb55..4cdfc33 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -52,12 +52,15 @@
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 3522506..b0cf44a 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -258,8 +258,7 @@
}
}
-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<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 @@
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 @@
}
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 @@
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 063d7b9..cf06ba5 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -100,11 +100,21 @@
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 @@
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 34e6d3f..e5012d6 100644
--- a/tools/aapt2/xml/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -70,8 +70,7 @@
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"));