blob: b4311c56428b5a39b09b52071feca65ff8028860 [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
226 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
227 Printer printer(&fout);
228
229 // Comparison function used to order configurations
230 auto compare = [](ConfigDescription c1, ConfigDescription c2) -> bool {
231 return c1.compare(c2) < 0;
232 };
233
234 // Insert the configurations into a set in order to keep every configuarion seen
235 std::set<ConfigDescription, decltype(compare)> configs(compare);
236 for (auto& package : loaded_apk->GetResourceTable()->packages) {
237 for (auto& type : package->types) {
238 for (auto& entry : type->entries) {
239 for (auto& value : entry->values) {
240 configs.insert(value->config);
241 }
242 }
243 }
244 }
245
246 // Print the configurations in order
247 for (auto& config : configs) {
248 printer.Print(StringPrintf("%s\n", config.to_string().data()));
249 }
250
251 return 0;
252}
253
254int DumpStringsCommand::Action(const std::vector<std::string>& args) {
255 DumpContext context;
256 if (args.size() < 1) {
257 diag_->Error(DiagMessage() << "No dump apk specified.");
258 return 1;
259 }
260
261 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
262 Printer printer(&fout);
263
264 for (auto apk : args) {
265 auto loaded_apk = LoadedApk::LoadApkFromPath(apk, diag_);
266 if (!loaded_apk) {
267 return 1;
268 }
269
270 // Load the run-time xml string pool using the flattened data
271 BigBuffer buffer(4096);
272 StringPool::FlattenUtf8(&buffer, loaded_apk->GetResourceTable()->string_pool,
273 context.GetDiagnostics());
274 auto data = buffer.to_string();
275 android::ResStringPool pool(data.data(), data.size(), false);
276 Debug::DumpResStringPool(&pool, &printer);
277 }
278
279 return 0;
280}
281
282int DumpTableCommand::Action(const std::vector<std::string>& args) {
283 if (args.size() < 1) {
284 diag_->Error(DiagMessage() << "No dump apk specified.");
285 return 1;
286 }
287
288 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
289 Printer printer(&fout);
290
291 DebugPrintTableOptions print_options;
292 print_options.show_sources = true;
293 print_options.show_values = !no_values_;
294
295 for (auto apk : args) {
296 auto loaded_apk = LoadedApk::LoadApkFromPath(apk, diag_);
297 if (!loaded_apk) {
298 return 1;
299 }
300
301 if (loaded_apk->GetApkFormat()) {
302 printer.Println("Proto APK");
303 } else {
304 printer.Println("Binary APK");
305 }
306
307 Debug::PrintTable(*loaded_apk->GetResourceTable(), print_options, &printer);
308 }
309
310 return 0;
311}
312
313int DumpXmlTreeCommand::Action(const std::vector<std::string>& args) {
314 if (args.size() < 1) {
315 diag_->Error(DiagMessage() << "No dump apk specified");
316 return 1;
317 }
318
319 auto loaded_apk = LoadedApk::LoadApkFromPath(args[0], diag_);
320 if (!loaded_apk) {
321 return 1;
322 }
323
324 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
325 Printer printer(&fout);
326
327 // Dump the xml tree of every passed in file
328 for (auto file : files_) {
329 auto xml = loaded_apk->LoadXml(file, diag_);
330 if (!xml) {
331 return 1;
332 }
333
334 Debug::DumpXml(*xml, &printer);
335 }
336
337 return 0;
338}
339
340int DumpXmlStringsCommand::Action(const std::vector<std::string>& args) {
341 DumpContext context;
342 if (args.size() < 1) {
343 diag_->Error(DiagMessage() << "No dump apk specified.");
344 return 1;
345 }
346
347 auto loaded_apk = LoadedApk::LoadApkFromPath(args[0], diag_);
348 if (!loaded_apk) {
349 return 1;
350 }
351
352 io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
353 Printer printer(&fout);
354
355 // Dump the xml strings of every passed in file
356 for (auto xml_file : files_) {
357 android::ResXMLTree tree;
358
359 if (loaded_apk->GetApkFormat() == kProto) {
360 auto xml = loaded_apk->LoadXml(xml_file, diag_);
361 if (!xml) {
362 return 1;
363 }
364
365 // Flatten the xml document to get a binary representation of the proto xml file
366 BigBuffer buffer(4096);
367 XmlFlattenerOptions options = {};
368 options.keep_raw_values = true;
369 XmlFlattener flattener(&buffer, options);
370 if (!flattener.Consume(&context, xml.get())) {
371 return 1;
372 }
373
374 // Load the run-time xml tree using the flattened data
375 std::string data = buffer.to_string();
376 tree.setTo(data.data(), data.size(), /** copyData */ true);
377
378 } else if (loaded_apk->GetApkFormat() == kBinary) {
379 io::IFile* file = loaded_apk->GetFileCollection()->FindFile(xml_file);
380 if (!file) {
381 diag_->Error(DiagMessage(xml_file) << "file '" << xml_file << "' not found in APK");
382 return 1;
383 }
384
385 std::unique_ptr<io::IData> data = file->OpenAsData();
386 if (!data) {
387 diag_->Error(DiagMessage() << "failed to open file");
388 return 1;
389 }
390
391 // Load the run-time xml tree from the file data
392 tree.setTo(data->data(), data->size(), /** copyData */ true);
393 }
394
395 Debug::DumpResStringPool(&tree.getStrings(), &printer);
396 }
397
398 return 0;
399}
400
401/** Preform no action because a subcommand is required. */
402int DumpCommand::Action(const std::vector<std::string>& args) {
403 if (args.size() == 0) {
404 diag_->Error(DiagMessage() << "no subcommand specified");
405 } else {
406 diag_->Error(DiagMessage() << "unknown subcommand '" << args[0] << "'");
407 }
408
409 Usage(&std::cerr);
410 return 1;
411}
412
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700413} // namespace aapt