diff options
Diffstat (limited to 'tools/aapt2/cmd/Dump.cpp')
-rw-r--r-- | tools/aapt2/cmd/Dump.cpp | 470 |
1 files changed, 282 insertions, 188 deletions
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp index 717e757a1bba..b4311c56428b 100644 --- a/tools/aapt2/cmd/Dump.cpp +++ b/tools/aapt2/cmd/Dump.cpp @@ -24,8 +24,11 @@ #include "Debug.h" #include "Diagnostics.h" +#include "LoadedApk.h" +#include "Util.h" #include "format/Container.h" #include "format/binary/BinaryResourceParser.h" +#include "format/binary/XmlFlattener.h" #include "format/proto/ProtoDeserialize.h" #include "io/FileStream.h" #include "io/ZipArchive.h" @@ -70,184 +73,6 @@ static void DumpCompiledFile(const ResourceFile& file, const Source& source, off printer->Println(StringPrintf("Data: offset=%" PRIi64 " length=%zd", offset, len)); } -static bool DumpXmlFile(IAaptContext* context, io::IFile* file, bool proto, - text::Printer* printer) { - std::unique_ptr<xml::XmlResource> doc; - if (proto) { - std::unique_ptr<io::InputStream> in = file->OpenInputStream(); - if (in == nullptr) { - context->GetDiagnostics()->Error(DiagMessage() << "failed to open file"); - return false; - } - - io::ZeroCopyInputAdaptor adaptor(in.get()); - pb::XmlNode pb_node; - if (!pb_node.ParseFromZeroCopyStream(&adaptor)) { - context->GetDiagnostics()->Error(DiagMessage() << "failed to parse file as proto XML"); - return false; - } - - std::string err; - doc = DeserializeXmlResourceFromPb(pb_node, &err); - if (doc == nullptr) { - context->GetDiagnostics()->Error(DiagMessage() << "failed to deserialize proto XML"); - return false; - } - printer->Println("Proto XML"); - } else { - std::unique_ptr<io::IData> data = file->OpenAsData(); - if (data == nullptr) { - context->GetDiagnostics()->Error(DiagMessage() << "failed to open file"); - return false; - } - - std::string err; - doc = xml::Inflate(data->data(), data->size(), &err); - if (doc == nullptr) { - context->GetDiagnostics()->Error(DiagMessage() << "failed to parse file as binary XML"); - return false; - } - printer->Println("Binary XML"); - } - - Debug::DumpXml(*doc, printer); - return true; -} - -static bool TryDumpFile(IAaptContext* context, const std::string& file_path, - const DumpOptions& options) { - // Use a smaller buffer so that there is less latency for dumping to stdout. - constexpr size_t kStdOutBufferSize = 1024u; - io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize); - Printer printer(&fout); - - std::string err; - std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(file_path, &err); - if (zip) { - ResourceTable table; - bool proto = false; - if (io::IFile* file = zip->FindFile("resources.pb")) { - proto = true; - - std::unique_ptr<io::IData> data = file->OpenAsData(); - if (data == nullptr) { - context->GetDiagnostics()->Error(DiagMessage(file_path) << "failed to open resources.pb"); - return false; - } - - pb::ResourceTable pb_table; - if (!pb_table.ParseFromArray(data->data(), data->size())) { - context->GetDiagnostics()->Error(DiagMessage(file_path) << "invalid resources.pb"); - return false; - } - - if (!DeserializeTableFromPb(pb_table, zip.get(), &table, &err)) { - context->GetDiagnostics()->Error(DiagMessage(file_path) - << "failed to parse table: " << err); - return false; - } - } else if (io::IFile* file = zip->FindFile("resources.arsc")) { - std::unique_ptr<io::IData> data = file->OpenAsData(); - if (!data) { - context->GetDiagnostics()->Error(DiagMessage(file_path) << "failed to open resources.arsc"); - return false; - } - - BinaryResourceParser parser(context->GetDiagnostics(), &table, Source(file_path), - data->data(), data->size()); - if (!parser.Parse()) { - return false; - } - } - - if (!options.file_to_dump_path) { - if (proto) { - printer.Println("Proto APK"); - } else { - printer.Println("Binary APK"); - } - Debug::PrintTable(table, options.print_options, &printer); - return true; - } - - io::IFile* file = zip->FindFile(options.file_to_dump_path.value()); - if (file == nullptr) { - context->GetDiagnostics()->Error(DiagMessage(file_path) - << "file '" << options.file_to_dump_path.value() - << "' not found in APK"); - return false; - } - return DumpXmlFile(context, file, proto, &printer); - } - - err.clear(); - - io::FileInputStream input(file_path); - if (input.HadError()) { - context->GetDiagnostics()->Error(DiagMessage(file_path) - << "failed to open file: " << input.GetError()); - return false; - } - - // Try as a compiled file. - ContainerReader reader(&input); - if (reader.HadError()) { - context->GetDiagnostics()->Error(DiagMessage(file_path) - << "failed to read container: " << reader.GetError()); - return false; - } - - printer.Println("AAPT2 Container (APC)"); - ContainerReaderEntry* entry; - while ((entry = reader.Next()) != nullptr) { - if (entry->Type() == ContainerEntryType::kResTable) { - printer.Println("kResTable"); - - pb::ResourceTable pb_table; - if (!entry->GetResTable(&pb_table)) { - context->GetDiagnostics()->Error(DiagMessage(file_path) - << "failed to parse proto table: " << entry->GetError()); - continue; - } - - ResourceTable table; - err.clear(); - if (!DeserializeTableFromPb(pb_table, nullptr /*files*/, &table, &err)) { - context->GetDiagnostics()->Error(DiagMessage(file_path) - << "failed to parse table: " << err); - continue; - } - - printer.Indent(); - Debug::PrintTable(table, options.print_options, &printer); - printer.Undent(); - } else if (entry->Type() == ContainerEntryType::kResFile) { - printer.Println("kResFile"); - pb::internal::CompiledFile pb_compiled_file; - off64_t offset; - size_t length; - if (!entry->GetResFileOffsets(&pb_compiled_file, &offset, &length)) { - context->GetDiagnostics()->Error( - DiagMessage(file_path) << "failed to parse compiled proto file: " << entry->GetError()); - continue; - } - - ResourceFile file; - std::string error; - if (!DeserializeCompiledFileFromPb(pb_compiled_file, &file, &error)) { - context->GetDiagnostics()->Warn(DiagMessage(file_path) - << "failed to parse compiled file: " << error); - continue; - } - - printer.Indent(); - DumpCompiledFile(file, Source(file_path), offset, length, &printer); - printer.Undent(); - } - } - return true; -} - namespace { class DumpContext : public IAaptContext { @@ -292,10 +117,6 @@ class DumpContext : public IAaptContext { return 0; } - bool IsAutoNamespace() override { - return false; - } - private: StdErrDiagnostics diagnostics_; bool verbose_ = false; @@ -303,17 +124,290 @@ class DumpContext : public IAaptContext { } // namespace -int DumpCommand::Action(const std::vector<std::string>& args) { +// Use a smaller buffer so that there is less latency for dumping to stdout. +constexpr size_t kStdOutBufferSize = 1024u; + +int DumpAPCCommand::Action(const std::vector<std::string>& args) { + DumpContext context; + DebugPrintTableOptions print_options; + print_options.show_sources = true; + print_options.show_values = !no_values_; + + if (args.size() < 1) { + diag_->Error(DiagMessage() << "No dump container specified."); + return 1; + } + + io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize); + Printer printer(&fout); + + for (auto container : args) { + io::FileInputStream input(container); + if (input.HadError()) { + context.GetDiagnostics()->Error(DiagMessage(container) + << "failed to open file: " << input.GetError()); + return false; + } + + // Try as a compiled file. + ContainerReader reader(&input); + if (reader.HadError()) { + context.GetDiagnostics()->Error(DiagMessage(container) + << "failed to read container: " << reader.GetError()); + return false; + } + + printer.Println("AAPT2 Container (APC)"); + ContainerReaderEntry* entry; + std::string error; + while ((entry = reader.Next()) != nullptr) { + if (entry->Type() == ContainerEntryType::kResTable) { + printer.Println("kResTable"); + + pb::ResourceTable pb_table; + if (!entry->GetResTable(&pb_table)) { + context.GetDiagnostics()->Error(DiagMessage(container) + << "failed to parse proto table: " + << entry->GetError()); + continue; + } + + ResourceTable table; + error.clear(); + if (!DeserializeTableFromPb(pb_table, nullptr /*files*/, &table, &error)) { + context.GetDiagnostics()->Error(DiagMessage(container) + << "failed to parse table: " << error); + continue; + } + + printer.Indent(); + Debug::PrintTable(table, print_options, &printer); + printer.Undent(); + } else if (entry->Type() == ContainerEntryType::kResFile) { + printer.Println("kResFile"); + pb::internal::CompiledFile pb_compiled_file; + off64_t offset; + size_t length; + if (!entry->GetResFileOffsets(&pb_compiled_file, &offset, &length)) { + context.GetDiagnostics()->Error( + DiagMessage(container) << "failed to parse compiled proto file: " + << entry->GetError()); + continue; + } + + ResourceFile file; + if (!DeserializeCompiledFileFromPb(pb_compiled_file, &file, &error)) { + context.GetDiagnostics()->Warn(DiagMessage(container) + << "failed to parse compiled file: " << error); + continue; + } + + printer.Indent(); + DumpCompiledFile(file, Source(container), offset, length, &printer); + printer.Undent(); + } + } + } + + return 0; +} + +int DumpConfigsCommand::Action(const std::vector<std::string>& args) { + if (args.size() < 1) { + diag_->Error(DiagMessage() << "No dump apk specified."); + return 1; + } + + auto loaded_apk = LoadedApk::LoadApkFromPath(args[0], diag_); + if (!loaded_apk) { + return 1; + } + + io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize); + Printer printer(&fout); + + // Comparison function used to order configurations + auto compare = [](ConfigDescription c1, ConfigDescription c2) -> bool { + return c1.compare(c2) < 0; + }; + + // Insert the configurations into a set in order to keep every configuarion seen + std::set<ConfigDescription, decltype(compare)> configs(compare); + for (auto& package : loaded_apk->GetResourceTable()->packages) { + for (auto& type : package->types) { + for (auto& entry : type->entries) { + for (auto& value : entry->values) { + configs.insert(value->config); + } + } + } + } + + // Print the configurations in order + for (auto& config : configs) { + printer.Print(StringPrintf("%s\n", config.to_string().data())); + } + + return 0; +} + +int DumpStringsCommand::Action(const std::vector<std::string>& args) { DumpContext context; - context.SetVerbose(verbose_); - options_.print_options.show_sources = true; - options_.print_options.show_values = !no_values_; - for (const std::string& arg : args) { - if (!TryDumpFile(&context, arg, options_)) { + if (args.size() < 1) { + diag_->Error(DiagMessage() << "No dump apk specified."); + return 1; + } + + io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize); + Printer printer(&fout); + + for (auto apk : args) { + auto loaded_apk = LoadedApk::LoadApkFromPath(apk, diag_); + if (!loaded_apk) { return 1; } + + // Load the run-time xml string pool using the flattened data + BigBuffer buffer(4096); + StringPool::FlattenUtf8(&buffer, loaded_apk->GetResourceTable()->string_pool, + context.GetDiagnostics()); + auto data = buffer.to_string(); + android::ResStringPool pool(data.data(), data.size(), false); + Debug::DumpResStringPool(&pool, &printer); } + return 0; } +int DumpTableCommand::Action(const std::vector<std::string>& args) { + if (args.size() < 1) { + diag_->Error(DiagMessage() << "No dump apk specified."); + return 1; + } + + io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize); + Printer printer(&fout); + + DebugPrintTableOptions print_options; + print_options.show_sources = true; + print_options.show_values = !no_values_; + + for (auto apk : args) { + auto loaded_apk = LoadedApk::LoadApkFromPath(apk, diag_); + if (!loaded_apk) { + return 1; + } + + if (loaded_apk->GetApkFormat()) { + printer.Println("Proto APK"); + } else { + printer.Println("Binary APK"); + } + + Debug::PrintTable(*loaded_apk->GetResourceTable(), print_options, &printer); + } + + return 0; +} + +int DumpXmlTreeCommand::Action(const std::vector<std::string>& args) { + if (args.size() < 1) { + diag_->Error(DiagMessage() << "No dump apk specified"); + return 1; + } + + auto loaded_apk = LoadedApk::LoadApkFromPath(args[0], diag_); + if (!loaded_apk) { + return 1; + } + + io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize); + Printer printer(&fout); + + // Dump the xml tree of every passed in file + for (auto file : files_) { + auto xml = loaded_apk->LoadXml(file, diag_); + if (!xml) { + return 1; + } + + Debug::DumpXml(*xml, &printer); + } + + return 0; +} + +int DumpXmlStringsCommand::Action(const std::vector<std::string>& args) { + DumpContext context; + if (args.size() < 1) { + diag_->Error(DiagMessage() << "No dump apk specified."); + return 1; + } + + auto loaded_apk = LoadedApk::LoadApkFromPath(args[0], diag_); + if (!loaded_apk) { + return 1; + } + + io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize); + Printer printer(&fout); + + // Dump the xml strings of every passed in file + for (auto xml_file : files_) { + android::ResXMLTree tree; + + if (loaded_apk->GetApkFormat() == kProto) { + auto xml = loaded_apk->LoadXml(xml_file, diag_); + if (!xml) { + return 1; + } + + // Flatten the xml document to get a binary representation of the proto xml file + BigBuffer buffer(4096); + XmlFlattenerOptions options = {}; + options.keep_raw_values = true; + XmlFlattener flattener(&buffer, options); + if (!flattener.Consume(&context, xml.get())) { + return 1; + } + + // Load the run-time xml tree using the flattened data + std::string data = buffer.to_string(); + tree.setTo(data.data(), data.size(), /** copyData */ true); + + } else if (loaded_apk->GetApkFormat() == kBinary) { + io::IFile* file = loaded_apk->GetFileCollection()->FindFile(xml_file); + if (!file) { + diag_->Error(DiagMessage(xml_file) << "file '" << xml_file << "' not found in APK"); + return 1; + } + + std::unique_ptr<io::IData> data = file->OpenAsData(); + if (!data) { + diag_->Error(DiagMessage() << "failed to open file"); + return 1; + } + + // Load the run-time xml tree from the file data + tree.setTo(data->data(), data->size(), /** copyData */ true); + } + + Debug::DumpResStringPool(&tree.getStrings(), &printer); + } + + return 0; +} + +/** Preform no action because a subcommand is required. */ +int DumpCommand::Action(const std::vector<std::string>& args) { + if (args.size() == 0) { + diag_->Error(DiagMessage() << "no subcommand specified"); + } else { + diag_->Error(DiagMessage() << "unknown subcommand '" << args[0] << "'"); + } + + Usage(&std::cerr); + return 1; +} + } // namespace aapt |