/* * Copyright (C) 2016 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 "Dump.h" #include #include #include "android-base/stringprintf.h" #include "androidfw/StringPiece.h" #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" #include "process/IResourceTableConsumer.h" #include "text/Printer.h" #include "util/Files.h" using ::aapt::text::Printer; using ::android::StringPiece; using ::android::base::StringPrintf; namespace aapt { static const char* ResourceFileTypeToString(const ResourceFile::Type& type) { switch (type) { case ResourceFile::Type::kPng: return "PNG"; case ResourceFile::Type::kBinaryXml: return "BINARY_XML"; case ResourceFile::Type::kProtoXml: return "PROTO_XML"; default: break; } return "UNKNOWN"; } static void DumpCompiledFile(const ResourceFile& file, const Source& source, off64_t offset, size_t len, Printer* printer) { printer->Print("Resource: "); printer->Println(file.name.to_string()); printer->Print("Config: "); printer->Println(file.config.to_string()); printer->Print("Source: "); printer->Println(file.source.to_string()); printer->Print("Type: "); printer->Println(ResourceFileTypeToString(file.type)); printer->Println(StringPrintf("Data: offset=%" PRIi64 " length=%zd", offset, len)); } namespace { class DumpContext : public IAaptContext { public: PackageType GetPackageType() override { // Doesn't matter. return PackageType::kApp; } IDiagnostics* GetDiagnostics() override { return &diagnostics_; } NameMangler* GetNameMangler() override { UNIMPLEMENTED(FATAL); return nullptr; } const std::string& GetCompilationPackage() override { static std::string empty; return empty; } uint8_t GetPackageId() override { return 0; } SymbolTable* GetExternalSymbols() override { UNIMPLEMENTED(FATAL); return nullptr; } bool IsVerbose() override { return verbose_; } void SetVerbose(bool val) { verbose_ = val; } int GetMinSdkVersion() override { return 0; } private: StdErrDiagnostics diagnostics_; bool verbose_ = false; }; } // namespace // 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& 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& 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; } ResourceTable* table = loaded_apk->GetResourceTable(); if (!table) { diag_->Error(DiagMessage() << "Failed to retrieve resource table."); 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 configs(compare); for (auto& package : table->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& args) { DumpContext context; 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; } ResourceTable* table = loaded_apk->GetResourceTable(); if (!table) { diag_->Error(DiagMessage() << "Failed to retrieve resource table."); return 1; } // Load the run-time xml string pool using the flattened data BigBuffer buffer(4096); StringPool::FlattenUtf8(&buffer, table->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& 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() == ApkFormat::kProto) { printer.Println("Proto APK"); } else { printer.Println("Binary APK"); } ResourceTable* table = loaded_apk->GetResourceTable(); if (!table) { diag_->Error(DiagMessage() << "Failed to retrieve resource table."); return 1; } Debug::PrintTable(*table, print_options, &printer); } return 0; } int DumpXmlTreeCommand::Action(const std::vector& 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& 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() == ApkFormat::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() == ApkFormat::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 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; } int DumpPackageNameCommand::Action(const std::vector& 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); xml::Element* manifest_el = loaded_apk->GetManifest()->root.get(); if (!manifest_el) { diag_->Error(DiagMessage() << "No AndroidManifest."); return 1; } xml::Attribute* attr = manifest_el->FindAttribute({}, "package"); if (!attr) { diag_->Error(DiagMessage() << "No package name."); return 1; } printer.Println(StringPrintf("%s", attr->value.c_str())); return 0; } /** Preform no action because a subcommand is required. */ int DumpCommand::Action(const std::vector& 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