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