blob: 717e757a1bba6e4fc7dd0960dd90fce5cd0ab71b [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
Ryan Mitchell833a1a62018-07-10 13:51:36 -070017#include "Dump.h"
18
Adam Lesinski93190b72017-11-03 15:20:17 -070019#include <cinttypes>
Adam Lesinskice5e56e2016-10-21 17:56:45 -070020#include <vector>
21
Adam Lesinski93190b72017-11-03 15:20:17 -070022#include "android-base/stringprintf.h"
Adam Lesinskid5083f62017-01-16 15:07:21 -080023#include "androidfw/StringPiece.h"
24
Adam Lesinski59e04c62016-02-04 15:59:23 -080025#include "Debug.h"
26#include "Diagnostics.h"
Adam Lesinski00451162017-10-03 07:44:08 -070027#include "format/Container.h"
Adam Lesinski46708052017-09-29 14:49:15 -070028#include "format/binary/BinaryResourceParser.h"
29#include "format/proto/ProtoDeserialize.h"
Adam Lesinski00451162017-10-03 07:44:08 -070030#include "io/FileStream.h"
Adam Lesinski64587af2016-02-18 18:33:06 -080031#include "io/ZipArchive.h"
Adam Lesinski59e04c62016-02-04 15:59:23 -080032#include "process/IResourceTableConsumer.h"
Adam Lesinski93190b72017-11-03 15:20:17 -070033#include "text/Printer.h"
Adam Lesinski59e04c62016-02-04 15:59:23 -080034#include "util/Files.h"
Adam Lesinskid5083f62017-01-16 15:07:21 -080035
Adam Lesinski93190b72017-11-03 15:20:17 -070036using ::aapt::text::Printer;
Adam Lesinski4ffea042017-08-10 15:37:28 -070037using ::android::StringPiece;
Adam Lesinski93190b72017-11-03 15:20:17 -070038using ::android::base::StringPrintf;
Adam Lesinski59e04c62016-02-04 15:59:23 -080039
Adam Lesinski59e04c62016-02-04 15:59:23 -080040namespace aapt {
41
Adam Lesinski00451162017-10-03 07:44:08 -070042static const char* ResourceFileTypeToString(const ResourceFile::Type& type) {
43 switch (type) {
44 case ResourceFile::Type::kPng:
45 return "PNG";
46 case ResourceFile::Type::kBinaryXml:
47 return "BINARY_XML";
48 case ResourceFile::Type::kProtoXml:
49 return "PROTO_XML";
50 default:
51 break;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070052 }
Adam Lesinski00451162017-10-03 07:44:08 -070053 return "UNKNOWN";
Adam Lesinski59e04c62016-02-04 15:59:23 -080054}
55
Adam Lesinski00451162017-10-03 07:44:08 -070056static void DumpCompiledFile(const ResourceFile& file, const Source& source, off64_t offset,
Adam Lesinski93190b72017-11-03 15:20:17 -070057 size_t len, Printer* printer) {
58 printer->Print("Resource: ");
59 printer->Println(file.name.to_string());
60
61 printer->Print("Config: ");
62 printer->Println(file.config.to_string());
63
64 printer->Print("Source: ");
65 printer->Println(file.source.to_string());
66
67 printer->Print("Type: ");
68 printer->Println(ResourceFileTypeToString(file.type));
69
70 printer->Println(StringPrintf("Data: offset=%" PRIi64 " length=%zd", offset, len));
Adam Lesinski00451162017-10-03 07:44:08 -070071}
72
Adam Lesinskida9eba32018-02-13 16:44:10 -080073static bool DumpXmlFile(IAaptContext* context, io::IFile* file, bool proto,
74 text::Printer* printer) {
75 std::unique_ptr<xml::XmlResource> doc;
76 if (proto) {
77 std::unique_ptr<io::InputStream> in = file->OpenInputStream();
78 if (in == nullptr) {
79 context->GetDiagnostics()->Error(DiagMessage() << "failed to open file");
80 return false;
81 }
82
83 io::ZeroCopyInputAdaptor adaptor(in.get());
84 pb::XmlNode pb_node;
85 if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
86 context->GetDiagnostics()->Error(DiagMessage() << "failed to parse file as proto XML");
87 return false;
88 }
89
90 std::string err;
91 doc = DeserializeXmlResourceFromPb(pb_node, &err);
92 if (doc == nullptr) {
93 context->GetDiagnostics()->Error(DiagMessage() << "failed to deserialize proto XML");
94 return false;
95 }
96 printer->Println("Proto XML");
97 } else {
98 std::unique_ptr<io::IData> data = file->OpenAsData();
99 if (data == nullptr) {
100 context->GetDiagnostics()->Error(DiagMessage() << "failed to open file");
101 return false;
102 }
103
104 std::string err;
105 doc = xml::Inflate(data->data(), data->size(), &err);
106 if (doc == nullptr) {
107 context->GetDiagnostics()->Error(DiagMessage() << "failed to parse file as binary XML");
108 return false;
109 }
110 printer->Println("Binary XML");
111 }
112
113 Debug::DumpXml(*doc, printer);
114 return true;
115}
116
Adam Lesinski71be7052017-12-12 16:48:07 -0800117static bool TryDumpFile(IAaptContext* context, const std::string& file_path,
Adam Lesinskida9eba32018-02-13 16:44:10 -0800118 const DumpOptions& options) {
Adam Lesinski93190b72017-11-03 15:20:17 -0700119 // Use a smaller buffer so that there is less latency for dumping to stdout.
120 constexpr size_t kStdOutBufferSize = 1024u;
121 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
122 Printer printer(&fout);
123
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700124 std::string err;
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700125 std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(file_path, &err);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700126 if (zip) {
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700127 ResourceTable table;
Adam Lesinskida9eba32018-02-13 16:44:10 -0800128 bool proto = false;
Adam Lesinskie59f0d82017-10-13 09:36:53 -0700129 if (io::IFile* file = zip->FindFile("resources.pb")) {
Adam Lesinskida9eba32018-02-13 16:44:10 -0800130 proto = true;
131
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700132 std::unique_ptr<io::IData> data = file->OpenAsData();
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700133 if (data == nullptr) {
Adam Lesinskie59f0d82017-10-13 09:36:53 -0700134 context->GetDiagnostics()->Error(DiagMessage(file_path) << "failed to open resources.pb");
Pierre Lecesneaadf27e2017-05-05 14:58:21 +0100135 return false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700136 }
Adam Lesinski64587af2016-02-18 18:33:06 -0800137
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700138 pb::ResourceTable pb_table;
139 if (!pb_table.ParseFromArray(data->data(), data->size())) {
Adam Lesinskie59f0d82017-10-13 09:36:53 -0700140 context->GetDiagnostics()->Error(DiagMessage(file_path) << "invalid 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 Lesinski8780eb62017-10-31 17:44:39 -0700144 if (!DeserializeTableFromPb(pb_table, zip.get(), &table, &err)) {
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700145 context->GetDiagnostics()->Error(DiagMessage(file_path)
146 << "failed to parse table: " << err);
147 return false;
148 }
149 } else if (io::IFile* file = zip->FindFile("resources.arsc")) {
150 std::unique_ptr<io::IData> data = file->OpenAsData();
151 if (!data) {
152 context->GetDiagnostics()->Error(DiagMessage(file_path) << "failed to open resources.arsc");
153 return false;
154 }
155
Adam Lesinski8780eb62017-10-31 17:44:39 -0700156 BinaryResourceParser parser(context->GetDiagnostics(), &table, Source(file_path),
157 data->data(), data->size());
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700158 if (!parser.Parse()) {
Pierre Lecesneaadf27e2017-05-05 14:58:21 +0100159 return false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700160 }
Adam Lesinski64587af2016-02-18 18:33:06 -0800161 }
162
Adam Lesinskida9eba32018-02-13 16:44:10 -0800163 if (!options.file_to_dump_path) {
164 if (proto) {
165 printer.Println("Proto APK");
166 } else {
167 printer.Println("Binary APK");
168 }
169 Debug::PrintTable(table, options.print_options, &printer);
170 return true;
171 }
172
173 io::IFile* file = zip->FindFile(options.file_to_dump_path.value());
174 if (file == nullptr) {
175 context->GetDiagnostics()->Error(DiagMessage(file_path)
176 << "file '" << options.file_to_dump_path.value()
177 << "' not found in APK");
178 return false;
179 }
180 return DumpXmlFile(context, file, proto, &printer);
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700181 }
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700182
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700183 err.clear();
184
Adam Lesinski00451162017-10-03 07:44:08 -0700185 io::FileInputStream input(file_path);
186 if (input.HadError()) {
187 context->GetDiagnostics()->Error(DiagMessage(file_path)
188 << "failed to open file: " << input.GetError());
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700189 return false;
190 }
191
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700192 // Try as a compiled file.
Adam Lesinski00451162017-10-03 07:44:08 -0700193 ContainerReader reader(&input);
194 if (reader.HadError()) {
195 context->GetDiagnostics()->Error(DiagMessage(file_path)
196 << "failed to read container: " << reader.GetError());
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700197 return false;
198 }
199
Adam Lesinski93190b72017-11-03 15:20:17 -0700200 printer.Println("AAPT2 Container (APC)");
Adam Lesinski00451162017-10-03 07:44:08 -0700201 ContainerReaderEntry* entry;
202 while ((entry = reader.Next()) != nullptr) {
203 if (entry->Type() == ContainerEntryType::kResTable) {
Adam Lesinski93190b72017-11-03 15:20:17 -0700204 printer.Println("kResTable");
205
Adam Lesinski00451162017-10-03 07:44:08 -0700206 pb::ResourceTable pb_table;
207 if (!entry->GetResTable(&pb_table)) {
208 context->GetDiagnostics()->Error(DiagMessage(file_path)
209 << "failed to parse proto table: " << entry->GetError());
210 continue;
211 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800212
Adam Lesinski00451162017-10-03 07:44:08 -0700213 ResourceTable table;
214 err.clear();
Adam Lesinski8780eb62017-10-31 17:44:39 -0700215 if (!DeserializeTableFromPb(pb_table, nullptr /*files*/, &table, &err)) {
Adam Lesinski00451162017-10-03 07:44:08 -0700216 context->GetDiagnostics()->Error(DiagMessage(file_path)
217 << "failed to parse table: " << err);
218 continue;
219 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700220
Adam Lesinski93190b72017-11-03 15:20:17 -0700221 printer.Indent();
Adam Lesinskida9eba32018-02-13 16:44:10 -0800222 Debug::PrintTable(table, options.print_options, &printer);
Adam Lesinski93190b72017-11-03 15:20:17 -0700223 printer.Undent();
Adam Lesinski00451162017-10-03 07:44:08 -0700224 } else if (entry->Type() == ContainerEntryType::kResFile) {
Adam Lesinski93190b72017-11-03 15:20:17 -0700225 printer.Println("kResFile");
Adam Lesinski00451162017-10-03 07:44:08 -0700226 pb::internal::CompiledFile pb_compiled_file;
227 off64_t offset;
228 size_t length;
229 if (!entry->GetResFileOffsets(&pb_compiled_file, &offset, &length)) {
230 context->GetDiagnostics()->Error(
231 DiagMessage(file_path) << "failed to parse compiled proto file: " << entry->GetError());
232 continue;
233 }
234
235 ResourceFile file;
236 std::string error;
237 if (!DeserializeCompiledFileFromPb(pb_compiled_file, &file, &error)) {
238 context->GetDiagnostics()->Warn(DiagMessage(file_path)
239 << "failed to parse compiled file: " << error);
240 continue;
241 }
242
Adam Lesinski93190b72017-11-03 15:20:17 -0700243 printer.Indent();
244 DumpCompiledFile(file, Source(file_path), offset, length, &printer);
245 printer.Undent();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700246 }
247 }
Pierre Lecesneaadf27e2017-05-05 14:58:21 +0100248 return true;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800249}
250
Adam Lesinski00451162017-10-03 07:44:08 -0700251namespace {
252
Adam Lesinski59e04c62016-02-04 15:59:23 -0800253class DumpContext : public IAaptContext {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700254 public:
Adam Lesinskib522f042017-04-21 16:57:59 -0700255 PackageType GetPackageType() override {
256 // Doesn't matter.
257 return PackageType::kApp;
258 }
259
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700260 IDiagnostics* GetDiagnostics() override {
261 return &diagnostics_;
262 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800263
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700264 NameMangler* GetNameMangler() override {
Adam Lesinski00451162017-10-03 07:44:08 -0700265 UNIMPLEMENTED(FATAL);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700266 return nullptr;
267 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800268
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700269 const std::string& GetCompilationPackage() override {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700270 static std::string empty;
271 return empty;
272 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800273
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700274 uint8_t GetPackageId() override {
275 return 0;
276 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800277
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700278 SymbolTable* GetExternalSymbols() override {
Adam Lesinski00451162017-10-03 07:44:08 -0700279 UNIMPLEMENTED(FATAL);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700280 return nullptr;
281 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800282
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700283 bool IsVerbose() override {
284 return verbose_;
285 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800286
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700287 void SetVerbose(bool val) {
288 verbose_ = val;
289 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800290
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700291 int GetMinSdkVersion() override {
292 return 0;
293 }
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700294
Chris Warrington481f0272018-02-06 14:03:39 +0000295 bool IsAutoNamespace() override {
296 return false;
297 }
298
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700299 private:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700300 StdErrDiagnostics diagnostics_;
301 bool verbose_ = false;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800302};
303
Adam Lesinski00451162017-10-03 07:44:08 -0700304} // namespace
305
Ryan Mitchell833a1a62018-07-10 13:51:36 -0700306int DumpCommand::Action(const std::vector<std::string>& args) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700307 DumpContext context;
Ryan Mitchell833a1a62018-07-10 13:51:36 -0700308 context.SetVerbose(verbose_);
309 options_.print_options.show_sources = true;
310 options_.print_options.show_values = !no_values_;
311 for (const std::string& arg : args) {
312 if (!TryDumpFile(&context, arg, options_)) {
Pierre Lecesneaadf27e2017-05-05 14:58:21 +0100313 return 1;
314 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700315 }
316 return 0;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800317}
318
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700319} // namespace aapt