• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ResourceFileTypeToString(const ResourceFile::Type & type)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 
DumpCompiledFile(const ResourceFile & file,const Source & source,off64_t offset,size_t len,Printer * printer)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 
DumpXmlFile(IAaptContext * context,io::IFile * file,bool proto,text::Printer * printer)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 
TryDumpFile(IAaptContext * context,const std::string & file_path,const DumpOptions & options)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:
GetPackageType()261   PackageType GetPackageType() override {
262     // Doesn't matter.
263     return PackageType::kApp;
264   }
265 
GetDiagnostics()266   IDiagnostics* GetDiagnostics() override {
267     return &diagnostics_;
268   }
269 
GetNameMangler()270   NameMangler* GetNameMangler() override {
271     UNIMPLEMENTED(FATAL);
272     return nullptr;
273   }
274 
GetCompilationPackage()275   const std::string& GetCompilationPackage() override {
276     static std::string empty;
277     return empty;
278   }
279 
GetPackageId()280   uint8_t GetPackageId() override {
281     return 0;
282   }
283 
GetExternalSymbols()284   SymbolTable* GetExternalSymbols() override {
285     UNIMPLEMENTED(FATAL);
286     return nullptr;
287   }
288 
IsVerbose()289   bool IsVerbose() override {
290     return verbose_;
291   }
292 
SetVerbose(bool val)293   void SetVerbose(bool val) {
294     verbose_ = val;
295   }
296 
GetMinSdkVersion()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.
Dump(const std::vector<StringPiece> & args)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