blob: 91e39771e49055c523a45c67f76db2ae8b874a63 [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"
Ryan Mitchell5d275512018-07-19 14:29:00 -070027#include "LoadedApk.h"
28#include "Util.h"
Adam Lesinski00451162017-10-03 07:44:08 -070029#include "format/Container.h"
Adam Lesinski46708052017-09-29 14:49:15 -070030#include "format/binary/BinaryResourceParser.h"
Ryan Mitchell5d275512018-07-19 14:29:00 -070031#include "format/binary/XmlFlattener.h"
Adam Lesinski46708052017-09-29 14:49:15 -070032#include "format/proto/ProtoDeserialize.h"
Adam Lesinski00451162017-10-03 07:44:08 -070033#include "io/FileStream.h"
Adam Lesinski64587af2016-02-18 18:33:06 -080034#include "io/ZipArchive.h"
Adam Lesinski59e04c62016-02-04 15:59:23 -080035#include "process/IResourceTableConsumer.h"
Adam Lesinski93190b72017-11-03 15:20:17 -070036#include "text/Printer.h"
Adam Lesinski59e04c62016-02-04 15:59:23 -080037#include "util/Files.h"
Adam Lesinskid5083f62017-01-16 15:07:21 -080038
Adam Lesinski93190b72017-11-03 15:20:17 -070039using ::aapt::text::Printer;
Adam Lesinski4ffea042017-08-10 15:37:28 -070040using ::android::StringPiece;
Adam Lesinski93190b72017-11-03 15:20:17 -070041using ::android::base::StringPrintf;
Adam Lesinski59e04c62016-02-04 15:59:23 -080042
Adam Lesinski59e04c62016-02-04 15:59:23 -080043namespace aapt {
44
Adam Lesinski00451162017-10-03 07:44:08 -070045static const char* ResourceFileTypeToString(const ResourceFile::Type& type) {
46 switch (type) {
47 case ResourceFile::Type::kPng:
48 return "PNG";
49 case ResourceFile::Type::kBinaryXml:
50 return "BINARY_XML";
51 case ResourceFile::Type::kProtoXml:
52 return "PROTO_XML";
53 default:
54 break;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070055 }
Adam Lesinski00451162017-10-03 07:44:08 -070056 return "UNKNOWN";
Adam Lesinski59e04c62016-02-04 15:59:23 -080057}
58
Adam Lesinski00451162017-10-03 07:44:08 -070059static void DumpCompiledFile(const ResourceFile& file, const Source& source, off64_t offset,
Adam Lesinski93190b72017-11-03 15:20:17 -070060 size_t len, Printer* printer) {
61 printer->Print("Resource: ");
62 printer->Println(file.name.to_string());
63
64 printer->Print("Config: ");
65 printer->Println(file.config.to_string());
66
67 printer->Print("Source: ");
68 printer->Println(file.source.to_string());
69
70 printer->Print("Type: ");
71 printer->Println(ResourceFileTypeToString(file.type));
72
73 printer->Println(StringPrintf("Data: offset=%" PRIi64 " length=%zd", offset, len));
Adam Lesinski00451162017-10-03 07:44:08 -070074}
75
Adam Lesinski00451162017-10-03 07:44:08 -070076namespace {
77
Adam Lesinski59e04c62016-02-04 15:59:23 -080078class DumpContext : public IAaptContext {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070079 public:
Adam Lesinskib522f042017-04-21 16:57:59 -070080 PackageType GetPackageType() override {
81 // Doesn't matter.
82 return PackageType::kApp;
83 }
84
Adam Lesinskid0f492d2017-04-03 18:12:45 -070085 IDiagnostics* GetDiagnostics() override {
86 return &diagnostics_;
87 }
Adam Lesinski59e04c62016-02-04 15:59:23 -080088
Adam Lesinskice5e56e2016-10-21 17:56:45 -070089 NameMangler* GetNameMangler() override {
Adam Lesinski00451162017-10-03 07:44:08 -070090 UNIMPLEMENTED(FATAL);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070091 return nullptr;
92 }
Adam Lesinski59e04c62016-02-04 15:59:23 -080093
Adam Lesinskice5e56e2016-10-21 17:56:45 -070094 const std::string& GetCompilationPackage() override {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070095 static std::string empty;
96 return empty;
97 }
Adam Lesinski59e04c62016-02-04 15:59:23 -080098
Adam Lesinskid0f492d2017-04-03 18:12:45 -070099 uint8_t GetPackageId() override {
100 return 0;
101 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800102
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700103 SymbolTable* GetExternalSymbols() override {
Adam Lesinski00451162017-10-03 07:44:08 -0700104 UNIMPLEMENTED(FATAL);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700105 return nullptr;
106 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800107
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700108 bool IsVerbose() override {
109 return verbose_;
110 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800111
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700112 void SetVerbose(bool val) {
113 verbose_ = val;
114 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800115
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700116 int GetMinSdkVersion() override {
117 return 0;
118 }
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700119
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700120 private:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700121 StdErrDiagnostics diagnostics_;
122 bool verbose_ = false;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800123};
124
Adam Lesinski00451162017-10-03 07:44:08 -0700125} // namespace
126
Ryan Mitchell5d275512018-07-19 14:29:00 -0700127// Use a smaller buffer so that there is less latency for dumping to stdout.
128constexpr size_t kStdOutBufferSize = 1024u;
129
130int DumpAPCCommand::Action(const std::vector<std::string>& args) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700131 DumpContext context;
Ryan Mitchell5d275512018-07-19 14:29:00 -0700132 DebugPrintTableOptions print_options;
133 print_options.show_sources = true;
134 print_options.show_values = !no_values_;
135
136 if (args.size() < 1) {
137 diag_->Error(DiagMessage() << "No dump container specified.");
138 return 1;
139 }
140
141 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
142 Printer printer(&fout);
143
144 for (auto container : args) {
145 io::FileInputStream input(container);
146 if (input.HadError()) {
147 context.GetDiagnostics()->Error(DiagMessage(container)
148 << "failed to open file: " << input.GetError());
149 return false;
150 }
151
152 // Try as a compiled file.
153 ContainerReader reader(&input);
154 if (reader.HadError()) {
155 context.GetDiagnostics()->Error(DiagMessage(container)
156 << "failed to read container: " << reader.GetError());
157 return false;
158 }
159
160 printer.Println("AAPT2 Container (APC)");
161 ContainerReaderEntry* entry;
162 std::string error;
163 while ((entry = reader.Next()) != nullptr) {
164 if (entry->Type() == ContainerEntryType::kResTable) {
165 printer.Println("kResTable");
166
167 pb::ResourceTable pb_table;
168 if (!entry->GetResTable(&pb_table)) {
169 context.GetDiagnostics()->Error(DiagMessage(container)
170 << "failed to parse proto table: "
171 << entry->GetError());
172 continue;
173 }
174
175 ResourceTable table;
176 error.clear();
177 if (!DeserializeTableFromPb(pb_table, nullptr /*files*/, &table, &error)) {
178 context.GetDiagnostics()->Error(DiagMessage(container)
179 << "failed to parse table: " << error);
180 continue;
181 }
182
183 printer.Indent();
184 Debug::PrintTable(table, print_options, &printer);
185 printer.Undent();
186 } else if (entry->Type() == ContainerEntryType::kResFile) {
187 printer.Println("kResFile");
188 pb::internal::CompiledFile pb_compiled_file;
189 off64_t offset;
190 size_t length;
191 if (!entry->GetResFileOffsets(&pb_compiled_file, &offset, &length)) {
192 context.GetDiagnostics()->Error(
193 DiagMessage(container) << "failed to parse compiled proto file: "
194 << entry->GetError());
195 continue;
196 }
197
198 ResourceFile file;
199 if (!DeserializeCompiledFileFromPb(pb_compiled_file, &file, &error)) {
200 context.GetDiagnostics()->Warn(DiagMessage(container)
201 << "failed to parse compiled file: " << error);
202 continue;
203 }
204
205 printer.Indent();
206 DumpCompiledFile(file, Source(container), offset, length, &printer);
207 printer.Undent();
208 }
Pierre Lecesneaadf27e2017-05-05 14:58:21 +0100209 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700210 }
Ryan Mitchell5d275512018-07-19 14:29:00 -0700211
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700212 return 0;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800213}
214
Ryan Mitchell5d275512018-07-19 14:29:00 -0700215int DumpConfigsCommand::Action(const std::vector<std::string>& args) {
216 if (args.size() < 1) {
217 diag_->Error(DiagMessage() << "No dump apk specified.");
218 return 1;
219 }
220
221 auto loaded_apk = LoadedApk::LoadApkFromPath(args[0], diag_);
222 if (!loaded_apk) {
223 return 1;
224 }
225
Ryan Mitchellfc225b22018-08-21 14:52:51 -0700226 ResourceTable* table = loaded_apk->GetResourceTable();
227 if (!table) {
228 diag_->Error(DiagMessage() << "Failed to retrieve resource table.");
229 return 1;
230 }
231
Ryan Mitchell5d275512018-07-19 14:29:00 -0700232 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
233 Printer printer(&fout);
234
235 // Comparison function used to order configurations
236 auto compare = [](ConfigDescription c1, ConfigDescription c2) -> bool {
237 return c1.compare(c2) < 0;
238 };
239
240 // Insert the configurations into a set in order to keep every configuarion seen
241 std::set<ConfigDescription, decltype(compare)> configs(compare);
Ryan Mitchellfc225b22018-08-21 14:52:51 -0700242 for (auto& package : table->packages) {
Ryan Mitchell5d275512018-07-19 14:29:00 -0700243 for (auto& type : package->types) {
244 for (auto& entry : type->entries) {
245 for (auto& value : entry->values) {
246 configs.insert(value->config);
247 }
248 }
249 }
250 }
251
252 // Print the configurations in order
253 for (auto& config : configs) {
254 printer.Print(StringPrintf("%s\n", config.to_string().data()));
255 }
256
257 return 0;
258}
259
260int DumpStringsCommand::Action(const std::vector<std::string>& args) {
261 DumpContext context;
262 if (args.size() < 1) {
263 diag_->Error(DiagMessage() << "No dump apk specified.");
264 return 1;
265 }
266
267 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
268 Printer printer(&fout);
269
270 for (auto apk : args) {
271 auto loaded_apk = LoadedApk::LoadApkFromPath(apk, diag_);
272 if (!loaded_apk) {
273 return 1;
274 }
275
Ryan Mitchellfc225b22018-08-21 14:52:51 -0700276 ResourceTable* table = loaded_apk->GetResourceTable();
277 if (!table) {
278 diag_->Error(DiagMessage() << "Failed to retrieve resource table.");
279 return 1;
280 }
281
Ryan Mitchell5d275512018-07-19 14:29:00 -0700282 // Load the run-time xml string pool using the flattened data
283 BigBuffer buffer(4096);
Ryan Mitchellfc225b22018-08-21 14:52:51 -0700284 StringPool::FlattenUtf8(&buffer, table->string_pool, context.GetDiagnostics());
Ryan Mitchell5d275512018-07-19 14:29:00 -0700285 auto data = buffer.to_string();
286 android::ResStringPool pool(data.data(), data.size(), false);
287 Debug::DumpResStringPool(&pool, &printer);
288 }
289
290 return 0;
291}
292
293int DumpTableCommand::Action(const std::vector<std::string>& args) {
294 if (args.size() < 1) {
295 diag_->Error(DiagMessage() << "No dump apk specified.");
296 return 1;
297 }
298
299 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
300 Printer printer(&fout);
301
302 DebugPrintTableOptions print_options;
303 print_options.show_sources = true;
304 print_options.show_values = !no_values_;
305
306 for (auto apk : args) {
307 auto loaded_apk = LoadedApk::LoadApkFromPath(apk, diag_);
308 if (!loaded_apk) {
309 return 1;
310 }
311
Ryan Mitchelldea2fe32018-08-30 13:48:54 -0700312 if (loaded_apk->GetApkFormat() == ApkFormat::kProto) {
Ryan Mitchell5d275512018-07-19 14:29:00 -0700313 printer.Println("Proto APK");
314 } else {
315 printer.Println("Binary APK");
316 }
317
Ryan Mitchellfc225b22018-08-21 14:52:51 -0700318 ResourceTable* table = loaded_apk->GetResourceTable();
319 if (!table) {
320 diag_->Error(DiagMessage() << "Failed to retrieve resource table.");
321 return 1;
322 }
323
324 Debug::PrintTable(*table, print_options, &printer);
Ryan Mitchell5d275512018-07-19 14:29:00 -0700325 }
326
327 return 0;
328}
329
330int DumpXmlTreeCommand::Action(const std::vector<std::string>& args) {
331 if (args.size() < 1) {
332 diag_->Error(DiagMessage() << "No dump apk specified");
333 return 1;
334 }
335
336 auto loaded_apk = LoadedApk::LoadApkFromPath(args[0], diag_);
337 if (!loaded_apk) {
338 return 1;
339 }
340
341 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
342 Printer printer(&fout);
343
344 // Dump the xml tree of every passed in file
345 for (auto file : files_) {
346 auto xml = loaded_apk->LoadXml(file, diag_);
347 if (!xml) {
348 return 1;
349 }
350
351 Debug::DumpXml(*xml, &printer);
352 }
353
354 return 0;
355}
356
357int DumpXmlStringsCommand::Action(const std::vector<std::string>& args) {
358 DumpContext context;
359 if (args.size() < 1) {
360 diag_->Error(DiagMessage() << "No dump apk specified.");
361 return 1;
362 }
363
364 auto loaded_apk = LoadedApk::LoadApkFromPath(args[0], diag_);
365 if (!loaded_apk) {
366 return 1;
367 }
368
369 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
370 Printer printer(&fout);
371
372 // Dump the xml strings of every passed in file
373 for (auto xml_file : files_) {
374 android::ResXMLTree tree;
375
Ryan Mitchelldea2fe32018-08-30 13:48:54 -0700376 if (loaded_apk->GetApkFormat() == ApkFormat::kProto) {
Ryan Mitchell5d275512018-07-19 14:29:00 -0700377 auto xml = loaded_apk->LoadXml(xml_file, diag_);
378 if (!xml) {
379 return 1;
380 }
381
382 // Flatten the xml document to get a binary representation of the proto xml file
383 BigBuffer buffer(4096);
384 XmlFlattenerOptions options = {};
385 options.keep_raw_values = true;
386 XmlFlattener flattener(&buffer, options);
387 if (!flattener.Consume(&context, xml.get())) {
388 return 1;
389 }
390
391 // Load the run-time xml tree using the flattened data
392 std::string data = buffer.to_string();
393 tree.setTo(data.data(), data.size(), /** copyData */ true);
394
Ryan Mitchelldea2fe32018-08-30 13:48:54 -0700395 } else if (loaded_apk->GetApkFormat() == ApkFormat::kBinary) {
Ryan Mitchell5d275512018-07-19 14:29:00 -0700396 io::IFile* file = loaded_apk->GetFileCollection()->FindFile(xml_file);
397 if (!file) {
398 diag_->Error(DiagMessage(xml_file) << "file '" << xml_file << "' not found in APK");
399 return 1;
400 }
401
402 std::unique_ptr<io::IData> data = file->OpenAsData();
403 if (!data) {
404 diag_->Error(DiagMessage() << "failed to open file");
405 return 1;
406 }
407
408 // Load the run-time xml tree from the file data
409 tree.setTo(data->data(), data->size(), /** copyData */ true);
410 }
411
412 Debug::DumpResStringPool(&tree.getStrings(), &printer);
413 }
414
415 return 0;
416}
417
Todd Kennedy908b7fc2018-08-24 10:11:21 -0700418int DumpPackageNameCommand::Action(const std::vector<std::string>& args) {
419 if (args.size() < 1) {
420 diag_->Error(DiagMessage() << "No dump apk specified.");
421 return 1;
422 }
423
424 auto loaded_apk = LoadedApk::LoadApkFromPath(args[0], diag_);
425 if (!loaded_apk) {
426 return 1;
427 }
428
429 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
430 Printer printer(&fout);
431
432 xml::Element* manifest_el = loaded_apk->GetManifest()->root.get();
433 if (!manifest_el) {
434 diag_->Error(DiagMessage() << "No AndroidManifest.");
435 return 1;
436 }
437
438 xml::Attribute* attr = manifest_el->FindAttribute({}, "package");
439 if (!attr) {
440 diag_->Error(DiagMessage() << "No package name.");
441 return 1;
442 }
443 printer.Println(StringPrintf("%s", attr->value.c_str()));
444
445 return 0;
446}
447
Ryan Mitchell5d275512018-07-19 14:29:00 -0700448/** Preform no action because a subcommand is required. */
449int DumpCommand::Action(const std::vector<std::string>& args) {
450 if (args.size() == 0) {
451 diag_->Error(DiagMessage() << "no subcommand specified");
452 } else {
453 diag_->Error(DiagMessage() << "unknown subcommand '" << args[0] << "'");
454 }
455
456 Usage(&std::cerr);
457 return 1;
458}
459
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700460} // namespace aapt