blob: 6bb762aafb3bce937083e65da0767485417bcdc5 [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"
Todd Kennedy895e19e2018-08-24 12:26:06 -070026#include "LoadedApk.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 Lesinskida9eba32018-02-13 16:44:10 -080042struct DumpOptions {
43 DebugPrintTableOptions print_options;
44
45 // The path to a file within an APK to dump.
46 Maybe<std::string> file_to_dump_path;
47};
48
Adam Lesinski00451162017-10-03 07:44:08 -070049static const char* ResourceFileTypeToString(const ResourceFile::Type& type) {
50 switch (type) {
51 case ResourceFile::Type::kPng:
52 return "PNG";
53 case ResourceFile::Type::kBinaryXml:
54 return "BINARY_XML";
55 case ResourceFile::Type::kProtoXml:
56 return "PROTO_XML";
57 default:
58 break;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070059 }
Adam Lesinski00451162017-10-03 07:44:08 -070060 return "UNKNOWN";
Adam Lesinski59e04c62016-02-04 15:59:23 -080061}
62
Adam Lesinski00451162017-10-03 07:44:08 -070063static void DumpCompiledFile(const ResourceFile& file, const Source& source, off64_t offset,
Adam Lesinski93190b72017-11-03 15:20:17 -070064 size_t len, Printer* printer) {
65 printer->Print("Resource: ");
66 printer->Println(file.name.to_string());
67
68 printer->Print("Config: ");
69 printer->Println(file.config.to_string());
70
71 printer->Print("Source: ");
72 printer->Println(file.source.to_string());
73
74 printer->Print("Type: ");
75 printer->Println(ResourceFileTypeToString(file.type));
76
77 printer->Println(StringPrintf("Data: offset=%" PRIi64 " length=%zd", offset, len));
Adam Lesinski00451162017-10-03 07:44:08 -070078}
79
Adam Lesinskida9eba32018-02-13 16:44:10 -080080static bool DumpXmlFile(IAaptContext* context, io::IFile* file, bool proto,
81 text::Printer* printer) {
82 std::unique_ptr<xml::XmlResource> doc;
83 if (proto) {
84 std::unique_ptr<io::InputStream> in = file->OpenInputStream();
85 if (in == nullptr) {
86 context->GetDiagnostics()->Error(DiagMessage() << "failed to open file");
87 return false;
88 }
89
90 io::ZeroCopyInputAdaptor adaptor(in.get());
91 pb::XmlNode pb_node;
92 if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
93 context->GetDiagnostics()->Error(DiagMessage() << "failed to parse file as proto XML");
94 return false;
95 }
96
97 std::string err;
98 doc = DeserializeXmlResourceFromPb(pb_node, &err);
99 if (doc == nullptr) {
100 context->GetDiagnostics()->Error(DiagMessage() << "failed to deserialize proto XML");
101 return false;
102 }
103 printer->Println("Proto XML");
104 } else {
105 std::unique_ptr<io::IData> data = file->OpenAsData();
106 if (data == nullptr) {
107 context->GetDiagnostics()->Error(DiagMessage() << "failed to open file");
108 return false;
109 }
110
111 std::string err;
112 doc = xml::Inflate(data->data(), data->size(), &err);
113 if (doc == nullptr) {
114 context->GetDiagnostics()->Error(DiagMessage() << "failed to parse file as binary XML");
115 return false;
116 }
117 printer->Println("Binary XML");
118 }
119
120 Debug::DumpXml(*doc, printer);
121 return true;
122}
123
Adam Lesinski71be7052017-12-12 16:48:07 -0800124static bool TryDumpFile(IAaptContext* context, const std::string& file_path,
Adam Lesinskida9eba32018-02-13 16:44:10 -0800125 const DumpOptions& options) {
Adam Lesinski93190b72017-11-03 15:20:17 -0700126 // Use a smaller buffer so that there is less latency for dumping to stdout.
127 constexpr size_t kStdOutBufferSize = 1024u;
128 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
129 Printer printer(&fout);
130
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700131 std::string err;
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700132 std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(file_path, &err);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700133 if (zip) {
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700134 ResourceTable table;
Adam Lesinskida9eba32018-02-13 16:44:10 -0800135 bool proto = false;
Adam Lesinskie59f0d82017-10-13 09:36:53 -0700136 if (io::IFile* file = zip->FindFile("resources.pb")) {
Adam Lesinskida9eba32018-02-13 16:44:10 -0800137 proto = true;
138
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700139 std::unique_ptr<io::IData> data = file->OpenAsData();
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700140 if (data == nullptr) {
Adam Lesinskie59f0d82017-10-13 09:36:53 -0700141 context->GetDiagnostics()->Error(DiagMessage(file_path) << "failed to open resources.pb");
Pierre Lecesneaadf27e2017-05-05 14:58:21 +0100142 return false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700143 }
Adam Lesinski64587af2016-02-18 18:33:06 -0800144
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700145 pb::ResourceTable pb_table;
146 if (!pb_table.ParseFromArray(data->data(), data->size())) {
Adam Lesinskie59f0d82017-10-13 09:36:53 -0700147 context->GetDiagnostics()->Error(DiagMessage(file_path) << "invalid resources.pb");
Pierre Lecesneaadf27e2017-05-05 14:58:21 +0100148 return false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700149 }
Adam Lesinski64587af2016-02-18 18:33:06 -0800150
Adam Lesinski8780eb62017-10-31 17:44:39 -0700151 if (!DeserializeTableFromPb(pb_table, zip.get(), &table, &err)) {
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700152 context->GetDiagnostics()->Error(DiagMessage(file_path)
153 << "failed to parse table: " << err);
154 return false;
155 }
156 } else if (io::IFile* file = zip->FindFile("resources.arsc")) {
157 std::unique_ptr<io::IData> data = file->OpenAsData();
158 if (!data) {
159 context->GetDiagnostics()->Error(DiagMessage(file_path) << "failed to open resources.arsc");
160 return false;
161 }
162
Adam Lesinski8780eb62017-10-31 17:44:39 -0700163 BinaryResourceParser parser(context->GetDiagnostics(), &table, Source(file_path),
164 data->data(), data->size());
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700165 if (!parser.Parse()) {
Pierre Lecesneaadf27e2017-05-05 14:58:21 +0100166 return false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700167 }
Adam Lesinski64587af2016-02-18 18:33:06 -0800168 }
169
Adam Lesinskida9eba32018-02-13 16:44:10 -0800170 if (!options.file_to_dump_path) {
171 if (proto) {
172 printer.Println("Proto APK");
173 } else {
174 printer.Println("Binary APK");
175 }
176 Debug::PrintTable(table, options.print_options, &printer);
177 return true;
178 }
179
180 io::IFile* file = zip->FindFile(options.file_to_dump_path.value());
181 if (file == nullptr) {
182 context->GetDiagnostics()->Error(DiagMessage(file_path)
183 << "file '" << options.file_to_dump_path.value()
184 << "' not found in APK");
185 return false;
186 }
187 return DumpXmlFile(context, file, proto, &printer);
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700188 }
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700189
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700190 err.clear();
191
Adam Lesinski00451162017-10-03 07:44:08 -0700192 io::FileInputStream input(file_path);
193 if (input.HadError()) {
194 context->GetDiagnostics()->Error(DiagMessage(file_path)
195 << "failed to open file: " << input.GetError());
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700196 return false;
197 }
198
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700199 // Try as a compiled file.
Adam Lesinski00451162017-10-03 07:44:08 -0700200 ContainerReader reader(&input);
201 if (reader.HadError()) {
202 context->GetDiagnostics()->Error(DiagMessage(file_path)
203 << "failed to read container: " << reader.GetError());
Adam Lesinski8cdca1b2017-09-28 15:50:03 -0700204 return false;
205 }
206
Adam Lesinski93190b72017-11-03 15:20:17 -0700207 printer.Println("AAPT2 Container (APC)");
Adam Lesinski00451162017-10-03 07:44:08 -0700208 ContainerReaderEntry* entry;
209 while ((entry = reader.Next()) != nullptr) {
210 if (entry->Type() == ContainerEntryType::kResTable) {
Adam Lesinski93190b72017-11-03 15:20:17 -0700211 printer.Println("kResTable");
212
Adam Lesinski00451162017-10-03 07:44:08 -0700213 pb::ResourceTable pb_table;
214 if (!entry->GetResTable(&pb_table)) {
215 context->GetDiagnostics()->Error(DiagMessage(file_path)
216 << "failed to parse proto table: " << entry->GetError());
217 continue;
218 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800219
Adam Lesinski00451162017-10-03 07:44:08 -0700220 ResourceTable table;
221 err.clear();
Adam Lesinski8780eb62017-10-31 17:44:39 -0700222 if (!DeserializeTableFromPb(pb_table, nullptr /*files*/, &table, &err)) {
Adam Lesinski00451162017-10-03 07:44:08 -0700223 context->GetDiagnostics()->Error(DiagMessage(file_path)
224 << "failed to parse table: " << err);
225 continue;
226 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700227
Adam Lesinski93190b72017-11-03 15:20:17 -0700228 printer.Indent();
Adam Lesinskida9eba32018-02-13 16:44:10 -0800229 Debug::PrintTable(table, options.print_options, &printer);
Adam Lesinski93190b72017-11-03 15:20:17 -0700230 printer.Undent();
Adam Lesinski00451162017-10-03 07:44:08 -0700231 } else if (entry->Type() == ContainerEntryType::kResFile) {
Adam Lesinski93190b72017-11-03 15:20:17 -0700232 printer.Println("kResFile");
Adam Lesinski00451162017-10-03 07:44:08 -0700233 pb::internal::CompiledFile pb_compiled_file;
234 off64_t offset;
235 size_t length;
236 if (!entry->GetResFileOffsets(&pb_compiled_file, &offset, &length)) {
237 context->GetDiagnostics()->Error(
238 DiagMessage(file_path) << "failed to parse compiled proto file: " << entry->GetError());
239 continue;
240 }
241
242 ResourceFile file;
243 std::string error;
244 if (!DeserializeCompiledFileFromPb(pb_compiled_file, &file, &error)) {
245 context->GetDiagnostics()->Warn(DiagMessage(file_path)
246 << "failed to parse compiled file: " << error);
247 continue;
248 }
249
Adam Lesinski93190b72017-11-03 15:20:17 -0700250 printer.Indent();
251 DumpCompiledFile(file, Source(file_path), offset, length, &printer);
252 printer.Undent();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700253 }
254 }
Pierre Lecesneaadf27e2017-05-05 14:58:21 +0100255 return true;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800256}
257
Todd Kennedy895e19e2018-08-24 12:26:06 -0700258static bool DumpPackageName(IAaptContext* context, const std::string& file_path) {
259 auto loaded_apk = LoadedApk::LoadApkFromPath(file_path, context->GetDiagnostics());
260 if (!loaded_apk) {
261 return false;
262 }
263
264 constexpr size_t kStdOutBufferSize = 1024u;
265 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
266 Printer printer(&fout);
267
268 xml::Element* manifest_el = loaded_apk->GetManifest()->root.get();
269 if (!manifest_el) {
270 context->GetDiagnostics()->Error(DiagMessage() << "No AndroidManifest.");
271 return false;
272 }
273
274 xml::Attribute* attr = manifest_el->FindAttribute({}, "package");
275 if (!attr) {
276 context->GetDiagnostics()->Error(DiagMessage() << "No package name.");
277 return false;
278 }
279 printer.Println(StringPrintf("%s", attr->value.c_str()));
280
281 return true;
282}
283
Adam Lesinski00451162017-10-03 07:44:08 -0700284namespace {
285
Adam Lesinski59e04c62016-02-04 15:59:23 -0800286class DumpContext : public IAaptContext {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700287 public:
Adam Lesinskib522f042017-04-21 16:57:59 -0700288 PackageType GetPackageType() override {
289 // Doesn't matter.
290 return PackageType::kApp;
291 }
292
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700293 IDiagnostics* GetDiagnostics() override {
294 return &diagnostics_;
295 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800296
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700297 NameMangler* GetNameMangler() override {
Adam Lesinski00451162017-10-03 07:44:08 -0700298 UNIMPLEMENTED(FATAL);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700299 return nullptr;
300 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800301
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700302 const std::string& GetCompilationPackage() override {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700303 static std::string empty;
304 return empty;
305 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800306
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700307 uint8_t GetPackageId() override {
308 return 0;
309 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800310
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700311 SymbolTable* GetExternalSymbols() override {
Adam Lesinski00451162017-10-03 07:44:08 -0700312 UNIMPLEMENTED(FATAL);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700313 return nullptr;
314 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800315
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700316 bool IsVerbose() override {
317 return verbose_;
318 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800319
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700320 void SetVerbose(bool val) {
321 verbose_ = val;
322 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800323
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700324 int GetMinSdkVersion() override {
325 return 0;
326 }
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700327
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700328 private:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700329 StdErrDiagnostics diagnostics_;
330 bool verbose_ = false;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800331};
332
Adam Lesinski00451162017-10-03 07:44:08 -0700333} // namespace
334
335// Entry point for dump command.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700336int Dump(const std::vector<StringPiece>& args) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700337 bool verbose = false;
Adam Lesinski71be7052017-12-12 16:48:07 -0800338 bool no_values = false;
Adam Lesinskida9eba32018-02-13 16:44:10 -0800339 DumpOptions options;
Adam Lesinski71be7052017-12-12 16:48:07 -0800340 Flags flags = Flags()
341 .OptionalSwitch("--no-values",
342 "Suppresses output of values when displaying resource tables.",
343 &no_values)
Adam Lesinskida9eba32018-02-13 16:44:10 -0800344 .OptionalFlag("--file", "Dumps the specified file from the APK passed as arg.",
345 &options.file_to_dump_path)
Adam Lesinski71be7052017-12-12 16:48:07 -0800346 .OptionalSwitch("-v", "increase verbosity of output", &verbose);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700347 if (!flags.Parse("aapt2 dump", args, &std::cerr)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700348 return 1;
349 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800350
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700351 DumpContext context;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700352 context.SetVerbose(verbose);
Adam Lesinski59e04c62016-02-04 15:59:23 -0800353
Todd Kennedy895e19e2018-08-24 12:26:06 -0700354 auto parsedArgs = flags.GetArgs();
355 if (parsedArgs.size() > 1 && parsedArgs[0] == "packagename") {
356 parsedArgs.erase(parsedArgs.begin());
357 for (const std::string& arg : parsedArgs) {
358 if (!DumpPackageName(&context, arg)) {
359 return 1;
360 }
361 }
362 return 0;
363 }
364
Adam Lesinskida9eba32018-02-13 16:44:10 -0800365 options.print_options.show_sources = true;
366 options.print_options.show_values = !no_values;
Todd Kennedy895e19e2018-08-24 12:26:06 -0700367 for (const std::string& arg : parsedArgs) {
Adam Lesinskida9eba32018-02-13 16:44:10 -0800368 if (!TryDumpFile(&context, arg, options)) {
Pierre Lecesneaadf27e2017-05-05 14:58:21 +0100369 return 1;
370 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700371 }
372 return 0;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800373}
374
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700375} // namespace aapt