1 /*
2  * Copyright (C) 2015 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 
17 #include "Debug.h"
18 
19 #include <androidfw/TypeWrappers.h>
20 #include <androidfw/Util.h>
21 #include <format/binary/ResChunkPullParser.h>
22 
23 #include <algorithm>
24 #include <map>
25 #include <memory>
26 #include <queue>
27 #include <set>
28 #include <vector>
29 
30 #include "ResourceTable.h"
31 #include "ResourceUtils.h"
32 #include "ResourceValues.h"
33 #include "ValueVisitor.h"
34 #include "android-base/logging.h"
35 #include "android-base/stringprintf.h"
36 #include "androidfw/ResourceTypes.h"
37 #include "idmap2/Policies.h"
38 #include "text/Printer.h"
39 #include "util/Util.h"
40 
41 using ::aapt::text::Printer;
42 using ::android::StringPiece;
43 using ::android::base::StringPrintf;
44 
45 using android::idmap2::policy::kPolicyStringToFlag;
46 
47 using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
48 
49 namespace aapt {
50 
51 namespace {
52 
53 class ValueHeadlinePrinter : public ConstValueVisitor {
54  public:
55   using ConstValueVisitor::Visit;
56 
ValueHeadlinePrinter(const std::string & package,Printer * printer)57   explicit ValueHeadlinePrinter(const std::string& package, Printer* printer)
58       : package_(package), printer_(printer) {
59   }
60 
Visit(const Attribute * attr)61   void Visit(const Attribute* attr) override {
62     printer_->Print("(attr) type=");
63     printer_->Print(attr->MaskString());
64     if (!attr->symbols.empty()) {
65       printer_->Print(StringPrintf(" size=%zd", attr->symbols.size()));
66     }
67   }
68 
Visit(const Style * style)69   void Visit(const Style* style) override {
70     printer_->Print(StringPrintf("(style) size=%zd", style->entries.size()));
71     if (style->parent) {
72       printer_->Print(" parent=");
73 
74       const Reference& parent_ref = style->parent.value();
75       if (parent_ref.name) {
76         if (parent_ref.private_reference) {
77           printer_->Print("*");
78         }
79 
80         const ResourceName& parent_name = parent_ref.name.value();
81         if (package_ != parent_name.package) {
82           printer_->Print(parent_name.package);
83           printer_->Print(":");
84         }
85         printer_->Print(parent_name.type.to_string());
86         printer_->Print("/");
87         printer_->Print(parent_name.entry);
88         if (parent_ref.id) {
89           printer_->Print(" (");
90           printer_->Print(parent_ref.id.value().to_string());
91           printer_->Print(")");
92         }
93       } else if (parent_ref.id) {
94         printer_->Print(parent_ref.id.value().to_string());
95       } else {
96         printer_->Print("???");
97       }
98     }
99   }
100 
Visit(const Array * array)101   void Visit(const Array* array) override {
102     printer_->Print(StringPrintf("(array) size=%zd", array->elements.size()));
103   }
104 
Visit(const Plural * plural)105   void Visit(const Plural* plural) override {
106     size_t count = std::count_if(plural->values.begin(), plural->values.end(),
107                                  [](const std::unique_ptr<Item>& v) { return v != nullptr; });
108     printer_->Print(StringPrintf("(plurals) size=%zd", count));
109   }
110 
Visit(const Styleable * styleable)111   void Visit(const Styleable* styleable) override {
112     printer_->Println(StringPrintf("(styleable) size=%zd", styleable->entries.size()));
113   }
114 
VisitItem(const Item * item)115   void VisitItem(const Item* item) override {
116     // Pretty much guaranteed to be one line.
117     if (const Reference* ref = ValueCast<Reference>(item)) {
118       // Special case Reference so that we can print local resources without a package name.
119       ref->PrettyPrint(package_, printer_);
120     } else {
121       item->PrettyPrint(printer_);
122     }
123   }
124 
125  private:
126   std::string package_;
127   Printer* printer_;
128 };
129 
130 class ValueBodyPrinter : public ConstValueVisitor {
131  public:
132   using ConstValueVisitor::Visit;
133 
ValueBodyPrinter(const std::string & package,Printer * printer)134   explicit ValueBodyPrinter(const std::string& package, Printer* printer)
135       : package_(package), printer_(printer) {
136   }
137 
Visit(const Attribute * attr)138   void Visit(const Attribute* attr) override {
139     constexpr uint32_t kMask = android::ResTable_map::TYPE_ENUM | android::ResTable_map::TYPE_FLAGS;
140     if (attr->type_mask & kMask) {
141       for (const auto& symbol : attr->symbols) {
142         if (symbol.symbol.name) {
143           printer_->Print(symbol.symbol.name.value().entry);
144 
145           if (symbol.symbol.id) {
146             printer_->Print("(");
147             printer_->Print(symbol.symbol.id.value().to_string());
148             printer_->Print(")");
149           }
150         } else if (symbol.symbol.id) {
151           printer_->Print(symbol.symbol.id.value().to_string());
152         } else {
153           printer_->Print("???");
154         }
155 
156         printer_->Println(StringPrintf("=0x%08x", symbol.value));
157       }
158     }
159   }
160 
Visit(const Style * style)161   void Visit(const Style* style) override {
162     for (const auto& entry : style->entries) {
163       if (entry.key.name) {
164         const ResourceName& name = entry.key.name.value();
165         if (!name.package.empty() && name.package != package_) {
166           printer_->Print(name.package);
167           printer_->Print(":");
168         }
169         printer_->Print(name.entry);
170 
171         if (entry.key.id) {
172           printer_->Print("(");
173           printer_->Print(entry.key.id.value().to_string());
174           printer_->Print(")");
175         }
176       } else if (entry.key.id) {
177         printer_->Print(entry.key.id.value().to_string());
178       } else {
179         printer_->Print("???");
180       }
181 
182       printer_->Print("=");
183       PrintItem(*entry.value);
184       printer_->Println();
185     }
186   }
187 
Visit(const Array * array)188   void Visit(const Array* array) override {
189     const size_t count = array->elements.size();
190     printer_->Print("[");
191     for (size_t i = 0u; i < count; i++) {
192       if (i != 0u && i % 4u == 0u) {
193         printer_->Println();
194         printer_->Print(" ");
195       }
196       PrintItem(*array->elements[i]);
197       if (i != count - 1) {
198         printer_->Print(", ");
199       }
200     }
201     printer_->Println("]");
202   }
203 
Visit(const Plural * plural)204   void Visit(const Plural* plural) override {
205     constexpr std::array<const char*, Plural::Count> kPluralNames = {
206         {"zero", "one", "two", "few", "many", "other"}};
207 
208     for (size_t i = 0; i < Plural::Count; i++) {
209       if (plural->values[i] != nullptr) {
210         printer_->Print(StringPrintf("%s=", kPluralNames[i]));
211         PrintItem(*plural->values[i]);
212         printer_->Println();
213       }
214     }
215   }
216 
Visit(const Styleable * styleable)217   void Visit(const Styleable* styleable) override {
218     for (const auto& attr : styleable->entries) {
219       if (attr.name) {
220         const ResourceName& name = attr.name.value();
221         if (!name.package.empty() && name.package != package_) {
222           printer_->Print(name.package);
223           printer_->Print(":");
224         }
225         printer_->Print(name.entry);
226 
227         if (attr.id) {
228           printer_->Print("(");
229           printer_->Print(attr.id.value().to_string());
230           printer_->Print(")");
231         }
232       }
233 
234       if (attr.id) {
235         printer_->Print(attr.id.value().to_string());
236       }
237       printer_->Println();
238     }
239   }
240 
VisitItem(const Item * item)241   void VisitItem(const Item* item) override {
242     // Intentionally left empty, we already printed the Items.
243   }
244 
245  private:
PrintItem(const Item & item)246   void PrintItem(const Item& item) {
247     if (const Reference* ref = ValueCast<Reference>(&item)) {
248       // Special case Reference so that we can print local resources without a package name.
249       ref->PrettyPrint(package_, printer_);
250     } else {
251       item.PrettyPrint(printer_);
252     }
253   }
254 
255   std::string package_;
256   Printer* printer_;
257 };
258 
259 }  // namespace
260 
PrintTable(const ResourceTable & table,const DebugPrintTableOptions & options,Printer * printer)261 void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions& options,
262                        Printer* printer) {
263   const auto table_view = table.GetPartitionedView();
264   for (const auto& package : table_view.packages) {
265     ValueHeadlinePrinter headline_printer(package.name, printer);
266     ValueBodyPrinter body_printer(package.name, printer);
267 
268     auto& dynamicRefTable = table.GetReferencedPackages();
269     if (!dynamicRefTable.empty()) {
270       printer->Println(StringPrintf("DynamicRefTable entryCount=%d", int(dynamicRefTable.size())));
271       printer->Indent();
272       for (auto&& [id, name] : dynamicRefTable) {
273         printer->Println(StringPrintf("0x%02x -> %s", id, name.c_str()));
274       }
275       printer->Undent();
276     }
277 
278     printer->Print("Package name=");
279     printer->Print(package.name);
280     if (package.id) {
281       printer->Print(StringPrintf(" id=%02x", package.id.value()));
282     }
283     printer->Println();
284 
285     printer->Indent();
286     for (const auto& type : package.types) {
287       printer->Print("type ");
288       printer->Print(type.named_type.to_string());
289       if (type.id) {
290         printer->Print(StringPrintf(" id=%02x", type.id.value()));
291       }
292       printer->Println(StringPrintf(" entryCount=%zd", type.entries.size()));
293 
294       printer->Indent();
295       for (const ResourceTableEntryView& entry : type.entries) {
296         printer->Print("resource ");
297         printer->Print(ResourceId(package.id.value_or(0), type.id.value_or(0), entry.id.value_or(0))
298                            .to_string());
299         printer->Print(" ");
300 
301         // Write the name without the package (this is obvious and too verbose).
302         printer->Print(type.named_type.to_string());
303         printer->Print("/");
304         printer->Print(entry.name);
305 
306         switch (entry.visibility.level) {
307           case Visibility::Level::kPublic:
308             printer->Print(" PUBLIC");
309             break;
310           case Visibility::Level::kPrivate:
311             printer->Print(" _PRIVATE_");
312             break;
313           case Visibility::Level::kUndefined:
314             // Print nothing.
315             break;
316         }
317 
318         if (entry.visibility.staged_api) {
319           printer->Print(" STAGED");
320         }
321 
322         if (entry.overlayable_item) {
323           printer->Print(" OVERLAYABLE");
324         }
325 
326         if (entry.staged_id) {
327           printer->Print(" STAGED_ID=");
328           printer->Print(entry.staged_id.value().id.to_string());
329         }
330 
331         printer->Println();
332 
333         if (options.show_values) {
334           printer->Indent();
335           for (const auto& value : entry.values) {
336             printer->Print("(");
337             printer->Print(value->config.to_string());
338             printer->Print(") ");
339             value->value->Accept(&headline_printer);
340             if (options.show_sources && !value->value->GetSource().path.empty()) {
341               printer->Print(" src=");
342               printer->Print(value->value->GetSource().to_string());
343             }
344             printer->Println();
345             printer->Indent();
346             value->value->Accept(&body_printer);
347             printer->Undent();
348           }
349           printer->Undent();
350         }
351       }
352       printer->Undent();
353     }
354     printer->Undent();
355   }
356 }
357 
GetNodeIndex(const std::vector<ResourceName> & names,const ResourceName & name)358 static size_t GetNodeIndex(const std::vector<ResourceName>& names, const ResourceName& name) {
359   auto iter = std::lower_bound(names.begin(), names.end(), name);
360   CHECK(iter != names.end());
361   CHECK(*iter == name);
362   return std::distance(names.begin(), iter);
363 }
364 
PrintStyleGraph(ResourceTable * table,const ResourceName & target_style)365 void Debug::PrintStyleGraph(ResourceTable* table, const ResourceName& target_style) {
366   std::map<ResourceName, std::set<ResourceName>> graph;
367 
368   std::queue<ResourceName> styles_to_visit;
369   styles_to_visit.push(target_style);
370   for (; !styles_to_visit.empty(); styles_to_visit.pop()) {
371     const ResourceName& style_name = styles_to_visit.front();
372     std::set<ResourceName>& parents = graph[style_name];
373     if (!parents.empty()) {
374       // We've already visited this style.
375       continue;
376     }
377 
378     std::optional<ResourceTable::SearchResult> result = table->FindResource(style_name);
379     if (result) {
380       ResourceEntry* entry = result.value().entry;
381       for (const auto& value : entry->values) {
382         if (Style* style = ValueCast<Style>(value->value.get())) {
383           if (style->parent && style->parent.value().name) {
384             parents.insert(style->parent.value().name.value());
385             styles_to_visit.push(style->parent.value().name.value());
386           }
387         }
388       }
389     }
390   }
391 
392   std::vector<ResourceName> names;
393   for (const auto& entry : graph) {
394     names.push_back(entry.first);
395   }
396 
397   std::cout << "digraph styles {\n";
398   for (const auto& name : names) {
399     std::cout << "  node_" << GetNodeIndex(names, name) << " [label=\"" << name << "\"];\n";
400   }
401 
402   for (const auto& entry : graph) {
403     const ResourceName& style_name = entry.first;
404     size_t style_node_index = GetNodeIndex(names, style_name);
405 
406     for (const auto& parent_name : entry.second) {
407       std::cout << "  node_" << style_node_index << " -> "
408                 << "node_" << GetNodeIndex(names, parent_name) << ";\n";
409     }
410   }
411 
412   std::cout << "}" << std::endl;
413 }
414 
DumpHex(const void * data,size_t len)415 void Debug::DumpHex(const void* data, size_t len) {
416   const uint8_t* d = (const uint8_t*)data;
417   for (size_t i = 0; i < len; i++) {
418     std::cerr << std::hex << std::setfill('0') << std::setw(2) << (uint32_t)d[i] << " ";
419     if (i % 8 == 7) {
420       std::cerr << "\n";
421     }
422   }
423 
424   if (len - 1 % 8 != 7) {
425     std::cerr << std::endl;
426   }
427 }
428 
DumpResStringPool(const android::ResStringPool * pool,text::Printer * printer)429 void Debug::DumpResStringPool(const android::ResStringPool* pool, text::Printer* printer) {
430   using namespace android;
431 
432   if (pool->getError() == NO_INIT) {
433     printer->Print("String pool is unitialized.\n");
434     return;
435   } else if (pool->getError() != NO_ERROR) {
436     printer->Print("String pool is corrupt/invalid.\n");
437     return;
438   }
439 
440   SortedVector<const void*> uniqueStrings;
441   const size_t N = pool->size();
442   for (size_t i=0; i<N; i++) {
443     size_t len;
444     if (pool->isUTF8()) {
445       uniqueStrings.add(UnpackOptionalString(pool->string8At(i), &len));
446     } else {
447       uniqueStrings.add(UnpackOptionalString(pool->stringAt(i), &len));
448     }
449   }
450 
451   printer->Print(StringPrintf("String pool of %zd unique %s %s strings, %zd entries and %zd styles "
452                               "using %zd bytes:\n", uniqueStrings.size(),
453                               pool->isUTF8() ? "UTF-8" : "UTF-16",
454                               pool->isSorted() ? "sorted" : "non-sorted", N, pool->styleCount(),
455                               pool->bytes()));
456 
457   const size_t NS = pool->size();
458   for (size_t s=0; s<NS; s++) {
459     auto str = pool->string8ObjectAt(s);
460     printer->Print(StringPrintf("String #%zd : %s\n", s, str.has_value() ? str->c_str() : ""));
461   }
462 }
463 
464 namespace {
465 
466 class XmlPrinter : public xml::ConstVisitor {
467  public:
468   using xml::ConstVisitor::Visit;
469 
XmlPrinter(Printer * printer)470   explicit XmlPrinter(Printer* printer) : printer_(printer) {
471   }
472 
Visit(const xml::Element * el)473   void Visit(const xml::Element* el) override {
474     for (const xml::NamespaceDecl& decl : el->namespace_decls) {
475       printer_->Println(StringPrintf("N: %s=%s (line=%zu)", decl.prefix.c_str(), decl.uri.c_str(),
476                                      decl.line_number));
477       printer_->Indent();
478     }
479 
480     printer_->Print("E: ");
481     if (!el->namespace_uri.empty()) {
482       printer_->Print(el->namespace_uri);
483       printer_->Print(":");
484     }
485     printer_->Println(StringPrintf("%s (line=%zu)", el->name.c_str(), el->line_number));
486     printer_->Indent();
487 
488     for (const xml::Attribute& attr : el->attributes) {
489       printer_->Print("A: ");
490       if (!attr.namespace_uri.empty()) {
491         printer_->Print(attr.namespace_uri);
492         printer_->Print(":");
493       }
494       printer_->Print(attr.name);
495 
496       if (attr.compiled_attribute) {
497         printer_->Print("(");
498         printer_->Print(attr.compiled_attribute.value().id.value_or(ResourceId(0)).to_string());
499         printer_->Print(")");
500       }
501       printer_->Print("=");
502       if (attr.compiled_value != nullptr) {
503         attr.compiled_value->PrettyPrint(printer_);
504       } else {
505         printer_->Print("\"");
506         printer_->Print(attr.value);
507         printer_->Print("\"");
508       }
509 
510       if (!attr.value.empty()) {
511         printer_->Print(" (Raw: \"");
512         printer_->Print(attr.value);
513         printer_->Print("\")");
514       }
515       printer_->Println();
516     }
517 
518     printer_->Indent();
519     xml::ConstVisitor::Visit(el);
520     printer_->Undent();
521     printer_->Undent();
522 
523     for (size_t i = 0; i < el->namespace_decls.size(); i++) {
524       printer_->Undent();
525     }
526   }
527 
Visit(const xml::Text * text)528   void Visit(const xml::Text* text) override {
529     printer_->Println(
530         StringPrintf("T: '%s'", android::ResTable::normalizeForOutput(text->text.c_str()).c_str()));
531   }
532 
533  private:
534   Printer* printer_;
535 };
536 
537 }  // namespace
538 
DumpXml(const xml::XmlResource & doc,Printer * printer)539 void Debug::DumpXml(const xml::XmlResource& doc, Printer* printer) {
540   XmlPrinter xml_visitor(printer);
541   doc.root->Accept(&xml_visitor);
542 }
543 
544 struct DumpOverlayableEntry {
545   std::string overlayable_section;
546   std::string policy_subsection;
547   std::string resource_name;
548 };
549 
DumpOverlayable(const ResourceTable & table,text::Printer * printer)550 void Debug::DumpOverlayable(const ResourceTable& table, text::Printer* printer) {
551   std::vector<DumpOverlayableEntry> items;
552   for (const auto& package : table.packages) {
553     for (const auto& type : package->types) {
554       for (const auto& entry : type->entries) {
555         if (entry->overlayable_item) {
556           const auto& overlayable_item = entry->overlayable_item.value();
557           const auto overlayable_section = StringPrintf(R"(name="%s" actor="%s")",
558               overlayable_item.overlayable->name.c_str(),
559               overlayable_item.overlayable->actor.c_str());
560           const auto policy_subsection = StringPrintf(R"(policies="%s")",
561               android::idmap2::policy::PoliciesToDebugString(overlayable_item.policies).c_str());
562           const auto value =
563               StringPrintf("%s/%s", type->named_type.to_string().data(), entry->name.c_str());
564           items.push_back(DumpOverlayableEntry{overlayable_section, policy_subsection, value});
565         }
566       }
567     }
568   }
569 
570   std::sort(items.begin(), items.end(),
571       [](const DumpOverlayableEntry& a, const DumpOverlayableEntry& b) {
572         if (a.overlayable_section != b.overlayable_section) {
573           return a.overlayable_section < b.overlayable_section;
574         }
575         if (a.policy_subsection != b.policy_subsection) {
576           return a.policy_subsection < b.policy_subsection;
577         }
578         return a.resource_name < b.resource_name;
579       });
580 
581   std::string last_overlayable_section;
582   std::string last_policy_subsection;
583   for (const auto& item : items) {
584     if (last_overlayable_section != item.overlayable_section) {
585       printer->Println(item.overlayable_section);
586       last_overlayable_section = item.overlayable_section;
587     }
588     if (last_policy_subsection != item.policy_subsection) {
589       printer->Indent();
590       printer->Println(item.policy_subsection);
591       last_policy_subsection = item.policy_subsection;
592       printer->Undent();
593     }
594     printer->Indent();
595     printer->Indent();
596     printer->Println(item.resource_name);
597     printer->Undent();
598     printer->Undent();
599   }
600 }
601 
602 namespace {
603 
604 using namespace android;
605 
606 class ChunkPrinter {
607  public:
ChunkPrinter(const void * data,size_t len,Printer * printer,android::IDiagnostics * diag)608   ChunkPrinter(const void* data, size_t len, Printer* printer, android::IDiagnostics* diag)
609       : data_(data), data_len_(len), printer_(printer), diag_(diag) {
610   }
611 
PrintChunkHeader(const ResChunk_header * chunk)612   void PrintChunkHeader(const ResChunk_header* chunk) {
613     switch (android::util::DeviceToHost16(chunk->type)) {
614       case RES_STRING_POOL_TYPE:
615         printer_->Print("[RES_STRING_POOL_TYPE]");
616         break;
617       case RES_TABLE_LIBRARY_TYPE:
618         printer_->Print("[RES_TABLE_LIBRARY_TYPE]");
619         break;
620       case RES_TABLE_TYPE:
621         printer_->Print("[ResTable_header]");
622         break;
623       case RES_TABLE_PACKAGE_TYPE:
624         printer_->Print("[ResTable_package]");
625         break;
626       case RES_TABLE_TYPE_TYPE:
627         printer_->Print("[ResTable_type]");
628         break;
629       case RES_TABLE_TYPE_SPEC_TYPE:
630         printer_->Print("[RES_TABLE_TYPE_SPEC_TYPE]");
631         break;
632       default:
633         break;
634     }
635 
636     printer_->Print(StringPrintf(" chunkSize: %u", android::util::DeviceToHost32(chunk->size)));
637     printer_->Print(
638         StringPrintf(" headerSize: %u", android::util::DeviceToHost32(chunk->headerSize)));
639   }
640 
PrintTable(const ResTable_header * chunk)641   bool PrintTable(const ResTable_header* chunk) {
642     printer_->Print(
643         StringPrintf(" Package count: %u\n", android::util::DeviceToHost32(chunk->packageCount)));
644 
645     // Print the chunks contained within the table
646     printer_->Indent();
647     bool success = PrintChunk(
648         ResChunkPullParser(GetChunkData(&chunk->header), GetChunkDataLen(&chunk->header)));
649     printer_->Undent();
650     return success;
651   }
652 
PrintResValue(const Res_value * value,const ConfigDescription & config,const ResourceType * type)653   void PrintResValue(const Res_value* value, const ConfigDescription& config,
654                      const ResourceType* type) {
655     printer_->Print("[Res_value]");
656     printer_->Print(StringPrintf(" size: %u", android::util::DeviceToHost32(value->size)));
657     printer_->Print(
658         StringPrintf(" dataType: 0x%02x", android::util::DeviceToHost32(value->dataType)));
659     printer_->Print(StringPrintf(" data: 0x%08x", android::util::DeviceToHost32(value->data)));
660 
661     if (type) {
662       auto item =
663           ResourceUtils::ParseBinaryResValue(*type, config, value_pool_, *value, &out_pool_);
664       printer_->Print(" (");
665       item->PrettyPrint(printer_);
666       printer_->Print(")");
667     }
668 
669     printer_->Print("\n");
670   }
671 
PrintTableType(const ResTable_type * chunk)672   bool PrintTableType(const ResTable_type* chunk) {
673     printer_->Print(StringPrintf(" id: 0x%02x", android::util::DeviceToHost32(chunk->id)));
674     printer_->Print(StringPrintf(
675         " name: %s",
676         android::util::GetString(type_pool_, android::util::DeviceToHost32(chunk->id) - 1)
677             .c_str()));
678     printer_->Print(StringPrintf(" flags: 0x%02x", android::util::DeviceToHost32(chunk->flags)));
679     printer_->Print(
680         StringPrintf(" entryCount: %u", android::util::DeviceToHost32(chunk->entryCount)));
681     printer_->Print(
682         StringPrintf(" entryStart: %u", android::util::DeviceToHost32(chunk->entriesStart)));
683 
684     ConfigDescription config;
685     config.copyFromDtoH(chunk->config);
686     printer_->Print(StringPrintf(" config: %s\n", config.to_string().c_str()));
687 
688     const ResourceType* type = ParseResourceType(
689         android::util::GetString(type_pool_, android::util::DeviceToHost32(chunk->id) - 1));
690 
691     printer_->Indent();
692 
693     TypeVariant tv(chunk);
694     for (auto it = tv.beginEntries(); it != tv.endEntries(); ++it) {
695       const ResTable_entry* entry = *it;
696       if (!entry) {
697         continue;
698       }
699 
700       if (entry->is_complex()) {
701         printer_->Print("[ResTable_map_entry]");
702       } else if (entry->is_compact()) {
703         printer_->Print("[ResTable_entry_compact]");
704       } else {
705         printer_->Print("[ResTable_entry]");
706       }
707 
708       printer_->Print(StringPrintf(" id: 0x%04x", it.index()));
709       printer_->Print(StringPrintf(
710           " name: %s", android::util::GetString(key_pool_, entry->key()).c_str()));
711       printer_->Print(StringPrintf(" keyIndex: %u", entry->key()));
712       printer_->Print(StringPrintf(" size: %zu", entry->size()));
713       printer_->Print(StringPrintf(" flags: 0x%04x", entry->flags()));
714 
715       printer_->Indent();
716 
717       if (auto map_entry = entry->map_entry()) {
718         uint32_t map_entry_count = android::util::DeviceToHost32(map_entry->count);
719         printer_->Print(StringPrintf(" count: 0x%04x", map_entry_count));
720         printer_->Print(StringPrintf(" parent: 0x%08x\n",
721                                      android::util::DeviceToHost32(map_entry->parent.ident)));
722 
723         // Print the name and value mappings
724         auto maps = (const ResTable_map*)((const uint8_t*)entry + entry->size());
725         for (size_t i = 0; i < map_entry_count; i++) {
726           PrintResValue(&(maps[i].value), config, type);
727 
728           printer_->Print(StringPrintf(
729               " name: %s name-id:%d\n",
730               android::util::GetString(key_pool_, android::util::DeviceToHost32(maps[i].name.ident))
731                   .c_str(),
732               android::util::DeviceToHost32(maps[i].name.ident)));
733         }
734       } else {
735         printer_->Print("\n");
736 
737         // Print the value of the entry
738         Res_value value = entry->value();
739         PrintResValue(&value, config, type);
740       }
741 
742       printer_->Undent();
743     }
744 
745     printer_->Undent();
746     return true;
747   }
748 
PrintStringPool(const ResStringPool_header * chunk)749   void PrintStringPool(const ResStringPool_header* chunk) {
750     // Initialize the string pools
751 
752     ResStringPool* pool;
753     if (value_pool_.getError() == NO_INIT) {
754       pool = &value_pool_;
755     } else if (type_pool_.getError() == NO_INIT) {
756       pool = &type_pool_;
757     } else if (key_pool_.getError() == NO_INIT) {
758       pool = &key_pool_;
759     } else {
760       return;
761     }
762 
763     pool->setTo(chunk, android::util::DeviceToHost32(
764                            (reinterpret_cast<const ResChunk_header*>(chunk))->size));
765 
766     printer_->Print("\n");
767 
768     for (size_t i = 0; i < pool->size(); i++) {
769       printer_->Print(StringPrintf("#%zd : %s\n", i, android::util::GetString(*pool, i).c_str()));
770     }
771   }
772 
PrintPackage(const ResTable_package * chunk)773   bool PrintPackage(const ResTable_package* chunk) {
774     printer_->Print(StringPrintf(" id: 0x%02x", android::util::DeviceToHost32(chunk->id)));
775 
776     size_t len = strnlen16((const char16_t*)chunk->name, std::size(chunk->name));
777     std::u16string package_name(len, u'\0');
778     package_name.resize(len);
779     for (size_t i = 0; i < len; i++) {
780       package_name[i] = android::util::DeviceToHost16(chunk->name[i]);
781     }
782 
783     printer_->Print(StringPrintf("name: %s", String8(package_name.c_str()).c_str()));
784     printer_->Print(
785         StringPrintf(" typeStrings: %u", android::util::DeviceToHost32(chunk->typeStrings)));
786     printer_->Print(
787         StringPrintf(" lastPublicType: %u", android::util::DeviceToHost32(chunk->lastPublicType)));
788     printer_->Print(
789         StringPrintf(" keyStrings: %u", android::util::DeviceToHost32(chunk->keyStrings)));
790     printer_->Print(
791         StringPrintf(" lastPublicKey: %u", android::util::DeviceToHost32(chunk->lastPublicKey)));
792     printer_->Print(
793         StringPrintf(" typeIdOffset: %u\n", android::util::DeviceToHost32(chunk->typeIdOffset)));
794 
795     // Print the chunks contained within the table
796     printer_->Indent();
797     bool success = PrintChunk(
798         ResChunkPullParser(GetChunkData(&chunk->header), GetChunkDataLen(&chunk->header)));
799     printer_->Undent();
800     return success;
801   }
802 
PrintChunk(ResChunkPullParser && parser)803   bool PrintChunk(ResChunkPullParser&& parser) {
804     while (ResChunkPullParser::IsGoodEvent(parser.Next())) {
805       auto chunk = parser.chunk();
806       PrintChunkHeader(chunk);
807 
808       switch (android::util::DeviceToHost16(chunk->type)) {
809         case RES_STRING_POOL_TYPE:
810           PrintStringPool(reinterpret_cast<const ResStringPool_header*>(chunk));
811           break;
812 
813         case RES_TABLE_TYPE:
814           PrintTable(reinterpret_cast<const ResTable_header*>(chunk));
815           break;
816 
817         case RES_TABLE_PACKAGE_TYPE:
818           type_pool_.uninit();
819           key_pool_.uninit();
820           PrintPackage(reinterpret_cast<const ResTable_package*>(chunk));
821           break;
822 
823         case RES_TABLE_TYPE_TYPE:
824           PrintTableType(reinterpret_cast<const ResTable_type*>(chunk));
825           break;
826 
827         default:
828           printer_->Print("\n");
829           break;
830       }
831     }
832 
833     if (parser.event() == ResChunkPullParser::Event::kBadDocument) {
834       diag_->Error(android::DiagMessage(source_) << "corrupt resource table: " << parser.error());
835       return false;
836     }
837 
838     return true;
839   }
840 
Print()841   void Print() {
842     PrintChunk(ResChunkPullParser(data_, data_len_));
843     printer_->Print("[End]\n");
844   }
845 
846  private:
847   const android::Source source_;
848   const void* data_;
849   const size_t data_len_;
850   Printer* printer_;
851   android::IDiagnostics* diag_;
852 
853   // The standard value string pool for resource values.
854   ResStringPool value_pool_;
855 
856   // The string pool that holds the names of the types defined
857   // in this table.
858   ResStringPool type_pool_;
859 
860   // The string pool that holds the names of the entries defined
861   // in this table.
862   ResStringPool key_pool_;
863 
864   android::StringPool out_pool_;
865 };
866 
867 }  // namespace
868 
DumpChunks(const void * data,size_t len,Printer * printer,android::IDiagnostics * diag)869 void Debug::DumpChunks(const void* data, size_t len, Printer* printer,
870                        android::IDiagnostics* diag) {
871   ChunkPrinter chunk_printer(data, len, printer, diag);
872   chunk_printer.Print();
873 }
874 
875 }  // namespace aapt
876