Loading tools/aapt2/Debug.cpp +263 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ #include "Debug.h" #include <androidfw/TypeWrappers.h> #include <format/binary/ResChunkPullParser.h> #include <algorithm> #include <map> #include <memory> Loading @@ -23,17 +26,16 @@ #include <set> #include <vector> #include "android-base/logging.h" #include "android-base/stringprintf.h" #include "ResourceTable.h" #include "ResourceUtils.h" #include "ResourceValues.h" #include "ValueVisitor.h" #include "android-base/logging.h" #include "android-base/stringprintf.h" #include "idmap2/Policies.h" #include "text/Printer.h" #include "util/Util.h" #include "idmap2/Policies.h" using ::aapt::text::Printer; using ::android::StringPiece; using ::android::base::StringPrintf; Loading Loading @@ -584,4 +586,260 @@ void Debug::DumpOverlayable(const ResourceTable& table, text::Printer* printer) } } namespace { using namespace android; class ChunkPrinter { public: ChunkPrinter(const void* data, size_t len, Printer* printer, IDiagnostics* diag) : data_(data), data_len_(len), printer_(printer), diag_(diag) { } void PrintChunkHeader(const ResChunk_header* chunk) { switch (util::DeviceToHost16(chunk->type)) { case RES_STRING_POOL_TYPE: printer_->Print("[RES_STRING_POOL_TYPE]"); break; case RES_TABLE_LIBRARY_TYPE: printer_->Print("[RES_TABLE_LIBRARY_TYPE]"); break; case RES_TABLE_TYPE: printer_->Print("[ResTable_header]"); break; case RES_TABLE_PACKAGE_TYPE: printer_->Print("[ResTable_package]"); break; case RES_TABLE_TYPE_TYPE: printer_->Print("[ResTable_type]"); break; case RES_TABLE_TYPE_SPEC_TYPE: printer_->Print("[RES_TABLE_TYPE_SPEC_TYPE]"); break; default: break; } printer_->Print(StringPrintf(" chunkSize: %u", util::DeviceToHost32(chunk->size))); printer_->Print(StringPrintf(" headerSize: %u", util::DeviceToHost32(chunk->headerSize))); } bool PrintTable(const ResTable_header* chunk) { printer_->Print( StringPrintf(" Package count: %u\n", util::DeviceToHost32(chunk->packageCount))); // Print the chunks contained within the table printer_->Indent(); bool success = PrintChunk( ResChunkPullParser(GetChunkData(&chunk->header), GetChunkDataLen(&chunk->header))); printer_->Undent(); return success; } void PrintResValue(const Res_value* value, const ConfigDescription& config, const ResourceType* type) { printer_->Print("[Res_value]"); printer_->Print(StringPrintf(" size: %u", util::DeviceToHost32(value->size))); printer_->Print(StringPrintf(" dataType: 0x%02x", util::DeviceToHost32(value->dataType))); printer_->Print(StringPrintf(" data: 0x%08x", util::DeviceToHost32(value->data))); if (type) { auto item = ResourceUtils::ParseBinaryResValue(*type, config, value_pool_, *value, &out_pool_); printer_->Print(" ("); item->PrettyPrint(printer_); printer_->Print(")"); } printer_->Print("\n"); } bool PrintTableType(const ResTable_type* chunk) { printer_->Print(StringPrintf(" id: 0x%02x", util::DeviceToHost32(chunk->id))); printer_->Print(StringPrintf( " name: %s", util::GetString(type_pool_, util::DeviceToHost32(chunk->id) - 1).c_str())); printer_->Print(StringPrintf(" flags: 0x%02x", util::DeviceToHost32(chunk->flags))); printer_->Print(StringPrintf(" entryCount: %u", util::DeviceToHost32(chunk->entryCount))); printer_->Print(StringPrintf(" entryStart: %u", util::DeviceToHost32(chunk->entriesStart))); ConfigDescription config; config.copyFromDtoH(chunk->config); printer_->Print(StringPrintf(" config: %s\n", config.to_string().c_str())); const ResourceType* type = ParseResourceType(util::GetString(type_pool_, util::DeviceToHost32(chunk->id) - 1)); printer_->Indent(); TypeVariant tv(chunk); for (auto it = tv.beginEntries(); it != tv.endEntries(); ++it) { const ResTable_entry* entry = *it; if (!entry) { continue; } printer_->Print((entry->flags & ResTable_entry::FLAG_COMPLEX) ? "[ResTable_map_entry]" : "[ResTable_entry]"); printer_->Print(StringPrintf(" id: 0x%04x", it.index())); printer_->Print(StringPrintf( " name: %s", util::GetString(key_pool_, util::DeviceToHost32(entry->key.index)).c_str())); printer_->Print(StringPrintf(" keyIndex: %u", util::DeviceToHost32(entry->key.index))); printer_->Print(StringPrintf(" size: %u", util::DeviceToHost32(entry->size))); printer_->Print(StringPrintf(" flags: 0x%04x", util::DeviceToHost32(entry->flags))); printer_->Indent(); if (entry->flags & ResTable_entry::FLAG_COMPLEX) { auto map_entry = (const ResTable_map_entry*)entry; printer_->Print(StringPrintf(" count: 0x%04x", util::DeviceToHost32(map_entry->count))); printer_->Print( StringPrintf(" parent: 0x%08x\n", util::DeviceToHost32(map_entry->parent.ident))); // Print the name and value mappings auto maps = (const ResTable_map*)((const uint8_t*)entry + util::DeviceToHost32(entry->size)); for (size_t i = 0, count = util::DeviceToHost32(map_entry->count); i < count; i++) { PrintResValue(&(maps[i].value), config, type); printer_->Print(StringPrintf( " name: %s name-id:%d\n", util::GetString(key_pool_, util::DeviceToHost32(maps[i].name.ident)).c_str(), util::DeviceToHost32(maps[i].name.ident))); } } else { printer_->Print("\n"); // Print the value of the entry auto value = (const Res_value*)((const uint8_t*)entry + util::DeviceToHost32(entry->size)); PrintResValue(value, config, type); } printer_->Undent(); } printer_->Undent(); return true; } void PrintStringPool(const ResStringPool_header* chunk) { // Initialize the string pools ResStringPool* pool; if (value_pool_.getError() == NO_INIT) { pool = &value_pool_; } else if (type_pool_.getError() == NO_INIT) { pool = &type_pool_; } else if (key_pool_.getError() == NO_INIT) { pool = &key_pool_; } else { return; } pool->setTo(chunk, util::DeviceToHost32((reinterpret_cast<const ResChunk_header*>(chunk))->size)); printer_->Print("\n"); for (size_t i = 0; i < pool->size(); i++) { printer_->Print(StringPrintf("#%zd : %s\n", i, util::GetString(*pool, i).c_str())); } } bool PrintPackage(const ResTable_package* chunk) { printer_->Print(StringPrintf(" id: 0x%02x", util::DeviceToHost32(chunk->id))); size_t len = strnlen16((const char16_t*)chunk->name, std::size(chunk->name)); std::u16string package_name(len, u'\0'); package_name.resize(len); for (size_t i = 0; i < len; i++) { package_name[i] = util::DeviceToHost16(chunk->name[i]); } printer_->Print(StringPrintf("name: %s", String8(package_name.c_str()).c_str())); printer_->Print(StringPrintf(" typeStrings: %u", util::DeviceToHost32(chunk->typeStrings))); printer_->Print( StringPrintf(" lastPublicType: %u", util::DeviceToHost32(chunk->lastPublicType))); printer_->Print(StringPrintf(" keyStrings: %u", util::DeviceToHost32(chunk->keyStrings))); printer_->Print(StringPrintf(" lastPublicKey: %u", util::DeviceToHost32(chunk->lastPublicKey))); printer_->Print(StringPrintf(" typeIdOffset: %u\n", util::DeviceToHost32(chunk->typeIdOffset))); // Print the chunks contained within the table printer_->Indent(); bool success = PrintChunk( ResChunkPullParser(GetChunkData(&chunk->header), GetChunkDataLen(&chunk->header))); printer_->Undent(); return success; } bool PrintChunk(ResChunkPullParser&& parser) { while (ResChunkPullParser::IsGoodEvent(parser.Next())) { auto chunk = parser.chunk(); PrintChunkHeader(chunk); switch (util::DeviceToHost16(chunk->type)) { case RES_STRING_POOL_TYPE: PrintStringPool(reinterpret_cast<const ResStringPool_header*>(chunk)); break; case RES_TABLE_TYPE: PrintTable(reinterpret_cast<const ResTable_header*>(chunk)); break; case RES_TABLE_PACKAGE_TYPE: type_pool_.uninit(); key_pool_.uninit(); PrintPackage(reinterpret_cast<const ResTable_package*>(chunk)); break; case RES_TABLE_TYPE_TYPE: PrintTableType(reinterpret_cast<const ResTable_type*>(chunk)); break; default: printer_->Print("\n"); break; } } if (parser.event() == ResChunkPullParser::Event::kBadDocument) { diag_->Error(DiagMessage(source_) << "corrupt resource table: " << parser.error()); return false; } return true; } void Print() { PrintChunk(ResChunkPullParser(data_, data_len_)); printer_->Print("[End]\n"); } private: const Source source_; const void* data_; const size_t data_len_; Printer* printer_; IDiagnostics* diag_; // The standard value string pool for resource values. ResStringPool value_pool_; // The string pool that holds the names of the types defined // in this table. ResStringPool type_pool_; // The string pool that holds the names of the entries defined // in this table. ResStringPool key_pool_; StringPool out_pool_; }; } // namespace void Debug::DumpChunks(const void* data, size_t len, Printer* printer, IDiagnostics* diag) { ChunkPrinter chunk_printer(data, len, printer, diag); chunk_printer.Print(); } } // namespace aapt tools/aapt2/Debug.h +1 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ struct Debug { static void DumpXml(const xml::XmlResource& doc, text::Printer* printer); static void DumpResStringPool(const android::ResStringPool* pool, text::Printer* printer); static void DumpOverlayable(const ResourceTable& table, text::Printer* printer); static void DumpChunks(const void* data, size_t len, text::Printer* printer, IDiagnostics* diag); }; } // namespace aapt Loading tools/aapt2/cmd/Dump.cpp +17 −0 Original line number Diff line number Diff line Loading @@ -560,4 +560,21 @@ const char DumpBadgerCommand::kBadgerData[2925] = { 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10}; int DumpChunks::Dump(LoadedApk* apk) { auto file = apk->GetFileCollection()->FindFile("resources.arsc"); if (!file) { GetDiagnostics()->Error(DiagMessage() << "Failed to find resources.arsc in APK"); return 1; } auto data = file->OpenAsData(); if (!data) { GetDiagnostics()->Error(DiagMessage() << "Failed to open resources.arsc "); return 1; } Debug::DumpChunks(data->data(), data->size(), GetPrinter(), GetDiagnostics()); return 0; } } // namespace aapt tools/aapt2/cmd/Dump.h +14 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,9 @@ #ifndef AAPT2_DUMP_H #define AAPT2_DUMP_H #include <io/FileStream.h> #include <io/ZipArchive.h> #include "Command.h" #include "Debug.h" #include "LoadedApk.h" Loading Loading @@ -226,6 +229,16 @@ class DumpXmlStringsCommand : public DumpApkCommand { std::vector<std::string> files_; }; class DumpChunks : public DumpApkCommand { public: DumpChunks(text::Printer* printer, IDiagnostics* diag) : DumpApkCommand("chunks", printer, diag) { SetDescription("Print the chunk information of the compiled resources.arsc in the APK."); } int Dump(LoadedApk* apk) override; }; /** Prints the tree of a compiled xml in an APK. */ class DumpXmlTreeCommand : public DumpApkCommand { public: explicit DumpXmlTreeCommand(text::Printer* printer, IDiagnostics* diag) Loading Loading @@ -263,6 +276,7 @@ class DumpCommand : public Command { AddOptionalSubcommand(util::make_unique<DumpStringsCommand>(printer, diag_)); AddOptionalSubcommand(util::make_unique<DumpStyleParentCommand>(printer, diag_)); AddOptionalSubcommand(util::make_unique<DumpTableCommand>(printer, diag_)); AddOptionalSubcommand(util::make_unique<DumpChunks>(printer, diag_)); AddOptionalSubcommand(util::make_unique<DumpXmlStringsCommand>(printer, diag_)); AddOptionalSubcommand(util::make_unique<DumpXmlTreeCommand>(printer, diag_)); AddOptionalSubcommand(util::make_unique<DumpOverlayableCommand>(printer, diag_)); Loading Loading
tools/aapt2/Debug.cpp +263 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ #include "Debug.h" #include <androidfw/TypeWrappers.h> #include <format/binary/ResChunkPullParser.h> #include <algorithm> #include <map> #include <memory> Loading @@ -23,17 +26,16 @@ #include <set> #include <vector> #include "android-base/logging.h" #include "android-base/stringprintf.h" #include "ResourceTable.h" #include "ResourceUtils.h" #include "ResourceValues.h" #include "ValueVisitor.h" #include "android-base/logging.h" #include "android-base/stringprintf.h" #include "idmap2/Policies.h" #include "text/Printer.h" #include "util/Util.h" #include "idmap2/Policies.h" using ::aapt::text::Printer; using ::android::StringPiece; using ::android::base::StringPrintf; Loading Loading @@ -584,4 +586,260 @@ void Debug::DumpOverlayable(const ResourceTable& table, text::Printer* printer) } } namespace { using namespace android; class ChunkPrinter { public: ChunkPrinter(const void* data, size_t len, Printer* printer, IDiagnostics* diag) : data_(data), data_len_(len), printer_(printer), diag_(diag) { } void PrintChunkHeader(const ResChunk_header* chunk) { switch (util::DeviceToHost16(chunk->type)) { case RES_STRING_POOL_TYPE: printer_->Print("[RES_STRING_POOL_TYPE]"); break; case RES_TABLE_LIBRARY_TYPE: printer_->Print("[RES_TABLE_LIBRARY_TYPE]"); break; case RES_TABLE_TYPE: printer_->Print("[ResTable_header]"); break; case RES_TABLE_PACKAGE_TYPE: printer_->Print("[ResTable_package]"); break; case RES_TABLE_TYPE_TYPE: printer_->Print("[ResTable_type]"); break; case RES_TABLE_TYPE_SPEC_TYPE: printer_->Print("[RES_TABLE_TYPE_SPEC_TYPE]"); break; default: break; } printer_->Print(StringPrintf(" chunkSize: %u", util::DeviceToHost32(chunk->size))); printer_->Print(StringPrintf(" headerSize: %u", util::DeviceToHost32(chunk->headerSize))); } bool PrintTable(const ResTable_header* chunk) { printer_->Print( StringPrintf(" Package count: %u\n", util::DeviceToHost32(chunk->packageCount))); // Print the chunks contained within the table printer_->Indent(); bool success = PrintChunk( ResChunkPullParser(GetChunkData(&chunk->header), GetChunkDataLen(&chunk->header))); printer_->Undent(); return success; } void PrintResValue(const Res_value* value, const ConfigDescription& config, const ResourceType* type) { printer_->Print("[Res_value]"); printer_->Print(StringPrintf(" size: %u", util::DeviceToHost32(value->size))); printer_->Print(StringPrintf(" dataType: 0x%02x", util::DeviceToHost32(value->dataType))); printer_->Print(StringPrintf(" data: 0x%08x", util::DeviceToHost32(value->data))); if (type) { auto item = ResourceUtils::ParseBinaryResValue(*type, config, value_pool_, *value, &out_pool_); printer_->Print(" ("); item->PrettyPrint(printer_); printer_->Print(")"); } printer_->Print("\n"); } bool PrintTableType(const ResTable_type* chunk) { printer_->Print(StringPrintf(" id: 0x%02x", util::DeviceToHost32(chunk->id))); printer_->Print(StringPrintf( " name: %s", util::GetString(type_pool_, util::DeviceToHost32(chunk->id) - 1).c_str())); printer_->Print(StringPrintf(" flags: 0x%02x", util::DeviceToHost32(chunk->flags))); printer_->Print(StringPrintf(" entryCount: %u", util::DeviceToHost32(chunk->entryCount))); printer_->Print(StringPrintf(" entryStart: %u", util::DeviceToHost32(chunk->entriesStart))); ConfigDescription config; config.copyFromDtoH(chunk->config); printer_->Print(StringPrintf(" config: %s\n", config.to_string().c_str())); const ResourceType* type = ParseResourceType(util::GetString(type_pool_, util::DeviceToHost32(chunk->id) - 1)); printer_->Indent(); TypeVariant tv(chunk); for (auto it = tv.beginEntries(); it != tv.endEntries(); ++it) { const ResTable_entry* entry = *it; if (!entry) { continue; } printer_->Print((entry->flags & ResTable_entry::FLAG_COMPLEX) ? "[ResTable_map_entry]" : "[ResTable_entry]"); printer_->Print(StringPrintf(" id: 0x%04x", it.index())); printer_->Print(StringPrintf( " name: %s", util::GetString(key_pool_, util::DeviceToHost32(entry->key.index)).c_str())); printer_->Print(StringPrintf(" keyIndex: %u", util::DeviceToHost32(entry->key.index))); printer_->Print(StringPrintf(" size: %u", util::DeviceToHost32(entry->size))); printer_->Print(StringPrintf(" flags: 0x%04x", util::DeviceToHost32(entry->flags))); printer_->Indent(); if (entry->flags & ResTable_entry::FLAG_COMPLEX) { auto map_entry = (const ResTable_map_entry*)entry; printer_->Print(StringPrintf(" count: 0x%04x", util::DeviceToHost32(map_entry->count))); printer_->Print( StringPrintf(" parent: 0x%08x\n", util::DeviceToHost32(map_entry->parent.ident))); // Print the name and value mappings auto maps = (const ResTable_map*)((const uint8_t*)entry + util::DeviceToHost32(entry->size)); for (size_t i = 0, count = util::DeviceToHost32(map_entry->count); i < count; i++) { PrintResValue(&(maps[i].value), config, type); printer_->Print(StringPrintf( " name: %s name-id:%d\n", util::GetString(key_pool_, util::DeviceToHost32(maps[i].name.ident)).c_str(), util::DeviceToHost32(maps[i].name.ident))); } } else { printer_->Print("\n"); // Print the value of the entry auto value = (const Res_value*)((const uint8_t*)entry + util::DeviceToHost32(entry->size)); PrintResValue(value, config, type); } printer_->Undent(); } printer_->Undent(); return true; } void PrintStringPool(const ResStringPool_header* chunk) { // Initialize the string pools ResStringPool* pool; if (value_pool_.getError() == NO_INIT) { pool = &value_pool_; } else if (type_pool_.getError() == NO_INIT) { pool = &type_pool_; } else if (key_pool_.getError() == NO_INIT) { pool = &key_pool_; } else { return; } pool->setTo(chunk, util::DeviceToHost32((reinterpret_cast<const ResChunk_header*>(chunk))->size)); printer_->Print("\n"); for (size_t i = 0; i < pool->size(); i++) { printer_->Print(StringPrintf("#%zd : %s\n", i, util::GetString(*pool, i).c_str())); } } bool PrintPackage(const ResTable_package* chunk) { printer_->Print(StringPrintf(" id: 0x%02x", util::DeviceToHost32(chunk->id))); size_t len = strnlen16((const char16_t*)chunk->name, std::size(chunk->name)); std::u16string package_name(len, u'\0'); package_name.resize(len); for (size_t i = 0; i < len; i++) { package_name[i] = util::DeviceToHost16(chunk->name[i]); } printer_->Print(StringPrintf("name: %s", String8(package_name.c_str()).c_str())); printer_->Print(StringPrintf(" typeStrings: %u", util::DeviceToHost32(chunk->typeStrings))); printer_->Print( StringPrintf(" lastPublicType: %u", util::DeviceToHost32(chunk->lastPublicType))); printer_->Print(StringPrintf(" keyStrings: %u", util::DeviceToHost32(chunk->keyStrings))); printer_->Print(StringPrintf(" lastPublicKey: %u", util::DeviceToHost32(chunk->lastPublicKey))); printer_->Print(StringPrintf(" typeIdOffset: %u\n", util::DeviceToHost32(chunk->typeIdOffset))); // Print the chunks contained within the table printer_->Indent(); bool success = PrintChunk( ResChunkPullParser(GetChunkData(&chunk->header), GetChunkDataLen(&chunk->header))); printer_->Undent(); return success; } bool PrintChunk(ResChunkPullParser&& parser) { while (ResChunkPullParser::IsGoodEvent(parser.Next())) { auto chunk = parser.chunk(); PrintChunkHeader(chunk); switch (util::DeviceToHost16(chunk->type)) { case RES_STRING_POOL_TYPE: PrintStringPool(reinterpret_cast<const ResStringPool_header*>(chunk)); break; case RES_TABLE_TYPE: PrintTable(reinterpret_cast<const ResTable_header*>(chunk)); break; case RES_TABLE_PACKAGE_TYPE: type_pool_.uninit(); key_pool_.uninit(); PrintPackage(reinterpret_cast<const ResTable_package*>(chunk)); break; case RES_TABLE_TYPE_TYPE: PrintTableType(reinterpret_cast<const ResTable_type*>(chunk)); break; default: printer_->Print("\n"); break; } } if (parser.event() == ResChunkPullParser::Event::kBadDocument) { diag_->Error(DiagMessage(source_) << "corrupt resource table: " << parser.error()); return false; } return true; } void Print() { PrintChunk(ResChunkPullParser(data_, data_len_)); printer_->Print("[End]\n"); } private: const Source source_; const void* data_; const size_t data_len_; Printer* printer_; IDiagnostics* diag_; // The standard value string pool for resource values. ResStringPool value_pool_; // The string pool that holds the names of the types defined // in this table. ResStringPool type_pool_; // The string pool that holds the names of the entries defined // in this table. ResStringPool key_pool_; StringPool out_pool_; }; } // namespace void Debug::DumpChunks(const void* data, size_t len, Printer* printer, IDiagnostics* diag) { ChunkPrinter chunk_printer(data, len, printer, diag); chunk_printer.Print(); } } // namespace aapt
tools/aapt2/Debug.h +1 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ struct Debug { static void DumpXml(const xml::XmlResource& doc, text::Printer* printer); static void DumpResStringPool(const android::ResStringPool* pool, text::Printer* printer); static void DumpOverlayable(const ResourceTable& table, text::Printer* printer); static void DumpChunks(const void* data, size_t len, text::Printer* printer, IDiagnostics* diag); }; } // namespace aapt Loading
tools/aapt2/cmd/Dump.cpp +17 −0 Original line number Diff line number Diff line Loading @@ -560,4 +560,21 @@ const char DumpBadgerCommand::kBadgerData[2925] = { 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10}; int DumpChunks::Dump(LoadedApk* apk) { auto file = apk->GetFileCollection()->FindFile("resources.arsc"); if (!file) { GetDiagnostics()->Error(DiagMessage() << "Failed to find resources.arsc in APK"); return 1; } auto data = file->OpenAsData(); if (!data) { GetDiagnostics()->Error(DiagMessage() << "Failed to open resources.arsc "); return 1; } Debug::DumpChunks(data->data(), data->size(), GetPrinter(), GetDiagnostics()); return 0; } } // namespace aapt
tools/aapt2/cmd/Dump.h +14 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,9 @@ #ifndef AAPT2_DUMP_H #define AAPT2_DUMP_H #include <io/FileStream.h> #include <io/ZipArchive.h> #include "Command.h" #include "Debug.h" #include "LoadedApk.h" Loading Loading @@ -226,6 +229,16 @@ class DumpXmlStringsCommand : public DumpApkCommand { std::vector<std::string> files_; }; class DumpChunks : public DumpApkCommand { public: DumpChunks(text::Printer* printer, IDiagnostics* diag) : DumpApkCommand("chunks", printer, diag) { SetDescription("Print the chunk information of the compiled resources.arsc in the APK."); } int Dump(LoadedApk* apk) override; }; /** Prints the tree of a compiled xml in an APK. */ class DumpXmlTreeCommand : public DumpApkCommand { public: explicit DumpXmlTreeCommand(text::Printer* printer, IDiagnostics* diag) Loading Loading @@ -263,6 +276,7 @@ class DumpCommand : public Command { AddOptionalSubcommand(util::make_unique<DumpStringsCommand>(printer, diag_)); AddOptionalSubcommand(util::make_unique<DumpStyleParentCommand>(printer, diag_)); AddOptionalSubcommand(util::make_unique<DumpTableCommand>(printer, diag_)); AddOptionalSubcommand(util::make_unique<DumpChunks>(printer, diag_)); AddOptionalSubcommand(util::make_unique<DumpXmlStringsCommand>(printer, diag_)); AddOptionalSubcommand(util::make_unique<DumpXmlTreeCommand>(printer, diag_)); AddOptionalSubcommand(util::make_unique<DumpOverlayableCommand>(printer, diag_)); Loading