blob: 8e7e5e59bc31018306f3f338787e355cbf7c81b1 [file] [log] [blame]
Adam Lesinski59e04c62016-02-04 15:59:23 -08001/*
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
Adam Lesinski93190b72017-11-03 15:20:17 -070017#include <cinttypes>
Adam Lesinskice5e56e2016-10-21 17:56:45 -070018#include <vector>
19
Adam Lesinski93190b72017-11-03 15:20:17 -070020#include "android-base/stringprintf.h"
Adam Lesinskid5083f62017-01-16 15:07:21 -080021#include "androidfw/StringPiece.h"
22
Adam Lesinski59e04c62016-02-04 15:59:23 -080023#include "Debug.h"
24#include "Diagnostics.h"
25#include "Flags.h"
Adam Lesinski00451162017-10-03 07:44:08 -070026#include "format/Container.h"
Adam Lesinski46708052017-09-29 14:49:15 -070027#include "format/binary/BinaryResourceParser.h"
28#include "format/proto/ProtoDeserialize.h"
Adam Lesinski00451162017-10-03 07:44:08 -070029#include "io/FileStream.h"
Adam Lesinski64587af2016-02-18 18:33:06 -080030#include "io/ZipArchive.h"
Adam Lesinski59e04c62016-02-04 15:59:23 -080031#include "process/IResourceTableConsumer.h"
Adam Lesinski93190b72017-11-03 15:20:17 -070032#include "text/Printer.h"
Adam Lesinski59e04c62016-02-04 15:59:23 -080033#include "util/Files.h"
Adam Lesinskid5083f62017-01-16 15:07:21 -080034
Adam Lesinski93190b72017-11-03 15:20:17 -070035using ::aapt::text::Printer;
Adam Lesinski4ffea042017-08-10 15:37:28 -070036using ::android::StringPiece;
Adam Lesinski93190b72017-11-03 15:20:17 -070037using ::android::base::StringPrintf;
Adam Lesinski59e04c62016-02-04 15:59:23 -080038
Adam Lesinski59e04c62016-02-04 15:59:23 -080039namespace aapt {
40
Adam Lesinskida9eba32018-02-13 16:44:10 -080041struct 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
Adam Lesinski00451162017-10-03 07:44:08 -070048static 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;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070058 }
Adam Lesinski00451162017-10-03 07:44:08 -070059 return "UNKNOWN";
Adam Lesinski59e04c62016-02-04 15:59:23 -080060}
61
Adam Lesinski00451162017-10-03 07:44:08 -070062static void DumpCompiledFile(const ResourceFile& file, const Source& source, off64_t offset,
Adam Lesinski93190b72017-11-03 15:20:17 -070063 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));
Adam Lesinski00451162017-10-03 07:44:08 -070077}
78
Adam Lesinskida9eba32018-02-13 16:44:10 -080079static 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
Adam Lesinski71be7052017-12-12 16:48:07 -0800123static bool TryDumpFile(IAaptContext* context, const std::string& file_path,
Adam Lesinskida9eba32018-02-13 16:44:10 -0800124 const DumpOptions& options) {
Adam Lesinski93190b72017-11-03 15:20:17 -0700125 // 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
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700130 std::string err;
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700131 std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(file_path, &err);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700132 if (zip) {
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700133 ResourceTable table;
Adam Lesinskida9eba32018-02-13 16:44:10 -0800134 bool proto = false;
Adam Lesinskie59f0d82017-10-13 09:36:53 -0700135 if (io::IFile* file = zip->FindFile("resources.pb")) {
Adam Lesinskida9eba32018-02-13 16:44:10 -0800136 proto = true;
137
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700138 std::unique_ptr<io::IData> data = file->OpenAsData();
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700139 if (data == nullptr) {
Adam Lesinskie59f0d82017-10-13 09:36:53 -0700140 context->GetDiagnostics()->Error(DiagMessage(file_path) << "failed to open resources.pb");
Pierre Lecesneaadf27e2017-05-05 14:58:21 +0100141 return false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700142 }
Adam Lesinski64587af2016-02-18 18:33:06 -0800143
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700144 pb::ResourceTable pb_table;
145 if (!pb_table.ParseFromArray(data->data(), data->size())) {
Adam Lesinskie59f0d82017-10-13 09:36:53 -0700146 context->GetDiagnostics()->Error(DiagMessage(file_path) << "invalid resources.pb");
Pierre Lecesneaadf27e2017-05-05 14:58:21 +0100147 return false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700148 }
Adam Lesinski64587af2016-02-18 18:33:06 -0800149
Adam Lesinski8780eb62017-10-31 17:44:39 -0700150 if (!DeserializeTableFromPb(pb_table, zip.get(), &table, &err)) {
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700151 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
Adam Lesinski8780eb62017-10-31 17:44:39 -0700162 BinaryResourceParser parser(context->GetDiagnostics(), &table, Source(file_path),
163 data->data(), data->size());
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700164 if (!parser.Parse()) {
Pierre Lecesneaadf27e2017-05-05 14:58:21 +0100165 return false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700166 }
Adam Lesinski64587af2016-02-18 18:33:06 -0800167 }
168
Adam Lesinskida9eba32018-02-13 16:44:10 -0800169 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);
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700187 }
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700188
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700189 err.clear();
190
Adam Lesinski00451162017-10-03 07:44:08 -0700191 io::FileInputStream input(file_path);
192 if (input.HadError()) {
193 context->GetDiagnostics()->Error(DiagMessage(file_path)
194 << "failed to open file: " << input.GetError());
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700195 return false;
196 }
197
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700198 // Try as a compiled file.
Adam Lesinski00451162017-10-03 07:44:08 -0700199 ContainerReader reader(&input);
200 if (reader.HadError()) {
201 context->GetDiagnostics()->Error(DiagMessage(file_path)
202 << "failed to read container: " << reader.GetError());
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700203 return false;
204 }
205
Adam Lesinski93190b72017-11-03 15:20:17 -0700206 printer.Println("AAPT2 Container (APC)");
Adam Lesinski00451162017-10-03 07:44:08 -0700207 ContainerReaderEntry* entry;
208 while ((entry = reader.Next()) != nullptr) {
209 if (entry->Type() == ContainerEntryType::kResTable) {
Adam Lesinski93190b72017-11-03 15:20:17 -0700210 printer.Println("kResTable");
211
Adam Lesinski00451162017-10-03 07:44:08 -0700212 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 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800218
Adam Lesinski00451162017-10-03 07:44:08 -0700219 ResourceTable table;
220 err.clear();
Adam Lesinski8780eb62017-10-31 17:44:39 -0700221 if (!DeserializeTableFromPb(pb_table, nullptr /*files*/, &table, &err)) {
Adam Lesinski00451162017-10-03 07:44:08 -0700222 context->GetDiagnostics()->Error(DiagMessage(file_path)
223 << "failed to parse table: " << err);
224 continue;
225 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700226
Adam Lesinski93190b72017-11-03 15:20:17 -0700227 printer.Indent();
Adam Lesinskida9eba32018-02-13 16:44:10 -0800228 Debug::PrintTable(table, options.print_options, &printer);
Adam Lesinski93190b72017-11-03 15:20:17 -0700229 printer.Undent();
Adam Lesinski00451162017-10-03 07:44:08 -0700230 } else if (entry->Type() == ContainerEntryType::kResFile) {
Adam Lesinski93190b72017-11-03 15:20:17 -0700231 printer.Println("kResFile");
Adam Lesinski00451162017-10-03 07:44:08 -0700232 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
Adam Lesinski93190b72017-11-03 15:20:17 -0700249 printer.Indent();
250 DumpCompiledFile(file, Source(file_path), offset, length, &printer);
251 printer.Undent();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700252 }
253 }
Pierre Lecesneaadf27e2017-05-05 14:58:21 +0100254 return true;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800255}
256
Adam Lesinski00451162017-10-03 07:44:08 -0700257namespace {
258
Adam Lesinski59e04c62016-02-04 15:59:23 -0800259class DumpContext : public IAaptContext {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700260 public:
Adam Lesinskib522f042017-04-21 16:57:59 -0700261 PackageType GetPackageType() override {
262 // Doesn't matter.
263 return PackageType::kApp;
264 }
265
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700266 IDiagnostics* GetDiagnostics() override {
267 return &diagnostics_;
268 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800269
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700270 NameMangler* GetNameMangler() override {
Adam Lesinski00451162017-10-03 07:44:08 -0700271 UNIMPLEMENTED(FATAL);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700272 return nullptr;
273 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800274
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700275 const std::string& GetCompilationPackage() override {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700276 static std::string empty;
277 return empty;
278 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800279
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700280 uint8_t GetPackageId() override {
281 return 0;
282 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800283
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700284 SymbolTable* GetExternalSymbols() override {
Adam Lesinski00451162017-10-03 07:44:08 -0700285 UNIMPLEMENTED(FATAL);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700286 return nullptr;
287 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800288
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700289 bool IsVerbose() override {
290 return verbose_;
291 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800292
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700293 void SetVerbose(bool val) {
294 verbose_ = val;
295 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800296
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700297 int GetMinSdkVersion() override {
298 return 0;
299 }
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700300
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700301 private:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700302 StdErrDiagnostics diagnostics_;
303 bool verbose_ = false;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800304};
305
Adam Lesinski00451162017-10-03 07:44:08 -0700306} // namespace
307
308// Entry point for dump command.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700309int Dump(const std::vector<StringPiece>& args) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700310 bool verbose = false;
Adam Lesinski71be7052017-12-12 16:48:07 -0800311 bool no_values = false;
Adam Lesinskida9eba32018-02-13 16:44:10 -0800312 DumpOptions options;
Adam Lesinski71be7052017-12-12 16:48:07 -0800313 Flags flags = Flags()
314 .OptionalSwitch("--no-values",
315 "Suppresses output of values when displaying resource tables.",
316 &no_values)
Adam Lesinskida9eba32018-02-13 16:44:10 -0800317 .OptionalFlag("--file", "Dumps the specified file from the APK passed as arg.",
318 &options.file_to_dump_path)
Adam Lesinski71be7052017-12-12 16:48:07 -0800319 .OptionalSwitch("-v", "increase verbosity of output", &verbose);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700320 if (!flags.Parse("aapt2 dump", args, &std::cerr)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700321 return 1;
322 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800323
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700324 DumpContext context;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700325 context.SetVerbose(verbose);
Adam Lesinski59e04c62016-02-04 15:59:23 -0800326
Adam Lesinskida9eba32018-02-13 16:44:10 -0800327 options.print_options.show_sources = true;
328 options.print_options.show_values = !no_values;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700329 for (const std::string& arg : flags.GetArgs()) {
Adam Lesinskida9eba32018-02-13 16:44:10 -0800330 if (!TryDumpFile(&context, arg, options)) {
Pierre Lecesneaadf27e2017-05-05 14:58:21 +0100331 return 1;
332 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700333 }
334 return 0;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800335}
336
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700337} // namespace aapt