blob: d80b5ea38bbd5c4b19d8380c3c34c305cb4e1584 [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"
MÃ¥rten Kongstad24c9aa62018-06-20 08:46:41 +020023#include "androidfw/ConfigDescription.h"
Adam Lesinskid5083f62017-01-16 15:07:21 -080024#include "androidfw/StringPiece.h"
25
Adam Lesinski59e04c62016-02-04 15:59:23 -080026#include "Debug.h"
27#include "Diagnostics.h"
Ryan Mitchell5d275512018-07-19 14:29:00 -070028#include "LoadedApk.h"
29#include "Util.h"
Adam Lesinski00451162017-10-03 07:44:08 -070030#include "format/Container.h"
Adam Lesinski46708052017-09-29 14:49:15 -070031#include "format/binary/BinaryResourceParser.h"
Ryan Mitchell5d275512018-07-19 14:29:00 -070032#include "format/binary/XmlFlattener.h"
Adam Lesinski46708052017-09-29 14:49:15 -070033#include "format/proto/ProtoDeserialize.h"
Adam Lesinski00451162017-10-03 07:44:08 -070034#include "io/FileStream.h"
Adam Lesinski64587af2016-02-18 18:33:06 -080035#include "io/ZipArchive.h"
Adam Lesinski59e04c62016-02-04 15:59:23 -080036#include "process/IResourceTableConsumer.h"
Adam Lesinski93190b72017-11-03 15:20:17 -070037#include "text/Printer.h"
Adam Lesinski59e04c62016-02-04 15:59:23 -080038#include "util/Files.h"
Adam Lesinskid5083f62017-01-16 15:07:21 -080039
Adam Lesinski93190b72017-11-03 15:20:17 -070040using ::aapt::text::Printer;
Adam Lesinski4ffea042017-08-10 15:37:28 -070041using ::android::StringPiece;
Adam Lesinski93190b72017-11-03 15:20:17 -070042using ::android::base::StringPrintf;
Adam Lesinski59e04c62016-02-04 15:59:23 -080043
Adam Lesinski59e04c62016-02-04 15:59:23 -080044namespace aapt {
45
Adam Lesinski00451162017-10-03 07:44:08 -070046static const char* ResourceFileTypeToString(const ResourceFile::Type& type) {
47 switch (type) {
48 case ResourceFile::Type::kPng:
49 return "PNG";
50 case ResourceFile::Type::kBinaryXml:
51 return "BINARY_XML";
52 case ResourceFile::Type::kProtoXml:
53 return "PROTO_XML";
54 default:
55 break;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070056 }
Adam Lesinski00451162017-10-03 07:44:08 -070057 return "UNKNOWN";
Adam Lesinski59e04c62016-02-04 15:59:23 -080058}
59
Adam Lesinski00451162017-10-03 07:44:08 -070060static void DumpCompiledFile(const ResourceFile& file, const Source& source, off64_t offset,
Adam Lesinski93190b72017-11-03 15:20:17 -070061 size_t len, Printer* printer) {
62 printer->Print("Resource: ");
63 printer->Println(file.name.to_string());
64
65 printer->Print("Config: ");
66 printer->Println(file.config.to_string());
67
68 printer->Print("Source: ");
69 printer->Println(file.source.to_string());
70
71 printer->Print("Type: ");
72 printer->Println(ResourceFileTypeToString(file.type));
73
74 printer->Println(StringPrintf("Data: offset=%" PRIi64 " length=%zd", offset, len));
Adam Lesinski00451162017-10-03 07:44:08 -070075}
76
Adam Lesinski00451162017-10-03 07:44:08 -070077namespace {
78
Adam Lesinski59e04c62016-02-04 15:59:23 -080079class DumpContext : public IAaptContext {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070080 public:
Adam Lesinskib522f042017-04-21 16:57:59 -070081 PackageType GetPackageType() override {
82 // Doesn't matter.
83 return PackageType::kApp;
84 }
85
Adam Lesinskid0f492d2017-04-03 18:12:45 -070086 IDiagnostics* GetDiagnostics() override {
87 return &diagnostics_;
88 }
Adam Lesinski59e04c62016-02-04 15:59:23 -080089
Adam Lesinskice5e56e2016-10-21 17:56:45 -070090 NameMangler* GetNameMangler() override {
Adam Lesinski00451162017-10-03 07:44:08 -070091 UNIMPLEMENTED(FATAL);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070092 return nullptr;
93 }
Adam Lesinski59e04c62016-02-04 15:59:23 -080094
Adam Lesinskice5e56e2016-10-21 17:56:45 -070095 const std::string& GetCompilationPackage() override {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070096 static std::string empty;
97 return empty;
98 }
Adam Lesinski59e04c62016-02-04 15:59:23 -080099
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700100 uint8_t GetPackageId() override {
101 return 0;
102 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800103
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700104 SymbolTable* GetExternalSymbols() override {
Adam Lesinski00451162017-10-03 07:44:08 -0700105 UNIMPLEMENTED(FATAL);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700106 return nullptr;
107 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800108
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700109 bool IsVerbose() override {
110 return verbose_;
111 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800112
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700113 void SetVerbose(bool val) {
114 verbose_ = val;
115 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800116
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700117 int GetMinSdkVersion() override {
118 return 0;
119 }
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700120
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700121 private:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700122 StdErrDiagnostics diagnostics_;
123 bool verbose_ = false;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800124};
125
Adam Lesinski00451162017-10-03 07:44:08 -0700126} // namespace
127
Ryan Mitchell5d275512018-07-19 14:29:00 -0700128// Use a smaller buffer so that there is less latency for dumping to stdout.
129constexpr size_t kStdOutBufferSize = 1024u;
130
131int DumpAPCCommand::Action(const std::vector<std::string>& args) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700132 DumpContext context;
Ryan Mitchell5d275512018-07-19 14:29:00 -0700133 DebugPrintTableOptions print_options;
134 print_options.show_sources = true;
135 print_options.show_values = !no_values_;
136
137 if (args.size() < 1) {
138 diag_->Error(DiagMessage() << "No dump container specified.");
139 return 1;
140 }
141
142 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
143 Printer printer(&fout);
144
145 for (auto container : args) {
146 io::FileInputStream input(container);
147 if (input.HadError()) {
148 context.GetDiagnostics()->Error(DiagMessage(container)
149 << "failed to open file: " << input.GetError());
150 return false;
151 }
152
153 // Try as a compiled file.
154 ContainerReader reader(&input);
155 if (reader.HadError()) {
156 context.GetDiagnostics()->Error(DiagMessage(container)
157 << "failed to read container: " << reader.GetError());
158 return false;
159 }
160
161 printer.Println("AAPT2 Container (APC)");
162 ContainerReaderEntry* entry;
163 std::string error;
164 while ((entry = reader.Next()) != nullptr) {
165 if (entry->Type() == ContainerEntryType::kResTable) {
166 printer.Println("kResTable");
167
168 pb::ResourceTable pb_table;
169 if (!entry->GetResTable(&pb_table)) {
170 context.GetDiagnostics()->Error(DiagMessage(container)
171 << "failed to parse proto table: "
172 << entry->GetError());
173 continue;
174 }
175
176 ResourceTable table;
177 error.clear();
178 if (!DeserializeTableFromPb(pb_table, nullptr /*files*/, &table, &error)) {
179 context.GetDiagnostics()->Error(DiagMessage(container)
180 << "failed to parse table: " << error);
181 continue;
182 }
183
184 printer.Indent();
185 Debug::PrintTable(table, print_options, &printer);
186 printer.Undent();
187 } else if (entry->Type() == ContainerEntryType::kResFile) {
188 printer.Println("kResFile");
189 pb::internal::CompiledFile pb_compiled_file;
190 off64_t offset;
191 size_t length;
192 if (!entry->GetResFileOffsets(&pb_compiled_file, &offset, &length)) {
193 context.GetDiagnostics()->Error(
194 DiagMessage(container) << "failed to parse compiled proto file: "
195 << entry->GetError());
196 continue;
197 }
198
199 ResourceFile file;
200 if (!DeserializeCompiledFileFromPb(pb_compiled_file, &file, &error)) {
201 context.GetDiagnostics()->Warn(DiagMessage(container)
202 << "failed to parse compiled file: " << error);
203 continue;
204 }
205
206 printer.Indent();
207 DumpCompiledFile(file, Source(container), offset, length, &printer);
208 printer.Undent();
209 }
Pierre Lecesneaadf27e2017-05-05 14:58:21 +0100210 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700211 }
Ryan Mitchell5d275512018-07-19 14:29:00 -0700212
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700213 return 0;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800214}
215
Ryan Mitchell5d275512018-07-19 14:29:00 -0700216int DumpConfigsCommand::Action(const std::vector<std::string>& args) {
217 if (args.size() < 1) {
218 diag_->Error(DiagMessage() << "No dump apk specified.");
219 return 1;
220 }
221
222 auto loaded_apk = LoadedApk::LoadApkFromPath(args[0], diag_);
223 if (!loaded_apk) {
224 return 1;
225 }
226
Ryan Mitchellfc225b22018-08-21 14:52:51 -0700227 ResourceTable* table = loaded_apk->GetResourceTable();
228 if (!table) {
229 diag_->Error(DiagMessage() << "Failed to retrieve resource table.");
230 return 1;
231 }
232
Ryan Mitchell5d275512018-07-19 14:29:00 -0700233 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
234 Printer printer(&fout);
235
236 // Comparison function used to order configurations
MÃ¥rten Kongstad24c9aa62018-06-20 08:46:41 +0200237 auto compare = [](android::ConfigDescription c1, android::ConfigDescription c2) -> bool {
Ryan Mitchell5d275512018-07-19 14:29:00 -0700238 return c1.compare(c2) < 0;
239 };
240
241 // Insert the configurations into a set in order to keep every configuarion seen
MÃ¥rten Kongstad24c9aa62018-06-20 08:46:41 +0200242 std::set<android::ConfigDescription, decltype(compare)> configs(compare);
Ryan Mitchellfc225b22018-08-21 14:52:51 -0700243 for (auto& package : table->packages) {
Ryan Mitchell5d275512018-07-19 14:29:00 -0700244 for (auto& type : package->types) {
245 for (auto& entry : type->entries) {
246 for (auto& value : entry->values) {
247 configs.insert(value->config);
248 }
249 }
250 }
251 }
252
253 // Print the configurations in order
254 for (auto& config : configs) {
255 printer.Print(StringPrintf("%s\n", config.to_string().data()));
256 }
257
258 return 0;
259}
260
261int DumpStringsCommand::Action(const std::vector<std::string>& args) {
262 DumpContext context;
263 if (args.size() < 1) {
264 diag_->Error(DiagMessage() << "No dump apk specified.");
265 return 1;
266 }
267
268 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
269 Printer printer(&fout);
270
271 for (auto apk : args) {
272 auto loaded_apk = LoadedApk::LoadApkFromPath(apk, diag_);
273 if (!loaded_apk) {
274 return 1;
275 }
276
Ryan Mitchellfc225b22018-08-21 14:52:51 -0700277 ResourceTable* table = loaded_apk->GetResourceTable();
278 if (!table) {
279 diag_->Error(DiagMessage() << "Failed to retrieve resource table.");
280 return 1;
281 }
282
Ryan Mitchell5d275512018-07-19 14:29:00 -0700283 // Load the run-time xml string pool using the flattened data
284 BigBuffer buffer(4096);
Ryan Mitchellfc225b22018-08-21 14:52:51 -0700285 StringPool::FlattenUtf8(&buffer, table->string_pool, context.GetDiagnostics());
Ryan Mitchell5d275512018-07-19 14:29:00 -0700286 auto data = buffer.to_string();
287 android::ResStringPool pool(data.data(), data.size(), false);
288 Debug::DumpResStringPool(&pool, &printer);
289 }
290
291 return 0;
292}
293
294int DumpTableCommand::Action(const std::vector<std::string>& args) {
295 if (args.size() < 1) {
296 diag_->Error(DiagMessage() << "No dump apk specified.");
297 return 1;
298 }
299
300 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
301 Printer printer(&fout);
302
303 DebugPrintTableOptions print_options;
304 print_options.show_sources = true;
305 print_options.show_values = !no_values_;
306
307 for (auto apk : args) {
308 auto loaded_apk = LoadedApk::LoadApkFromPath(apk, diag_);
309 if (!loaded_apk) {
310 return 1;
311 }
312
Ryan Mitchelldea2fe32018-08-30 13:48:54 -0700313 if (loaded_apk->GetApkFormat() == ApkFormat::kProto) {
Ryan Mitchell5d275512018-07-19 14:29:00 -0700314 printer.Println("Proto APK");
315 } else {
316 printer.Println("Binary APK");
317 }
318
Ryan Mitchellfc225b22018-08-21 14:52:51 -0700319 ResourceTable* table = loaded_apk->GetResourceTable();
320 if (!table) {
321 diag_->Error(DiagMessage() << "Failed to retrieve resource table.");
322 return 1;
323 }
324
325 Debug::PrintTable(*table, print_options, &printer);
Ryan Mitchell5d275512018-07-19 14:29:00 -0700326 }
327
328 return 0;
329}
330
331int DumpXmlTreeCommand::Action(const std::vector<std::string>& args) {
332 if (args.size() < 1) {
333 diag_->Error(DiagMessage() << "No dump apk specified");
334 return 1;
335 }
336
337 auto loaded_apk = LoadedApk::LoadApkFromPath(args[0], diag_);
338 if (!loaded_apk) {
339 return 1;
340 }
341
342 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
343 Printer printer(&fout);
344
345 // Dump the xml tree of every passed in file
346 for (auto file : files_) {
347 auto xml = loaded_apk->LoadXml(file, diag_);
348 if (!xml) {
349 return 1;
350 }
351
352 Debug::DumpXml(*xml, &printer);
353 }
354
355 return 0;
356}
357
358int DumpXmlStringsCommand::Action(const std::vector<std::string>& args) {
359 DumpContext context;
360 if (args.size() < 1) {
361 diag_->Error(DiagMessage() << "No dump apk specified.");
362 return 1;
363 }
364
365 auto loaded_apk = LoadedApk::LoadApkFromPath(args[0], diag_);
366 if (!loaded_apk) {
367 return 1;
368 }
369
370 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
371 Printer printer(&fout);
372
373 // Dump the xml strings of every passed in file
374 for (auto xml_file : files_) {
375 android::ResXMLTree tree;
376
Ryan Mitchelldea2fe32018-08-30 13:48:54 -0700377 if (loaded_apk->GetApkFormat() == ApkFormat::kProto) {
Ryan Mitchell5d275512018-07-19 14:29:00 -0700378 auto xml = loaded_apk->LoadXml(xml_file, diag_);
379 if (!xml) {
380 return 1;
381 }
382
383 // Flatten the xml document to get a binary representation of the proto xml file
384 BigBuffer buffer(4096);
385 XmlFlattenerOptions options = {};
386 options.keep_raw_values = true;
387 XmlFlattener flattener(&buffer, options);
388 if (!flattener.Consume(&context, xml.get())) {
389 return 1;
390 }
391
392 // Load the run-time xml tree using the flattened data
393 std::string data = buffer.to_string();
394 tree.setTo(data.data(), data.size(), /** copyData */ true);
395
Ryan Mitchelldea2fe32018-08-30 13:48:54 -0700396 } else if (loaded_apk->GetApkFormat() == ApkFormat::kBinary) {
Ryan Mitchell5d275512018-07-19 14:29:00 -0700397 io::IFile* file = loaded_apk->GetFileCollection()->FindFile(xml_file);
398 if (!file) {
399 diag_->Error(DiagMessage(xml_file) << "file '" << xml_file << "' not found in APK");
400 return 1;
401 }
402
403 std::unique_ptr<io::IData> data = file->OpenAsData();
404 if (!data) {
405 diag_->Error(DiagMessage() << "failed to open file");
406 return 1;
407 }
408
409 // Load the run-time xml tree from the file data
410 tree.setTo(data->data(), data->size(), /** copyData */ true);
411 }
412
413 Debug::DumpResStringPool(&tree.getStrings(), &printer);
414 }
415
416 return 0;
417}
418
Todd Kennedy908b7fc2018-08-24 10:11:21 -0700419int DumpPackageNameCommand::Action(const std::vector<std::string>& args) {
420 if (args.size() < 1) {
421 diag_->Error(DiagMessage() << "No dump apk specified.");
422 return 1;
423 }
424
425 auto loaded_apk = LoadedApk::LoadApkFromPath(args[0], diag_);
426 if (!loaded_apk) {
427 return 1;
428 }
429
430 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
431 Printer printer(&fout);
432
433 xml::Element* manifest_el = loaded_apk->GetManifest()->root.get();
434 if (!manifest_el) {
435 diag_->Error(DiagMessage() << "No AndroidManifest.");
436 return 1;
437 }
438
439 xml::Attribute* attr = manifest_el->FindAttribute({}, "package");
440 if (!attr) {
441 diag_->Error(DiagMessage() << "No package name.");
442 return 1;
443 }
444 printer.Println(StringPrintf("%s", attr->value.c_str()));
445
446 return 0;
447}
448
Ryan Mitchell5d275512018-07-19 14:29:00 -0700449/** Preform no action because a subcommand is required. */
450int DumpCommand::Action(const std::vector<std::string>& args) {
451 if (args.size() == 0) {
452 diag_->Error(DiagMessage() << "no subcommand specified");
453 } else {
454 diag_->Error(DiagMessage() << "unknown subcommand '" << args[0] << "'");
455 }
456
457 Usage(&std::cerr);
458 return 1;
459}
460
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700461} // namespace aapt