Merge "AAPT2: Add convert command"

This commit is contained in:
Adam Lesinski
2017-11-07 19:05:22 +00:00
committed by Android (Google) Code Review
29 changed files with 703 additions and 430 deletions

View File

@@ -16,6 +16,7 @@
toolSources = [
"cmd/Compile.cpp",
"cmd/Convert.cpp",
"cmd/Diff.cpp",
"cmd/Dump.cpp",
"cmd/Link.cpp",

View File

@@ -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 @@ 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

View File

@@ -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

View File

@@ -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;

228
tools/aapt2/cmd/Convert.cpp Normal file
View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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,
@@ -298,23 +295,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);
@@ -587,7 +567,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;
}
@@ -595,13 +575,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;
}
}
@@ -748,22 +730,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).
@@ -772,13 +761,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)
@@ -788,19 +779,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;
}
}
}
@@ -1194,46 +1180,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");
@@ -1254,13 +1212,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) {
@@ -1268,31 +1225,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()
@@ -1423,7 +1359,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;
@@ -1868,13 +1804,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_;

View File

@@ -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;
}

View File

@@ -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 {};
}

View File

@@ -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

View File

@@ -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 {};

View File

@@ -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_;

View File

@@ -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)) {

View File

@@ -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());

View File

@@ -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 {};
}

View File

@@ -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);

View File

@@ -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());

View File

@@ -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)

View File

@@ -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

View File

@@ -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);

View File

@@ -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 @@ 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) {

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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,

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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"));