Home | History | Annotate | Download | only in cmd
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <cinttypes>
     18 #include <vector>
     19 
     20 #include "android-base/stringprintf.h"
     21 #include "androidfw/StringPiece.h"
     22 
     23 #include "Debug.h"
     24 #include "Diagnostics.h"
     25 #include "Flags.h"
     26 #include "format/Container.h"
     27 #include "format/binary/BinaryResourceParser.h"
     28 #include "format/proto/ProtoDeserialize.h"
     29 #include "io/FileStream.h"
     30 #include "io/ZipArchive.h"
     31 #include "process/IResourceTableConsumer.h"
     32 #include "text/Printer.h"
     33 #include "util/Files.h"
     34 
     35 using ::aapt::text::Printer;
     36 using ::android::StringPiece;
     37 using ::android::base::StringPrintf;
     38 
     39 namespace aapt {
     40 
     41 struct DumpOptions {
     42   DebugPrintTableOptions print_options;
     43 
     44   // The path to a file within an APK to dump.
     45   Maybe<std::string> file_to_dump_path;
     46 };
     47 
     48 static const char* ResourceFileTypeToString(const ResourceFile::Type& type) {
     49   switch (type) {
     50     case ResourceFile::Type::kPng:
     51       return "PNG";
     52     case ResourceFile::Type::kBinaryXml:
     53       return "BINARY_XML";
     54     case ResourceFile::Type::kProtoXml:
     55       return "PROTO_XML";
     56     default:
     57       break;
     58   }
     59   return "UNKNOWN";
     60 }
     61 
     62 static void DumpCompiledFile(const ResourceFile& file, const Source& source, off64_t offset,
     63                              size_t len, Printer* printer) {
     64   printer->Print("Resource: ");
     65   printer->Println(file.name.to_string());
     66 
     67   printer->Print("Config:   ");
     68   printer->Println(file.config.to_string());
     69 
     70   printer->Print("Source:   ");
     71   printer->Println(file.source.to_string());
     72 
     73   printer->Print("Type:     ");
     74   printer->Println(ResourceFileTypeToString(file.type));
     75 
     76   printer->Println(StringPrintf("Data:     offset=%" PRIi64 " length=%zd", offset, len));
     77 }
     78 
     79 static bool DumpXmlFile(IAaptContext* context, io::IFile* file, bool proto,
     80                         text::Printer* printer) {
     81   std::unique_ptr<xml::XmlResource> doc;
     82   if (proto) {
     83     std::unique_ptr<io::InputStream> in = file->OpenInputStream();
     84     if (in == nullptr) {
     85       context->GetDiagnostics()->Error(DiagMessage() << "failed to open file");
     86       return false;
     87     }
     88 
     89     io::ZeroCopyInputAdaptor adaptor(in.get());
     90     pb::XmlNode pb_node;
     91     if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
     92       context->GetDiagnostics()->Error(DiagMessage() << "failed to parse file as proto XML");
     93       return false;
     94     }
     95 
     96     std::string err;
     97     doc = DeserializeXmlResourceFromPb(pb_node, &err);
     98     if (doc == nullptr) {
     99       context->GetDiagnostics()->Error(DiagMessage() << "failed to deserialize proto XML");
    100       return false;
    101     }
    102     printer->Println("Proto XML");
    103   } else {
    104     std::unique_ptr<io::IData> data = file->OpenAsData();
    105     if (data == nullptr) {
    106       context->GetDiagnostics()->Error(DiagMessage() << "failed to open file");
    107       return false;
    108     }
    109 
    110     std::string err;
    111     doc = xml::Inflate(data->data(), data->size(), &err);
    112     if (doc == nullptr) {
    113       context->GetDiagnostics()->Error(DiagMessage() << "failed to parse file as binary XML");
    114       return false;
    115     }
    116     printer->Println("Binary XML");
    117   }
    118 
    119   Debug::DumpXml(*doc, printer);
    120   return true;
    121 }
    122 
    123 static bool TryDumpFile(IAaptContext* context, const std::string& file_path,
    124                         const DumpOptions& options) {
    125   // Use a smaller buffer so that there is less latency for dumping to stdout.
    126   constexpr size_t kStdOutBufferSize = 1024u;
    127   io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
    128   Printer printer(&fout);
    129 
    130   std::string err;
    131   std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(file_path, &err);
    132   if (zip) {
    133     ResourceTable table;
    134     bool proto = false;
    135     if (io::IFile* file = zip->FindFile("resources.pb")) {
    136       proto = true;
    137 
    138       std::unique_ptr<io::IData> data = file->OpenAsData();
    139       if (data == nullptr) {
    140         context->GetDiagnostics()->Error(DiagMessage(file_path) << "failed to open resources.pb");
    141         return false;
    142       }
    143 
    144       pb::ResourceTable pb_table;
    145       if (!pb_table.ParseFromArray(data->data(), data->size())) {
    146         context->GetDiagnostics()->Error(DiagMessage(file_path) << "invalid resources.pb");
    147         return false;
    148       }
    149 
    150       if (!DeserializeTableFromPb(pb_table, zip.get(), &table, &err)) {
    151         context->GetDiagnostics()->Error(DiagMessage(file_path)
    152                                          << "failed to parse table: " << err);
    153         return false;
    154       }
    155     } else if (io::IFile* file = zip->FindFile("resources.arsc")) {
    156       std::unique_ptr<io::IData> data = file->OpenAsData();
    157       if (!data) {
    158         context->GetDiagnostics()->Error(DiagMessage(file_path) << "failed to open resources.arsc");
    159         return false;
    160       }
    161 
    162       BinaryResourceParser parser(context->GetDiagnostics(), &table, Source(file_path),
    163                                   data->data(), data->size());
    164       if (!parser.Parse()) {
    165         return false;
    166       }
    167     }
    168 
    169     if (!options.file_to_dump_path) {
    170       if (proto) {
    171         printer.Println("Proto APK");
    172       } else {
    173         printer.Println("Binary APK");
    174       }
    175       Debug::PrintTable(table, options.print_options, &printer);
    176       return true;
    177     }
    178 
    179     io::IFile* file = zip->FindFile(options.file_to_dump_path.value());
    180     if (file == nullptr) {
    181       context->GetDiagnostics()->Error(DiagMessage(file_path)
    182                                        << "file '" << options.file_to_dump_path.value()
    183                                        << "' not found in APK");
    184       return false;
    185     }
    186     return DumpXmlFile(context, file, proto, &printer);
    187   }
    188 
    189   err.clear();
    190 
    191   io::FileInputStream input(file_path);
    192   if (input.HadError()) {
    193     context->GetDiagnostics()->Error(DiagMessage(file_path)
    194                                      << "failed to open file: " << input.GetError());
    195     return false;
    196   }
    197 
    198   // Try as a compiled file.
    199   ContainerReader reader(&input);
    200   if (reader.HadError()) {
    201     context->GetDiagnostics()->Error(DiagMessage(file_path)
    202                                      << "failed to read container: " << reader.GetError());
    203     return false;
    204   }
    205 
    206   printer.Println("AAPT2 Container (APC)");
    207   ContainerReaderEntry* entry;
    208   while ((entry = reader.Next()) != nullptr) {
    209     if (entry->Type() == ContainerEntryType::kResTable) {
    210       printer.Println("kResTable");
    211 
    212       pb::ResourceTable pb_table;
    213       if (!entry->GetResTable(&pb_table)) {
    214         context->GetDiagnostics()->Error(DiagMessage(file_path)
    215                                          << "failed to parse proto table: " << entry->GetError());
    216         continue;
    217       }
    218 
    219       ResourceTable table;
    220       err.clear();
    221       if (!DeserializeTableFromPb(pb_table, nullptr /*files*/, &table, &err)) {
    222         context->GetDiagnostics()->Error(DiagMessage(file_path)
    223                                          << "failed to parse table: " << err);
    224         continue;
    225       }
    226 
    227       printer.Indent();
    228       Debug::PrintTable(table, options.print_options, &printer);
    229       printer.Undent();
    230     } else if (entry->Type() == ContainerEntryType::kResFile) {
    231       printer.Println("kResFile");
    232       pb::internal::CompiledFile pb_compiled_file;
    233       off64_t offset;
    234       size_t length;
    235       if (!entry->GetResFileOffsets(&pb_compiled_file, &offset, &length)) {
    236         context->GetDiagnostics()->Error(
    237             DiagMessage(file_path) << "failed to parse compiled proto file: " << entry->GetError());
    238         continue;
    239       }
    240 
    241       ResourceFile file;
    242       std::string error;
    243       if (!DeserializeCompiledFileFromPb(pb_compiled_file, &file, &error)) {
    244         context->GetDiagnostics()->Warn(DiagMessage(file_path)
    245                                         << "failed to parse compiled file: " << error);
    246         continue;
    247       }
    248 
    249       printer.Indent();
    250       DumpCompiledFile(file, Source(file_path), offset, length, &printer);
    251       printer.Undent();
    252     }
    253   }
    254   return true;
    255 }
    256 
    257 namespace {
    258 
    259 class DumpContext : public IAaptContext {
    260  public:
    261   PackageType GetPackageType() override {
    262     // Doesn't matter.
    263     return PackageType::kApp;
    264   }
    265 
    266   IDiagnostics* GetDiagnostics() override {
    267     return &diagnostics_;
    268   }
    269 
    270   NameMangler* GetNameMangler() override {
    271     UNIMPLEMENTED(FATAL);
    272     return nullptr;
    273   }
    274 
    275   const std::string& GetCompilationPackage() override {
    276     static std::string empty;
    277     return empty;
    278   }
    279 
    280   uint8_t GetPackageId() override {
    281     return 0;
    282   }
    283 
    284   SymbolTable* GetExternalSymbols() override {
    285     UNIMPLEMENTED(FATAL);
    286     return nullptr;
    287   }
    288 
    289   bool IsVerbose() override {
    290     return verbose_;
    291   }
    292 
    293   void SetVerbose(bool val) {
    294     verbose_ = val;
    295   }
    296 
    297   int GetMinSdkVersion() override {
    298     return 0;
    299   }
    300 
    301  private:
    302   StdErrDiagnostics diagnostics_;
    303   bool verbose_ = false;
    304 };
    305 
    306 }  // namespace
    307 
    308 // Entry point for dump command.
    309 int Dump(const std::vector<StringPiece>& args) {
    310   bool verbose = false;
    311   bool no_values = false;
    312   DumpOptions options;
    313   Flags flags = Flags()
    314                     .OptionalSwitch("--no-values",
    315                                     "Suppresses output of values when displaying resource tables.",
    316                                     &no_values)
    317                     .OptionalFlag("--file", "Dumps the specified file from the APK passed as arg.",
    318                                   &options.file_to_dump_path)
    319                     .OptionalSwitch("-v", "increase verbosity of output", &verbose);
    320   if (!flags.Parse("aapt2 dump", args, &std::cerr)) {
    321     return 1;
    322   }
    323 
    324   DumpContext context;
    325   context.SetVerbose(verbose);
    326 
    327   options.print_options.show_sources = true;
    328   options.print_options.show_values = !no_values;
    329   for (const std::string& arg : flags.GetArgs()) {
    330     if (!TryDumpFile(&context, arg, options)) {
    331       return 1;
    332     }
    333   }
    334   return 0;
    335 }
    336 
    337 }  // namespace aapt
    338