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 "ResourceParser.h"
18 
19 #include <functional>
20 #include <limits>
21 #include <sstream>
22 
23 #include <android-base/logging.h>
24 #include <idmap2/Policies.h>
25 
26 #include "ResourceTable.h"
27 #include "ResourceUtils.h"
28 #include "ResourceValues.h"
29 #include "ValueVisitor.h"
30 #include "text/Utf8Iterator.h"
31 #include "util/ImmutableMap.h"
32 
33 #include "util/Util.h"
34 #include "xml/XmlPullParser.h"
35 
36 using ::aapt::ResourceUtils::StringBuilder;
37 using ::aapt::text::Utf8Iterator;
38 using ::android::ConfigDescription;
39 using ::android::StringPiece;
40 
41 using android::idmap2::policy::kPolicyStringToFlag;
42 
43 namespace aapt {
44 namespace {
45 constexpr const char* kPublicGroupTag = "public-group";
46 constexpr const char* kStagingPublicGroupTag = "staging-public-group";
47 constexpr const char* kStagingPublicGroupFinalTag = "staging-public-group-final";
48 }  // namespace
49 
50 constexpr const char* sXliffNamespaceUri = "urn:oasis:names:tc:xliff:document:1.2";
51 
52 // Returns true if the element is <skip> or <eat-comment> and can be safely ignored.
ShouldIgnoreElement(StringPiece ns,StringPiece name)53 static bool ShouldIgnoreElement(StringPiece ns, StringPiece name) {
54   return ns.empty() && (name == "skip" || name == "eat-comment");
55 }
56 
ParseFormatTypeNoEnumsOrFlags(StringPiece piece)57 static uint32_t ParseFormatTypeNoEnumsOrFlags(StringPiece piece) {
58   if (piece == "reference") {
59     return android::ResTable_map::TYPE_REFERENCE;
60   } else if (piece == "string") {
61     return android::ResTable_map::TYPE_STRING;
62   } else if (piece == "integer") {
63     return android::ResTable_map::TYPE_INTEGER;
64   } else if (piece == "boolean") {
65     return android::ResTable_map::TYPE_BOOLEAN;
66   } else if (piece == "color") {
67     return android::ResTable_map::TYPE_COLOR;
68   } else if (piece == "float") {
69     return android::ResTable_map::TYPE_FLOAT;
70   } else if (piece == "dimension") {
71     return android::ResTable_map::TYPE_DIMENSION;
72   } else if (piece == "fraction") {
73     return android::ResTable_map::TYPE_FRACTION;
74   }
75   return 0;
76 }
77 
ParseFormatType(StringPiece piece)78 static uint32_t ParseFormatType(StringPiece piece) {
79   if (piece == "enum") {
80     return android::ResTable_map::TYPE_ENUM;
81   } else if (piece == "flags") {
82     return android::ResTable_map::TYPE_FLAGS;
83   }
84   return ParseFormatTypeNoEnumsOrFlags(piece);
85 }
86 
ParseFormatAttribute(StringPiece str)87 static uint32_t ParseFormatAttribute(StringPiece str) {
88   uint32_t mask = 0;
89   for (StringPiece part : util::Tokenize(str, '|')) {
90     StringPiece trimmed_part = util::TrimWhitespace(part);
91     uint32_t type = ParseFormatType(trimmed_part);
92     if (type == 0) {
93       return 0;
94     }
95     mask |= type;
96   }
97   return mask;
98 }
99 
100 // A parsed resource ready to be added to the ResourceTable.
101 struct ParsedResource {
102   ResourceName name;
103   ConfigDescription config;
104   std::string product;
105   android::Source source;
106 
107   ResourceId id;
108   Visibility::Level visibility_level = Visibility::Level::kUndefined;
109   bool staged_api = false;
110   bool allow_new = false;
111   std::optional<OverlayableItem> overlayable_item;
112   std::optional<StagedId> staged_alias;
113 
114   std::string comment;
115   std::unique_ptr<Value> value;
116   std::list<ParsedResource> child_resources;
117 };
118 
119 // Recursively adds resources to the ResourceTable.
AddResourcesToTable(ResourceTable * table,android::IDiagnostics * diag,ParsedResource * res)120 static bool AddResourcesToTable(ResourceTable* table, android::IDiagnostics* diag,
121                                 ParsedResource* res) {
122   StringPiece trimmed_comment = util::TrimWhitespace(res->comment);
123   if (trimmed_comment.size() != res->comment.size()) {
124     // Only if there was a change do we re-assign.
125     res->comment = std::string(trimmed_comment);
126   }
127 
128   NewResourceBuilder res_builder(res->name);
129   if (res->visibility_level != Visibility::Level::kUndefined) {
130     Visibility visibility;
131     visibility.level = res->visibility_level;
132     visibility.staged_api = res->staged_api;
133     visibility.source = res->source;
134     visibility.comment = res->comment;
135     res_builder.SetVisibility(visibility);
136   }
137 
138   if (res->id.is_valid()) {
139     res_builder.SetId(res->id);
140   }
141 
142   if (res->allow_new) {
143     AllowNew allow_new;
144     allow_new.source = res->source;
145     allow_new.comment = res->comment;
146     res_builder.SetAllowNew(allow_new);
147   }
148 
149   if (res->overlayable_item) {
150     res_builder.SetOverlayable(res->overlayable_item.value());
151   }
152 
153   if (res->value != nullptr) {
154     // Attach the comment, source and config to the value.
155     res->value->SetComment(std::move(res->comment));
156     res->value->SetSource(std::move(res->source));
157     res_builder.SetValue(std::move(res->value), res->config, res->product);
158   }
159 
160   if (res->staged_alias) {
161     res_builder.SetStagedId(res->staged_alias.value());
162   }
163 
164   bool error = false;
165   if (!res->name.entry.empty()) {
166     if (!table->AddResource(res_builder.Build(), diag)) {
167       return false;
168     }
169   }
170   for (ParsedResource& child : res->child_resources) {
171     error |= !AddResourcesToTable(table, diag, &child);
172   }
173   return !error;
174 }
175 
176 // Convenient aliases for more readable function calls.
177 enum { kAllowRawString = true, kNoRawString = false };
178 
ResourceParser(android::IDiagnostics * diag,ResourceTable * table,const android::Source & source,const ConfigDescription & config,const ResourceParserOptions & options)179 ResourceParser::ResourceParser(android::IDiagnostics* diag, ResourceTable* table,
180                                const android::Source& source, const ConfigDescription& config,
181                                const ResourceParserOptions& options)
182     : diag_(diag), table_(table), source_(source), config_(config), options_(options) {
183 }
184 
185 // Base class Node for representing the various Spans and UntranslatableSections of an XML string.
186 // This will be used to traverse and flatten the XML string into a single std::string, with all
187 // Span and Untranslatable data maintained in parallel, as indices into the string.
188 class Node {
189  public:
190   virtual ~Node() = default;
191 
192   // Adds the given child node to this parent node's set of child nodes, moving ownership to the
193   // parent node as well.
194   // Returns a pointer to the child node that was added as a convenience.
195   template <typename T>
AddChild(std::unique_ptr<T> node)196   T* AddChild(std::unique_ptr<T> node) {
197     T* raw_ptr = node.get();
198     children.push_back(std::move(node));
199     return raw_ptr;
200   }
201 
Build(StringBuilder * builder) const202   virtual void Build(StringBuilder* builder) const {
203     for (const auto& child : children) {
204       child->Build(builder);
205     }
206   }
207 
208   std::vector<std::unique_ptr<Node>> children;
209 };
210 
211 // A chunk of text in the XML string. This lives between other tags, such as XLIFF tags and Spans.
212 class SegmentNode : public Node {
213  public:
214   std::string data;
215 
Build(StringBuilder * builder) const216   void Build(StringBuilder* builder) const override {
217     builder->AppendText(data);
218   }
219 };
220 
221 // A tag that will be encoded into the final flattened string. Tags like <b> or <i>.
222 class SpanNode : public Node {
223  public:
224   std::string name;
225 
Build(StringBuilder * builder) const226   void Build(StringBuilder* builder) const override {
227     StringBuilder::SpanHandle span_handle = builder->StartSpan(name);
228     Node::Build(builder);
229     builder->EndSpan(span_handle);
230   }
231 };
232 
233 // An XLIFF 'g' tag, which marks a section of the string as untranslatable.
234 class UntranslatableNode : public Node {
235  public:
Build(StringBuilder * builder) const236   void Build(StringBuilder* builder) const override {
237     StringBuilder::UntranslatableHandle handle = builder->StartUntranslatable();
238     Node::Build(builder);
239     builder->EndUntranslatable(handle);
240   }
241 };
242 
243 // Build a string from XML that converts nested elements into Span objects.
FlattenXmlSubtree(xml::XmlPullParser * parser,std::string * out_raw_string,android::StyleString * out_style_string,std::vector<UntranslatableSection> * out_untranslatable_sections)244 bool ResourceParser::FlattenXmlSubtree(
245     xml::XmlPullParser* parser, std::string* out_raw_string, android::StyleString* out_style_string,
246     std::vector<UntranslatableSection>* out_untranslatable_sections) {
247   std::string raw_string;
248   std::string current_text;
249 
250   // The first occurrence of a <xliff:g> tag. Nested <xliff:g> tags are illegal.
251   std::optional<size_t> untranslatable_start_depth;
252 
253   Node root;
254   std::vector<Node*> node_stack;
255   node_stack.push_back(&root);
256 
257   bool saw_span_node = false;
258   SegmentNode* first_segment = nullptr;
259   SegmentNode* last_segment = nullptr;
260 
261   size_t depth = 1;
262   while (depth > 0 && xml::XmlPullParser::IsGoodEvent(parser->Next())) {
263     const xml::XmlPullParser::Event event = parser->event();
264 
265     // First take care of any SegmentNodes that should be created.
266     if (event == xml::XmlPullParser::Event::kStartElement
267         || event == xml::XmlPullParser::Event::kEndElement) {
268       if (!current_text.empty()) {
269         auto segment_node = util::make_unique<SegmentNode>();
270         segment_node->data = std::move(current_text);
271 
272         last_segment = node_stack.back()->AddChild(std::move(segment_node));
273         if (first_segment == nullptr) {
274           first_segment = last_segment;
275         }
276         current_text = {};
277       }
278     }
279 
280     switch (event) {
281       case xml::XmlPullParser::Event::kText: {
282         current_text += parser->text();
283         raw_string += parser->text();
284       } break;
285 
286       case xml::XmlPullParser::Event::kStartElement: {
287         if (parser->element_namespace().empty()) {
288           // This is an HTML tag which we encode as a span. Add it to the span stack.
289           std::unique_ptr<SpanNode> span_node = util::make_unique<SpanNode>();
290           span_node->name = parser->element_name();
291           const auto end_attr_iter = parser->end_attributes();
292           for (auto attr_iter = parser->begin_attributes(); attr_iter != end_attr_iter;
293                ++attr_iter) {
294             span_node->name += ";";
295             span_node->name += attr_iter->name;
296             span_node->name += "=";
297             span_node->name += attr_iter->value;
298           }
299 
300           node_stack.push_back(node_stack.back()->AddChild(std::move(span_node)));
301           saw_span_node = true;
302         } else if (parser->element_namespace() == sXliffNamespaceUri) {
303           // This is an XLIFF tag, which is not encoded as a span.
304           if (parser->element_name() == "g") {
305             // Check that an 'untranslatable' tag is not already being processed. Nested
306             // <xliff:g> tags are illegal.
307             if (untranslatable_start_depth) {
308               diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
309                            << "illegal nested XLIFF 'g' tag");
310               return false;
311             } else {
312               // Mark the beginning of an 'untranslatable' section.
313               untranslatable_start_depth = depth;
314               node_stack.push_back(
315                   node_stack.back()->AddChild(util::make_unique<UntranslatableNode>()));
316             }
317           } else {
318             // Ignore unknown XLIFF tags, but don't warn.
319             node_stack.push_back(node_stack.back()->AddChild(util::make_unique<Node>()));
320           }
321         } else {
322           // Besides XLIFF, any other namespaced tag is unsupported and ignored.
323           diag_->Warn(android::DiagMessage(source_.WithLine(parser->line_number()))
324                       << "ignoring element '" << parser->element_name()
325                       << "' with unknown namespace '" << parser->element_namespace() << "'");
326           node_stack.push_back(node_stack.back()->AddChild(util::make_unique<Node>()));
327         }
328 
329         // Enter one level inside the element.
330         depth++;
331       } break;
332 
333       case xml::XmlPullParser::Event::kEndElement: {
334         // Return one level from within the element.
335         depth--;
336         if (depth == 0) {
337           break;
338         }
339 
340         node_stack.pop_back();
341         if (untranslatable_start_depth == depth) {
342           // This is the end of an untranslatable section.
343           untranslatable_start_depth = {};
344         }
345       } break;
346 
347       default:
348         // ignore.
349         break;
350     }
351   }
352 
353   // Validity check to make sure we processed all the nodes.
354   CHECK(node_stack.size() == 1u);
355   CHECK(node_stack.back() == &root);
356 
357   if (!saw_span_node) {
358     // If there were no spans, we must treat this string a little differently (according to AAPT).
359     // Find and strip the leading whitespace from the first segment, and the trailing whitespace
360     // from the last segment.
361     if (first_segment != nullptr) {
362       // Trim leading whitespace.
363       StringPiece trimmed = util::TrimLeadingWhitespace(first_segment->data);
364       if (trimmed.size() != first_segment->data.size()) {
365         first_segment->data = std::string(trimmed);
366       }
367     }
368 
369     if (last_segment != nullptr) {
370       // Trim trailing whitespace.
371       StringPiece trimmed = util::TrimTrailingWhitespace(last_segment->data);
372       if (trimmed.size() != last_segment->data.size()) {
373         last_segment->data = std::string(trimmed);
374       }
375     }
376   }
377 
378   // Have the XML structure flatten itself into the StringBuilder. The StringBuilder will take
379   // care of recording the correctly adjusted Spans and UntranslatableSections.
380   StringBuilder builder;
381   root.Build(&builder);
382   if (!builder) {
383     diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
384                  << builder.GetError());
385     return false;
386   }
387 
388   ResourceUtils::FlattenedXmlString flattened_string = builder.GetFlattenedString();
389   *out_raw_string = std::move(raw_string);
390   *out_untranslatable_sections = std::move(flattened_string.untranslatable_sections);
391   out_style_string->str = std::move(flattened_string.text);
392   out_style_string->spans = std::move(flattened_string.spans);
393   return true;
394 }
395 
Parse(xml::XmlPullParser * parser)396 bool ResourceParser::Parse(xml::XmlPullParser* parser) {
397   bool error = false;
398   const size_t depth = parser->depth();
399   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
400     if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
401       // Skip comments and text.
402       continue;
403     }
404 
405     if (!parser->element_namespace().empty() || parser->element_name() != "resources") {
406       diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
407                    << "root element must be <resources>");
408       return false;
409     }
410 
411     error |= !ParseResources(parser);
412     break;
413   };
414 
415   if (parser->event() == xml::XmlPullParser::Event::kBadDocument) {
416     diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
417                  << "xml parser error: " << parser->error());
418     return false;
419   }
420   return !error;
421 }
422 
ParseResources(xml::XmlPullParser * parser)423 bool ResourceParser::ParseResources(xml::XmlPullParser* parser) {
424   std::set<ResourceName> stripped_resources;
425 
426   bool error = false;
427   std::string comment;
428   const size_t depth = parser->depth();
429   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
430     const xml::XmlPullParser::Event event = parser->event();
431     if (event == xml::XmlPullParser::Event::kComment) {
432       comment = parser->comment();
433       continue;
434     }
435 
436     if (event == xml::XmlPullParser::Event::kText) {
437       if (!util::TrimWhitespace(parser->text()).empty()) {
438         diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
439                      << "plain text not allowed here");
440         error = true;
441       }
442       continue;
443     }
444 
445     CHECK(event == xml::XmlPullParser::Event::kStartElement);
446 
447     if (!parser->element_namespace().empty()) {
448       // Skip unknown namespace.
449       continue;
450     }
451 
452     std::string element_name = parser->element_name();
453     if (element_name == "skip" || element_name == "eat-comment") {
454       comment = "";
455       continue;
456     }
457 
458     ParsedResource parsed_resource;
459     parsed_resource.config = config_;
460     parsed_resource.source = source_.WithLine(parser->line_number());
461     parsed_resource.comment = std::move(comment);
462     comment.clear();
463     if (options_.visibility) {
464       parsed_resource.visibility_level = options_.visibility.value();
465     }
466 
467     // Extract the product name if it exists.
468     if (std::optional<StringPiece> maybe_product = xml::FindNonEmptyAttribute(parser, "product")) {
469       parsed_resource.product = std::string(maybe_product.value());
470     }
471 
472     // Parse the resource regardless of product.
473     if (!ParseResource(parser, &parsed_resource)) {
474       error = true;
475       continue;
476     }
477 
478     if (!AddResourcesToTable(table_, diag_, &parsed_resource)) {
479       error = true;
480     }
481   }
482 
483   // Check that we included at least one variant of each stripped resource.
484   for (const ResourceName& stripped_resource : stripped_resources) {
485     if (!table_->FindResource(stripped_resource)) {
486       // Failed to find the resource.
487       diag_->Error(android::DiagMessage(source_)
488                    << "resource '" << stripped_resource
489                    << "' was filtered out but no product variant remains");
490       error = true;
491     }
492   }
493 
494   return !error;
495 }
496 
ParseResource(xml::XmlPullParser * parser,ParsedResource * out_resource)497 bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
498                                    ParsedResource* out_resource) {
499   struct ItemTypeFormat {
500     ResourceType type;
501     uint32_t format;
502   };
503 
504   using BagParseFunc = std::function<bool(ResourceParser*, xml::XmlPullParser*,
505                                           ParsedResource*)>;
506 
507   static const auto elToItemMap = ImmutableMap<std::string, ItemTypeFormat>::CreatePreSorted({
508       {"bool", {ResourceType::kBool, android::ResTable_map::TYPE_BOOLEAN}},
509       {"color", {ResourceType::kColor, android::ResTable_map::TYPE_COLOR}},
510       {"configVarying", {ResourceType::kConfigVarying, android::ResTable_map::TYPE_ANY}},
511       {"dimen",
512        {ResourceType::kDimen,
513         android::ResTable_map::TYPE_FLOAT | android::ResTable_map::TYPE_FRACTION |
514             android::ResTable_map::TYPE_DIMENSION}},
515       {"drawable", {ResourceType::kDrawable, android::ResTable_map::TYPE_COLOR}},
516       {"fraction",
517        {ResourceType::kFraction,
518         android::ResTable_map::TYPE_FLOAT | android::ResTable_map::TYPE_FRACTION |
519             android::ResTable_map::TYPE_DIMENSION}},
520       {"integer", {ResourceType::kInteger, android::ResTable_map::TYPE_INTEGER}},
521       {"string", {ResourceType::kString, android::ResTable_map::TYPE_STRING}},
522   });
523 
524   static const auto elToBagMap = ImmutableMap<std::string, BagParseFunc>::CreatePreSorted({
525       {"add-resource", std::mem_fn(&ResourceParser::ParseAddResource)},
526       {"array", std::mem_fn(&ResourceParser::ParseArray)},
527       {"attr", std::mem_fn(&ResourceParser::ParseAttr)},
528       {"configVarying",
529        std::bind(&ResourceParser::ParseStyle, std::placeholders::_1, ResourceType::kConfigVarying,
530                  std::placeholders::_2, std::placeholders::_3)},
531       {"declare-styleable", std::mem_fn(&ResourceParser::ParseDeclareStyleable)},
532       {"integer-array", std::mem_fn(&ResourceParser::ParseIntegerArray)},
533       {"java-symbol", std::mem_fn(&ResourceParser::ParseSymbol)},
534       {"overlayable", std::mem_fn(&ResourceParser::ParseOverlayable)},
535       {"plurals", std::mem_fn(&ResourceParser::ParsePlural)},
536       {"public", std::mem_fn(&ResourceParser::ParsePublic)},
537       {"public-group", std::mem_fn(&ResourceParser::ParsePublicGroup)},
538       {"staging-public-group", std::mem_fn(&ResourceParser::ParseStagingPublicGroup)},
539       {"staging-public-group-final", std::mem_fn(&ResourceParser::ParseStagingPublicGroupFinal)},
540       {"string-array", std::mem_fn(&ResourceParser::ParseStringArray)},
541       {"style", std::bind(&ResourceParser::ParseStyle, std::placeholders::_1, ResourceType::kStyle,
542                           std::placeholders::_2, std::placeholders::_3)},
543       {"symbol", std::mem_fn(&ResourceParser::ParseSymbol)},
544   });
545 
546   std::string resource_type = parser->element_name();
547 
548   // The value format accepted for this resource.
549   uint32_t resource_format = 0u;
550 
551   bool can_be_item = true;
552   bool can_be_bag = true;
553   if (resource_type == "item") {
554     can_be_bag = false;
555 
556     // The default format for <item> is any. If a format attribute is present, that one will
557     // override the default.
558     resource_format = android::ResTable_map::TYPE_ANY;
559 
560     // Items have their type encoded in the type attribute.
561     if (std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
562       resource_type = std::string(maybe_type.value());
563     } else {
564       diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
565                    << "<item> must have a 'type' attribute");
566       return false;
567     }
568 
569     if (std::optional<StringPiece> maybe_format = xml::FindNonEmptyAttribute(parser, "format")) {
570       // An explicit format for this resource was specified. The resource will
571       // retain its type in its name, but the accepted value for this type is
572       // overridden.
573       resource_format = ParseFormatTypeNoEnumsOrFlags(maybe_format.value());
574       if (!resource_format) {
575         diag_->Error(android::DiagMessage(out_resource->source)
576                      << "'" << maybe_format.value() << "' is an invalid format");
577         return false;
578       }
579     }
580   } else if (resource_type == "bag") {
581     can_be_item = false;
582 
583     // Bags have their type encoded in the type attribute.
584     if (std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
585       resource_type = std::string(maybe_type.value());
586     } else {
587       diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
588                    << "<bag> must have a 'type' attribute");
589       return false;
590     }
591   }
592 
593   // Get the name of the resource. This will be checked later, because not all
594   // XML elements require a name.
595   std::optional<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
596 
597   if (resource_type == "id") {
598     if (!maybe_name) {
599       diag_->Error(android::DiagMessage(out_resource->source)
600                    << "<" << parser->element_name() << "> missing 'name' attribute");
601       return false;
602     }
603 
604     out_resource->name.type =
605         ResourceNamedTypeWithDefaultName(ResourceType::kId).ToResourceNamedType();
606     out_resource->name.entry = std::string(maybe_name.value());
607 
608     // Ids either represent a unique resource id or reference another resource id
609     auto item = ParseItem(parser, out_resource, resource_format);
610     if (!item) {
611       return false;
612     }
613 
614     String* empty = ValueCast<String>(out_resource->value.get());
615     if (empty && *empty->value == "") {
616       // If no inner element exists, represent a unique identifier
617       out_resource->value = util::make_unique<Id>();
618     } else {
619       Reference* ref = ValueCast<Reference>(out_resource->value.get());
620       if (ref && !ref->name && !ref->id) {
621         // A null reference also means there is no inner element when ids are in the form:
622         //    <id name="name"/>
623         out_resource->value = util::make_unique<Id>();
624       } else if (!ref || ref->name.value().type.type != ResourceType::kId) {
625         // If an inner element exists, the inner element must be a reference to another resource id
626         diag_->Error(android::DiagMessage(out_resource->source)
627                      << "<" << parser->element_name()
628                      << "> inner element must either be a resource reference or empty");
629         return false;
630       }
631     }
632 
633     return true;
634   } else if (resource_type == "macro") {
635     if (!maybe_name) {
636       diag_->Error(android::DiagMessage(out_resource->source)
637                    << "<" << parser->element_name() << "> missing 'name' attribute");
638       return false;
639     }
640 
641     out_resource->name.type =
642         ResourceNamedTypeWithDefaultName(ResourceType::kMacro).ToResourceNamedType();
643     out_resource->name.entry = std::string(maybe_name.value());
644     return ParseMacro(parser, out_resource);
645   }
646 
647   if (can_be_item) {
648     const auto item_iter = elToItemMap.find(resource_type);
649     if (item_iter != elToItemMap.end()) {
650       // This is an item, record its type and format and start parsing.
651 
652       if (!maybe_name) {
653         diag_->Error(android::DiagMessage(out_resource->source)
654                      << "<" << parser->element_name() << "> missing 'name' attribute");
655         return false;
656       }
657 
658       out_resource->name.type =
659           ResourceNamedTypeWithDefaultName(item_iter->second.type).ToResourceNamedType();
660       out_resource->name.entry = std::string(maybe_name.value());
661 
662       // Only use the implied format of the type when there is no explicit format.
663       if (resource_format == 0u) {
664         resource_format = item_iter->second.format;
665       }
666 
667       if (!ParseItem(parser, out_resource, resource_format)) {
668         return false;
669       }
670       return true;
671     }
672   }
673 
674   // This might be a bag or something.
675   if (can_be_bag) {
676     const auto bag_iter = elToBagMap.find(resource_type);
677     if (bag_iter != elToBagMap.end()) {
678       // Ensure we have a name (unless this is a <public-group> or <overlayable>).
679       if (resource_type != kPublicGroupTag && resource_type != kStagingPublicGroupTag &&
680           resource_type != kStagingPublicGroupFinalTag && resource_type != "overlayable") {
681         if (!maybe_name) {
682           diag_->Error(android::DiagMessage(out_resource->source)
683                        << "<" << parser->element_name() << "> missing 'name' attribute");
684           return false;
685         }
686 
687         out_resource->name.entry = std::string(maybe_name.value());
688       }
689 
690       // Call the associated parse method. The type will be filled in by the
691       // parse func.
692       if (!bag_iter->second(this, parser, out_resource)) {
693         return false;
694       }
695       return true;
696     }
697   }
698 
699   if (can_be_item) {
700     // Try parsing the elementName (or type) as a resource. These shall only be
701     // resources like 'layout' or 'xml' and they can only be references.
702     std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(resource_type);
703     if (parsed_type) {
704       if (!maybe_name) {
705         diag_->Error(android::DiagMessage(out_resource->source)
706                      << "<" << parser->element_name() << "> missing 'name' attribute");
707         return false;
708       }
709 
710       out_resource->name.type = parsed_type->ToResourceNamedType();
711       out_resource->name.entry = std::string(maybe_name.value());
712       out_resource->value = ParseXml(parser, android::ResTable_map::TYPE_REFERENCE, kNoRawString);
713       if (!out_resource->value) {
714         diag_->Error(android::DiagMessage(out_resource->source)
715                      << "invalid value for type '" << *parsed_type << "'. Expected a reference");
716         return false;
717       }
718       return true;
719     }
720   }
721 
722   // If the resource type was not recognized, write the error and return false.
723   diag_->Error(android::DiagMessage(out_resource->source)
724                << "unknown resource type '" << resource_type << "'");
725   return false;
726 }
727 
ParseItem(xml::XmlPullParser * parser,ParsedResource * out_resource,const uint32_t format)728 bool ResourceParser::ParseItem(xml::XmlPullParser* parser,
729                                ParsedResource* out_resource,
730                                const uint32_t format) {
731   if (format == android::ResTable_map::TYPE_STRING) {
732     return ParseString(parser, out_resource);
733   }
734 
735   out_resource->value = ParseXml(parser, format, kNoRawString);
736   if (!out_resource->value) {
737     diag_->Error(android::DiagMessage(out_resource->source)
738                  << "invalid " << out_resource->name.type);
739     return false;
740   }
741   return true;
742 }
743 
CreateFlattenSubTree(xml::XmlPullParser * parser)744 std::optional<FlattenedXmlSubTree> ResourceParser::CreateFlattenSubTree(
745     xml::XmlPullParser* parser) {
746   const size_t begin_xml_line = parser->line_number();
747 
748   std::string raw_value;
749   android::StyleString style_string;
750   std::vector<UntranslatableSection> untranslatable_sections;
751   if (!FlattenXmlSubtree(parser, &raw_value, &style_string, &untranslatable_sections)) {
752     return {};
753   }
754 
755   return FlattenedXmlSubTree{.raw_value = raw_value,
756                              .style_string = style_string,
757                              .untranslatable_sections = untranslatable_sections,
758                              .namespace_resolver = parser,
759                              .source = source_.WithLine(begin_xml_line)};
760 }
761 
762 /**
763  * Reads the entire XML subtree and attempts to parse it as some Item,
764  * with typeMask denoting which items it can be. If allowRawValue is
765  * true, a RawString is returned if the XML couldn't be parsed as
766  * an Item. If allowRawValue is false, nullptr is returned in this
767  * case.
768  */
ParseXml(xml::XmlPullParser * parser,const uint32_t type_mask,const bool allow_raw_value)769 std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser, const uint32_t type_mask,
770                                                const bool allow_raw_value) {
771   auto sub_tree = CreateFlattenSubTree(parser);
772   if (!sub_tree.has_value()) {
773     return {};
774   }
775   return ParseXml(sub_tree.value(), type_mask, allow_raw_value, *table_, config_, *diag_);
776 }
777 
ParseXml(const FlattenedXmlSubTree & xmlsub_tree,const uint32_t type_mask,const bool allow_raw_value,ResourceTable & table,const android::ConfigDescription & config,android::IDiagnostics & diag)778 std::unique_ptr<Item> ResourceParser::ParseXml(const FlattenedXmlSubTree& xmlsub_tree,
779                                                const uint32_t type_mask, const bool allow_raw_value,
780                                                ResourceTable& table,
781                                                const android::ConfigDescription& config,
782                                                android::IDiagnostics& diag) {
783   if (!xmlsub_tree.style_string.spans.empty()) {
784     // This can only be a StyledString.
785     std::unique_ptr<StyledString> styled_string =
786         util::make_unique<StyledString>(table.string_pool.MakeRef(
787             xmlsub_tree.style_string,
788             android::StringPool::Context(android::StringPool::Context::kNormalPriority, config)));
789     styled_string->untranslatable_sections = xmlsub_tree.untranslatable_sections;
790     return std::move(styled_string);
791   }
792 
793   auto on_create_reference = [&](const ResourceName& name) {
794     // name.package can be empty here, as it will assume the package name of the
795     // table.
796     auto id = util::make_unique<Id>();
797     id->SetSource(xmlsub_tree.source);
798     return table.AddResource(NewResourceBuilder(name).SetValue(std::move(id)).Build(), &diag);
799   };
800 
801   // Process the raw value.
802   std::unique_ptr<Item> processed_item = ResourceUtils::TryParseItemForAttribute(
803       &diag, xmlsub_tree.raw_value, type_mask, on_create_reference);
804   if (processed_item) {
805     // Fix up the reference.
806     if (auto ref = ValueCast<Reference>(processed_item.get())) {
807       ref->allow_raw = allow_raw_value;
808       ResolvePackage(xmlsub_tree.namespace_resolver, ref);
809     }
810     return processed_item;
811   }
812 
813   // Try making a regular string.
814   if (type_mask & android::ResTable_map::TYPE_STRING) {
815     // Use the trimmed, escaped string.
816     std::unique_ptr<String> string = util::make_unique<String>(table.string_pool.MakeRef(
817         xmlsub_tree.style_string.str, android::StringPool::Context(config)));
818     string->untranslatable_sections = xmlsub_tree.untranslatable_sections;
819     return std::move(string);
820   }
821 
822   if (allow_raw_value) {
823     // We can't parse this so return a RawString if we are allowed.
824     return util::make_unique<RawString>(table.string_pool.MakeRef(
825         util::TrimWhitespace(xmlsub_tree.raw_value), android::StringPool::Context(config)));
826   } else if (util::TrimWhitespace(xmlsub_tree.raw_value).empty()) {
827     // If the text is empty, and the value is not allowed to be a string, encode it as a @null.
828     return ResourceUtils::MakeNull();
829   }
830   return {};
831 }
832 
ParseString(xml::XmlPullParser * parser,ParsedResource * out_resource)833 bool ResourceParser::ParseString(xml::XmlPullParser* parser,
834                                  ParsedResource* out_resource) {
835   bool formatted = true;
836   if (std::optional<StringPiece> formatted_attr = xml::FindAttribute(parser, "formatted")) {
837     std::optional<bool> maybe_formatted = ResourceUtils::ParseBool(formatted_attr.value());
838     if (!maybe_formatted) {
839       diag_->Error(android::DiagMessage(out_resource->source)
840                    << "invalid value for 'formatted'. Must be a boolean");
841       return false;
842     }
843     formatted = maybe_formatted.value();
844   }
845 
846   bool translatable = options_.translatable;
847   if (std::optional<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
848     std::optional<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
849     if (!maybe_translatable) {
850       diag_->Error(android::DiagMessage(out_resource->source)
851                    << "invalid value for 'translatable'. Must be a boolean");
852       return false;
853     }
854     translatable = maybe_translatable.value();
855   }
856 
857   out_resource->value =
858       ParseXml(parser, android::ResTable_map::TYPE_STRING, kNoRawString);
859   if (!out_resource->value) {
860     diag_->Error(android::DiagMessage(out_resource->source) << "not a valid string");
861     return false;
862   }
863 
864   if (String* string_value = ValueCast<String>(out_resource->value.get())) {
865     string_value->SetTranslatable(translatable);
866 
867     if (formatted && translatable) {
868       if (!util::VerifyJavaStringFormat(*string_value->value)) {
869         android::DiagMessage msg(out_resource->source);
870         msg << "multiple substitutions specified in non-positional format; "
871                "did you mean to add the formatted=\"false\" attribute?";
872         if (options_.error_on_positional_arguments) {
873           diag_->Error(msg);
874           return false;
875         }
876 
877         diag_->Warn(msg);
878       }
879     }
880 
881   } else if (StyledString* string_value = ValueCast<StyledString>(out_resource->value.get())) {
882     string_value->SetTranslatable(translatable);
883   }
884   return true;
885 }
886 
ParseMacro(xml::XmlPullParser * parser,ParsedResource * out_resource)887 bool ResourceParser::ParseMacro(xml::XmlPullParser* parser, ParsedResource* out_resource) {
888   auto sub_tree = CreateFlattenSubTree(parser);
889   if (!sub_tree) {
890     return false;
891   }
892 
893   if (out_resource->config != ConfigDescription::DefaultConfig()) {
894     diag_->Error(android::DiagMessage(out_resource->source)
895                  << "<macro> tags cannot be declared in configurations other than the default "
896                     "configuration'");
897     return false;
898   }
899 
900   auto macro = std::make_unique<Macro>();
901   macro->raw_value = std::move(sub_tree->raw_value);
902   macro->style_string = std::move(sub_tree->style_string);
903   macro->untranslatable_sections = std::move(sub_tree->untranslatable_sections);
904 
905   for (const auto& decl : parser->package_decls()) {
906     macro->alias_namespaces.emplace_back(
907         Macro::Namespace{.alias = decl.prefix,
908                          .package_name = decl.package.package,
909                          .is_private = decl.package.private_namespace});
910   }
911 
912   out_resource->value = std::move(macro);
913   return true;
914 }
915 
ParsePublic(xml::XmlPullParser * parser,ParsedResource * out_resource)916 bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource) {
917   if (options_.visibility) {
918     diag_->Error(android::DiagMessage(out_resource->source)
919                  << "<public> tag not allowed with --visibility flag");
920     return false;
921   }
922 
923   if (out_resource->config != ConfigDescription::DefaultConfig()) {
924     diag_->Warn(android::DiagMessage(out_resource->source)
925                 << "ignoring configuration '" << out_resource->config << "' for <public> tag");
926   }
927 
928   std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
929   if (!maybe_type) {
930     diag_->Error(android::DiagMessage(out_resource->source)
931                  << "<public> must have a 'type' attribute");
932     return false;
933   }
934 
935   std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(maybe_type.value());
936   if (!parsed_type) {
937     diag_->Error(android::DiagMessage(out_resource->source)
938                  << "invalid resource type '" << maybe_type.value() << "' in <public>");
939     return false;
940   }
941 
942   out_resource->name.type = parsed_type->ToResourceNamedType();
943 
944   if (std::optional<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "id")) {
945     std::optional<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
946     if (!maybe_id) {
947       diag_->Error(android::DiagMessage(out_resource->source)
948                    << "invalid resource ID '" << maybe_id_str.value() << "' in <public>");
949       return false;
950     }
951     out_resource->id = maybe_id.value();
952   }
953 
954   if (parsed_type->type == ResourceType::kId) {
955     // An ID marked as public is also the definition of an ID.
956     out_resource->value = util::make_unique<Id>();
957   }
958 
959   out_resource->visibility_level = Visibility::Level::kPublic;
960   return true;
961 }
962 
963 template <typename Func>
ParseGroupImpl(xml::XmlPullParser * parser,ParsedResource * out_resource,const char * tag_name,android::IDiagnostics * diag,Func && func)964 bool static ParseGroupImpl(xml::XmlPullParser* parser, ParsedResource* out_resource,
965                            const char* tag_name, android::IDiagnostics* diag, Func&& func) {
966   if (out_resource->config != ConfigDescription::DefaultConfig()) {
967     diag->Warn(android::DiagMessage(out_resource->source)
968                << "ignoring configuration '" << out_resource->config << "' for <" << tag_name
969                << "> tag");
970   }
971 
972   std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
973   if (!maybe_type) {
974     diag->Error(android::DiagMessage(out_resource->source)
975                 << "<" << tag_name << "> must have a 'type' attribute");
976     return false;
977   }
978 
979   std::optional<ResourceNamedTypeRef> maybe_parsed_type =
980       ParseResourceNamedType(maybe_type.value());
981   if (!maybe_parsed_type) {
982     diag->Error(android::DiagMessage(out_resource->source)
983                 << "invalid resource type '" << maybe_type.value() << "' in <" << tag_name << ">");
984     return false;
985   }
986   auto parsed_type = maybe_parsed_type->ToResourceNamedType();
987 
988   std::optional<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "first-id");
989   if (!maybe_id_str) {
990     diag->Error(android::DiagMessage(out_resource->source)
991                 << "<" << tag_name << "> must have a 'first-id' attribute");
992     return false;
993   }
994 
995   std::optional<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
996   if (!maybe_id) {
997     diag->Error(android::DiagMessage(out_resource->source)
998                 << "invalid resource ID '" << maybe_id_str.value() << "' in <" << tag_name << ">");
999     return false;
1000   }
1001 
1002   std::string comment;
1003   ResourceId next_id = maybe_id.value();
1004   bool error = false;
1005   const size_t depth = parser->depth();
1006   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
1007     if (parser->event() == xml::XmlPullParser::Event::kComment) {
1008       comment = std::string(util::TrimWhitespace(parser->comment()));
1009       continue;
1010     } else if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
1011       // Skip text.
1012       continue;
1013     }
1014 
1015     const android::Source item_source = out_resource->source.WithLine(parser->line_number());
1016     const std::string& element_namespace = parser->element_namespace();
1017     const std::string& element_name = parser->element_name();
1018     if (element_namespace.empty() && element_name == "public") {
1019       auto maybe_name = xml::FindNonEmptyAttribute(parser, "name");
1020       if (!maybe_name) {
1021         diag->Error(android::DiagMessage(item_source) << "<public> must have a 'name' attribute");
1022         error = true;
1023         continue;
1024       }
1025 
1026       if (xml::FindNonEmptyAttribute(parser, "id")) {
1027         diag->Error(android::DiagMessage(item_source)
1028                     << "'id' is ignored within <" << tag_name << ">");
1029         error = true;
1030         continue;
1031       }
1032 
1033       if (xml::FindNonEmptyAttribute(parser, "type")) {
1034         diag->Error(android::DiagMessage(item_source)
1035                     << "'type' is ignored within <" << tag_name << ">");
1036         error = true;
1037         continue;
1038       }
1039 
1040       if (maybe_name.value().substr(0, std::strlen("removed_")) == "removed_") {
1041         // Skip resources that have been removed from the framework, but leave a hole so that
1042         // other staged resources don't shift and break apps previously compiled against them
1043         next_id.id++;
1044         continue;
1045       }
1046 
1047       ParsedResource& entry_res = out_resource->child_resources.emplace_back(ParsedResource{
1048           .name = ResourceName{{}, parsed_type, std::string(maybe_name.value())},
1049           .source = item_source,
1050           .comment = std::move(comment),
1051       });
1052       comment.clear();
1053 
1054       // Execute group specific code.
1055       func(entry_res, next_id);
1056 
1057       next_id.id++;
1058     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
1059       diag->Error(android::DiagMessage(item_source) << ":" << element_name << ">");
1060       error = true;
1061     }
1062   }
1063   return !error;
1064 }
1065 
ParseStagingPublicGroup(xml::XmlPullParser * parser,ParsedResource * out_resource)1066 bool ResourceParser::ParseStagingPublicGroup(xml::XmlPullParser* parser,
1067                                              ParsedResource* out_resource) {
1068   return ParseGroupImpl(parser, out_resource, kStagingPublicGroupTag, diag_,
1069                         [](ParsedResource& parsed_entry, ResourceId id) {
1070                           parsed_entry.id = id;
1071                           parsed_entry.staged_api = true;
1072                           parsed_entry.visibility_level = Visibility::Level::kPublic;
1073                         });
1074 }
1075 
ParseStagingPublicGroupFinal(xml::XmlPullParser * parser,ParsedResource * out_resource)1076 bool ResourceParser::ParseStagingPublicGroupFinal(xml::XmlPullParser* parser,
1077                                                   ParsedResource* out_resource) {
1078   return ParseGroupImpl(parser, out_resource, kStagingPublicGroupFinalTag, diag_,
1079                         [](ParsedResource& parsed_entry, ResourceId id) {
1080                           parsed_entry.staged_alias = StagedId{id, parsed_entry.source};
1081                         });
1082 }
1083 
ParsePublicGroup(xml::XmlPullParser * parser,ParsedResource * out_resource)1084 bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource) {
1085   if (options_.visibility) {
1086     diag_->Error(android::DiagMessage(out_resource->source)
1087                  << "<" << kPublicGroupTag << "> tag not allowed with --visibility flag");
1088     return false;
1089   }
1090 
1091   return ParseGroupImpl(parser, out_resource, kPublicGroupTag, diag_,
1092                         [](ParsedResource& parsed_entry, ResourceId id) {
1093                           parsed_entry.id = id;
1094                           parsed_entry.visibility_level = Visibility::Level::kPublic;
1095                         });
1096 }
1097 
ParseSymbolImpl(xml::XmlPullParser * parser,ParsedResource * out_resource)1098 bool ResourceParser::ParseSymbolImpl(xml::XmlPullParser* parser,
1099                                      ParsedResource* out_resource) {
1100   std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
1101   if (!maybe_type) {
1102     diag_->Error(android::DiagMessage(out_resource->source)
1103                  << "<" << parser->element_name() << "> must have a 'type' attribute");
1104     return false;
1105   }
1106 
1107   std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(maybe_type.value());
1108   if (!parsed_type) {
1109     diag_->Error(android::DiagMessage(out_resource->source)
1110                  << "invalid resource type '" << maybe_type.value() << "' in <"
1111                  << parser->element_name() << ">");
1112     return false;
1113   }
1114 
1115   out_resource->name.type = parsed_type->ToResourceNamedType();
1116   return true;
1117 }
1118 
ParseSymbol(xml::XmlPullParser * parser,ParsedResource * out_resource)1119 bool ResourceParser::ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource) {
1120   if (options_.visibility) {
1121     diag_->Error(android::DiagMessage(out_resource->source)
1122                  << "<java-symbol> and <symbol> tags not allowed with --visibility flag");
1123     return false;
1124   }
1125   if (out_resource->config != ConfigDescription::DefaultConfig()) {
1126     diag_->Warn(android::DiagMessage(out_resource->source)
1127                 << "ignoring configuration '" << out_resource->config << "' for <"
1128                 << parser->element_name() << "> tag");
1129   }
1130 
1131   if (!ParseSymbolImpl(parser, out_resource)) {
1132     return false;
1133   }
1134 
1135   out_resource->visibility_level = Visibility::Level::kPrivate;
1136   return true;
1137 }
1138 
ParseOverlayable(xml::XmlPullParser * parser,ParsedResource * out_resource)1139 bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource) {
1140   if (out_resource->config != ConfigDescription::DefaultConfig()) {
1141     diag_->Warn(android::DiagMessage(out_resource->source)
1142                 << "ignoring configuration '" << out_resource->config << "' for <overlayable> tag");
1143   }
1144 
1145   std::optional<StringPiece> overlayable_name = xml::FindNonEmptyAttribute(parser, "name");
1146   if (!overlayable_name) {
1147     diag_->Error(android::DiagMessage(out_resource->source)
1148                  << "<overlayable> tag must have a 'name' attribute");
1149     return false;
1150   }
1151 
1152   const std::string kActorUriScheme =
1153       android::base::StringPrintf("%s://", Overlayable::kActorScheme);
1154   std::optional<StringPiece> overlayable_actor = xml::FindNonEmptyAttribute(parser, "actor");
1155   if (overlayable_actor && !util::StartsWith(overlayable_actor.value(), kActorUriScheme)) {
1156     diag_->Error(android::DiagMessage(out_resource->source)
1157                  << "specified <overlayable> tag 'actor' attribute must use the scheme '"
1158                  << Overlayable::kActorScheme << "'");
1159     return false;
1160   }
1161 
1162   // Create a overlayable entry grouping that represents this <overlayable>
1163   auto overlayable = std::make_shared<Overlayable>(
1164       overlayable_name.value(), (overlayable_actor) ? overlayable_actor.value() : "",
1165       source_);
1166 
1167   bool error = false;
1168   std::string comment;
1169   PolicyFlags current_policies = PolicyFlags::NONE;
1170   const size_t start_depth = parser->depth();
1171   while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
1172     xml::XmlPullParser::Event event = parser->event();
1173     if (event == xml::XmlPullParser::Event::kEndElement && parser->depth() == start_depth) {
1174       // Break the loop when exiting the <overlayable>
1175       break;
1176     } else if (event == xml::XmlPullParser::Event::kEndElement
1177                && parser->depth() == start_depth + 1) {
1178       // Clear the current policies when exiting the <policy> tags
1179       current_policies = PolicyFlags::NONE;
1180       continue;
1181     } else if (event == xml::XmlPullParser::Event::kComment) {
1182       // Retrieve the comment of individual <item> tags
1183       comment = parser->comment();
1184       continue;
1185     } else if (event != xml::XmlPullParser::Event::kStartElement) {
1186       // Skip to the start of the next element
1187       continue;
1188     }
1189 
1190     const android::Source element_source = source_.WithLine(parser->line_number());
1191     const std::string& element_name = parser->element_name();
1192     const std::string& element_namespace = parser->element_namespace();
1193     if (element_namespace.empty() && element_name == "item") {
1194       if (current_policies == PolicyFlags::NONE) {
1195         diag_->Error(android::DiagMessage(element_source)
1196                      << "<item> within an <overlayable> must be inside a <policy> block");
1197         error = true;
1198         continue;
1199       }
1200 
1201       // Items specify the name and type of resource that should be overlayable
1202       std::optional<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name");
1203       if (!item_name) {
1204         diag_->Error(android::DiagMessage(element_source)
1205                      << "<item> within an <overlayable> must have a 'name' attribute");
1206         error = true;
1207         continue;
1208       }
1209 
1210       std::optional<StringPiece> item_type = xml::FindNonEmptyAttribute(parser, "type");
1211       if (!item_type) {
1212         diag_->Error(android::DiagMessage(element_source)
1213                      << "<item> within an <overlayable> must have a 'type' attribute");
1214         error = true;
1215         continue;
1216       }
1217 
1218       std::optional<ResourceNamedTypeRef> type = ParseResourceNamedType(item_type.value());
1219       if (!type) {
1220         diag_->Error(android::DiagMessage(element_source)
1221                      << "invalid resource type '" << item_type.value()
1222                      << "' in <item> within an <overlayable>");
1223         error = true;
1224         continue;
1225       }
1226 
1227       OverlayableItem overlayable_item(overlayable);
1228       overlayable_item.policies = current_policies;
1229       overlayable_item.comment = comment;
1230       overlayable_item.source = element_source;
1231 
1232       ParsedResource child_resource{};
1233       child_resource.name.type = type->ToResourceNamedType();
1234       child_resource.name.entry = std::string(item_name.value());
1235       child_resource.overlayable_item = overlayable_item;
1236       out_resource->child_resources.push_back(std::move(child_resource));
1237 
1238     } else if (element_namespace.empty() && element_name == "policy") {
1239       if (current_policies != PolicyFlags::NONE) {
1240         // If the policy list is not empty, then we are currently inside a policy element
1241         diag_->Error(android::DiagMessage(element_source)
1242                      << "<policy> blocks cannot be recursively nested");
1243         error = true;
1244         break;
1245       } else if (std::optional<StringPiece> maybe_type =
1246                      xml::FindNonEmptyAttribute(parser, "type")) {
1247         // Parse the polices separated by vertical bar characters to allow for specifying multiple
1248         // policies. Items within the policy tag will have the specified policy.
1249         for (StringPiece part : util::Tokenize(maybe_type.value(), '|')) {
1250           StringPiece trimmed_part = util::TrimWhitespace(part);
1251           const auto policy = std::find_if(kPolicyStringToFlag.begin(),
1252                                            kPolicyStringToFlag.end(),
1253                                            [trimmed_part](const auto& it) {
1254                                              return trimmed_part == it.first;
1255                                            });
1256           if (policy == kPolicyStringToFlag.end()) {
1257             diag_->Error(android::DiagMessage(element_source)
1258                          << "<policy> has unsupported type '" << trimmed_part << "'");
1259             error = true;
1260             continue;
1261           }
1262 
1263           current_policies |= policy->second;
1264         }
1265       } else {
1266         diag_->Error(android::DiagMessage(element_source)
1267                      << "<policy> must have a 'type' attribute");
1268         error = true;
1269         continue;
1270       }
1271     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
1272       diag_->Error(android::DiagMessage(element_source)
1273                    << "invalid element <" << element_name << "> "
1274                    << " in <overlayable>");
1275       error = true;
1276       break;
1277     }
1278 
1279     comment.clear();
1280   }
1281 
1282   return !error;
1283 }
1284 
ParseAddResource(xml::XmlPullParser * parser,ParsedResource * out_resource)1285 bool ResourceParser::ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource) {
1286   if (ParseSymbolImpl(parser, out_resource)) {
1287     out_resource->visibility_level = Visibility::Level::kUndefined;
1288     out_resource->allow_new = true;
1289     return true;
1290   }
1291   return false;
1292 }
1293 
ParseAttr(xml::XmlPullParser * parser,ParsedResource * out_resource)1294 bool ResourceParser::ParseAttr(xml::XmlPullParser* parser,
1295                                ParsedResource* out_resource) {
1296   return ParseAttrImpl(parser, out_resource, false);
1297 }
1298 
ParseAttrImpl(xml::XmlPullParser * parser,ParsedResource * out_resource,bool weak)1299 bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,
1300                                    ParsedResource* out_resource, bool weak) {
1301   out_resource->name.type =
1302       ResourceNamedTypeWithDefaultName(ResourceType::kAttr).ToResourceNamedType();
1303 
1304   // Attributes only end up in default configuration.
1305   if (out_resource->config != ConfigDescription::DefaultConfig()) {
1306     diag_->Warn(android::DiagMessage(out_resource->source)
1307                 << "ignoring configuration '" << out_resource->config << "' for attribute "
1308                 << out_resource->name);
1309     out_resource->config = ConfigDescription::DefaultConfig();
1310   }
1311 
1312   uint32_t type_mask = 0;
1313 
1314   std::optional<StringPiece> maybe_format = xml::FindAttribute(parser, "format");
1315   if (maybe_format) {
1316     type_mask = ParseFormatAttribute(maybe_format.value());
1317     if (type_mask == 0) {
1318       diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
1319                    << "invalid attribute format '" << maybe_format.value() << "'");
1320       return false;
1321     }
1322   }
1323 
1324   std::optional<int32_t> maybe_min, maybe_max;
1325 
1326   if (std::optional<StringPiece> maybe_min_str = xml::FindAttribute(parser, "min")) {
1327     StringPiece min_str = util::TrimWhitespace(maybe_min_str.value());
1328     if (!min_str.empty()) {
1329       std::u16string min_str16 = android::util::Utf8ToUtf16(min_str);
1330       android::Res_value value;
1331       if (android::ResTable::stringToInt(min_str16.data(), min_str16.size(), &value)) {
1332         maybe_min = static_cast<int32_t>(value.data);
1333       }
1334     }
1335 
1336     if (!maybe_min) {
1337       diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
1338                    << "invalid 'min' value '" << min_str << "'");
1339       return false;
1340     }
1341   }
1342 
1343   if (std::optional<StringPiece> maybe_max_str = xml::FindAttribute(parser, "max")) {
1344     StringPiece max_str = util::TrimWhitespace(maybe_max_str.value());
1345     if (!max_str.empty()) {
1346       std::u16string max_str16 = android::util::Utf8ToUtf16(max_str);
1347       android::Res_value value;
1348       if (android::ResTable::stringToInt(max_str16.data(), max_str16.size(), &value)) {
1349         maybe_max = static_cast<int32_t>(value.data);
1350       }
1351     }
1352 
1353     if (!maybe_max) {
1354       diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
1355                    << "invalid 'max' value '" << max_str << "'");
1356       return false;
1357     }
1358   }
1359 
1360   if ((maybe_min || maybe_max) &&
1361       (type_mask & android::ResTable_map::TYPE_INTEGER) == 0) {
1362     diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
1363                  << "'min' and 'max' can only be used when format='integer'");
1364     return false;
1365   }
1366 
1367   struct SymbolComparator {
1368     bool operator()(const Attribute::Symbol& a, const Attribute::Symbol& b) const {
1369       return a.symbol.name.value() < b.symbol.name.value();
1370     }
1371   };
1372 
1373   std::set<Attribute::Symbol, SymbolComparator> items;
1374 
1375   std::string comment;
1376   bool error = false;
1377   const size_t depth = parser->depth();
1378   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
1379     if (parser->event() == xml::XmlPullParser::Event::kComment) {
1380       comment = std::string(util::TrimWhitespace(parser->comment()));
1381       continue;
1382     } else if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
1383       // Skip text.
1384       continue;
1385     }
1386 
1387     const android::Source item_source = source_.WithLine(parser->line_number());
1388     const std::string& element_namespace = parser->element_namespace();
1389     const std::string& element_name = parser->element_name();
1390     if (element_namespace.empty() && (element_name == "flag" || element_name == "enum")) {
1391       if (element_name == "enum") {
1392         if (type_mask & android::ResTable_map::TYPE_FLAGS) {
1393           diag_->Error(android::DiagMessage(item_source)
1394                        << "can not define an <enum>; already defined a <flag>");
1395           error = true;
1396           continue;
1397         }
1398         type_mask |= android::ResTable_map::TYPE_ENUM;
1399 
1400       } else if (element_name == "flag") {
1401         if (type_mask & android::ResTable_map::TYPE_ENUM) {
1402           diag_->Error(android::DiagMessage(item_source)
1403                        << "can not define a <flag>; already defined an <enum>");
1404           error = true;
1405           continue;
1406         }
1407         type_mask |= android::ResTable_map::TYPE_FLAGS;
1408       }
1409 
1410       if (std::optional<Attribute::Symbol> s = ParseEnumOrFlagItem(parser, element_name)) {
1411         Attribute::Symbol& symbol = s.value();
1412         ParsedResource child_resource;
1413         child_resource.name = symbol.symbol.name.value();
1414         child_resource.source = item_source;
1415         child_resource.value = util::make_unique<Id>();
1416         if (options_.visibility) {
1417           child_resource.visibility_level = options_.visibility.value();
1418         }
1419         out_resource->child_resources.push_back(std::move(child_resource));
1420 
1421         symbol.symbol.SetComment(std::move(comment));
1422         symbol.symbol.SetSource(item_source);
1423 
1424         auto insert_result = items.insert(std::move(symbol));
1425         if (!insert_result.second) {
1426           const Attribute::Symbol& existing_symbol = *insert_result.first;
1427           diag_->Error(android::DiagMessage(item_source)
1428                        << "duplicate symbol '" << existing_symbol.symbol.name.value().entry << "'");
1429 
1430           diag_->Note(android::DiagMessage(existing_symbol.symbol.GetSource())
1431                       << "first defined here");
1432           error = true;
1433         }
1434       } else {
1435         error = true;
1436       }
1437     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
1438       diag_->Error(android::DiagMessage(item_source) << ":" << element_name << ">");
1439       error = true;
1440     }
1441 
1442     comment = {};
1443   }
1444 
1445   if (error) {
1446     return false;
1447   }
1448 
1449   std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(
1450       type_mask ? type_mask : uint32_t{android::ResTable_map::TYPE_ANY});
1451   attr->SetWeak(weak);
1452   attr->symbols = std::vector<Attribute::Symbol>(items.begin(), items.end());
1453   attr->min_int = maybe_min.value_or(std::numeric_limits<int32_t>::min());
1454   attr->max_int = maybe_max.value_or(std::numeric_limits<int32_t>::max());
1455   out_resource->value = std::move(attr);
1456   return true;
1457 }
1458 
ParseEnumOrFlagItem(xml::XmlPullParser * parser,StringPiece tag)1459 std::optional<Attribute::Symbol> ResourceParser::ParseEnumOrFlagItem(xml::XmlPullParser* parser,
1460                                                                      StringPiece tag) {
1461   const android::Source source = source_.WithLine(parser->line_number());
1462 
1463   std::optional<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
1464   if (!maybe_name) {
1465     diag_->Error(android::DiagMessage(source)
1466                  << "no attribute 'name' found for tag <" << tag << ">");
1467     return {};
1468   }
1469 
1470   std::optional<StringPiece> maybe_value = xml::FindNonEmptyAttribute(parser, "value");
1471   if (!maybe_value) {
1472     diag_->Error(android::DiagMessage(source)
1473                  << "no attribute 'value' found for tag <" << tag << ">");
1474     return {};
1475   }
1476 
1477   std::u16string value16 = android::util::Utf8ToUtf16(maybe_value.value());
1478   android::Res_value val;
1479   if (!android::ResTable::stringToInt(value16.data(), value16.size(), &val)) {
1480     diag_->Error(android::DiagMessage(source) << "invalid value '" << maybe_value.value()
1481                                               << "' for <" << tag << ">; must be an integer");
1482     return {};
1483   }
1484 
1485   return Attribute::Symbol{
1486       Reference(ResourceNameRef({}, ResourceNamedTypeWithDefaultName(ResourceType::kId),
1487                                 maybe_name.value())),
1488       val.data, val.dataType};
1489 }
1490 
ParseStyleItem(xml::XmlPullParser * parser,Style * style)1491 bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) {
1492   const android::Source source = source_.WithLine(parser->line_number());
1493 
1494   std::optional<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
1495   if (!maybe_name) {
1496     diag_->Error(android::DiagMessage(source) << "<item> must have a 'name' attribute");
1497     return false;
1498   }
1499 
1500   std::optional<Reference> maybe_key = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
1501   if (!maybe_key) {
1502     diag_->Error(android::DiagMessage(source)
1503                  << "invalid attribute name '" << maybe_name.value() << "'");
1504     return false;
1505   }
1506 
1507   ResolvePackage(parser, &maybe_key.value());
1508   maybe_key.value().SetSource(source);
1509 
1510   std::unique_ptr<Item> value = ParseXml(parser, 0, kAllowRawString);
1511   if (!value) {
1512     diag_->Error(android::DiagMessage(source) << "could not parse style item");
1513     return false;
1514   }
1515 
1516   style->entries.push_back(Style::Entry{std::move(maybe_key.value()), std::move(value)});
1517   return true;
1518 }
1519 
ParseStyle(const ResourceType type,xml::XmlPullParser * parser,ParsedResource * out_resource)1520 bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* parser,
1521                                 ParsedResource* out_resource) {
1522   out_resource->name.type = ResourceNamedTypeWithDefaultName(type).ToResourceNamedType();
1523 
1524   std::unique_ptr<Style> style = util::make_unique<Style>();
1525 
1526   std::optional<StringPiece> maybe_parent = xml::FindAttribute(parser, "parent");
1527   if (maybe_parent) {
1528     // If the parent is empty, we don't have a parent, but we also don't infer either.
1529     if (!maybe_parent.value().empty()) {
1530       std::string err_str;
1531       style->parent = ResourceUtils::ParseStyleParentReference(maybe_parent.value(), &err_str);
1532       if (!style->parent) {
1533         diag_->Error(android::DiagMessage(out_resource->source) << err_str);
1534         return false;
1535       }
1536 
1537       // Transform the namespace prefix to the actual package name, and mark the reference as
1538       // private if appropriate.
1539       ResolvePackage(parser, &style->parent.value());
1540     }
1541 
1542   } else {
1543     // No parent was specified, so try inferring it from the style name.
1544     std::string style_name = out_resource->name.entry;
1545     size_t pos = style_name.find_last_of(u'.');
1546     if (pos != std::string::npos) {
1547       style->parent_inferred = true;
1548       style->parent = Reference(ResourceName(
1549           {}, ResourceNamedTypeWithDefaultName(ResourceType::kStyle), style_name.substr(0, pos)));
1550     }
1551   }
1552 
1553   bool error = false;
1554   const size_t depth = parser->depth();
1555   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
1556     if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
1557       // Skip text and comments.
1558       continue;
1559     }
1560 
1561     const std::string& element_namespace = parser->element_namespace();
1562     const std::string& element_name = parser->element_name();
1563     if (element_namespace == "" && element_name == "item") {
1564       error |= !ParseStyleItem(parser, style.get());
1565 
1566     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
1567       diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
1568                    << ":" << element_name << ">");
1569       error = true;
1570     }
1571   }
1572 
1573   if (error) {
1574     return false;
1575   }
1576 
1577   out_resource->value = std::move(style);
1578   return true;
1579 }
1580 
ParseArray(xml::XmlPullParser * parser,ParsedResource * out_resource)1581 bool ResourceParser::ParseArray(xml::XmlPullParser* parser, ParsedResource* out_resource) {
1582   uint32_t resource_format = android::ResTable_map::TYPE_ANY;
1583   if (std::optional<StringPiece> format_attr = xml::FindNonEmptyAttribute(parser, "format")) {
1584     resource_format = ParseFormatTypeNoEnumsOrFlags(format_attr.value());
1585     if (resource_format == 0u) {
1586       diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
1587                    << "'" << format_attr.value() << "' is an invalid format");
1588       return false;
1589     }
1590   }
1591   return ParseArrayImpl(parser, out_resource, resource_format);
1592 }
1593 
ParseIntegerArray(xml::XmlPullParser * parser,ParsedResource * out_resource)1594 bool ResourceParser::ParseIntegerArray(xml::XmlPullParser* parser, ParsedResource* out_resource) {
1595   return ParseArrayImpl(parser, out_resource, android::ResTable_map::TYPE_INTEGER);
1596 }
1597 
ParseStringArray(xml::XmlPullParser * parser,ParsedResource * out_resource)1598 bool ResourceParser::ParseStringArray(xml::XmlPullParser* parser, ParsedResource* out_resource) {
1599   return ParseArrayImpl(parser, out_resource, android::ResTable_map::TYPE_STRING);
1600 }
1601 
ParseArrayImpl(xml::XmlPullParser * parser,ParsedResource * out_resource,const uint32_t typeMask)1602 bool ResourceParser::ParseArrayImpl(xml::XmlPullParser* parser,
1603                                     ParsedResource* out_resource,
1604                                     const uint32_t typeMask) {
1605   out_resource->name.type =
1606       ResourceNamedTypeWithDefaultName(ResourceType::kArray).ToResourceNamedType();
1607 
1608   std::unique_ptr<Array> array = util::make_unique<Array>();
1609 
1610   bool translatable = options_.translatable;
1611   if (std::optional<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
1612     std::optional<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
1613     if (!maybe_translatable) {
1614       diag_->Error(android::DiagMessage(out_resource->source)
1615                    << "invalid value for 'translatable'. Must be a boolean");
1616       return false;
1617     }
1618     translatable = maybe_translatable.value();
1619   }
1620   array->SetTranslatable(translatable);
1621 
1622   bool error = false;
1623   const size_t depth = parser->depth();
1624   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
1625     if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
1626       // Skip text and comments.
1627       continue;
1628     }
1629 
1630     const android::Source item_source = source_.WithLine(parser->line_number());
1631     const std::string& element_namespace = parser->element_namespace();
1632     const std::string& element_name = parser->element_name();
1633     if (element_namespace.empty() && element_name == "item") {
1634       std::unique_ptr<Item> item = ParseXml(parser, typeMask, kNoRawString);
1635       if (!item) {
1636         diag_->Error(android::DiagMessage(item_source) << "could not parse array item");
1637         error = true;
1638         continue;
1639       }
1640       item->SetSource(item_source);
1641       array->elements.emplace_back(std::move(item));
1642 
1643     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
1644       diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
1645                    << "unknown tag <" << element_namespace << ":" << element_name << ">");
1646       error = true;
1647     }
1648   }
1649 
1650   if (error) {
1651     return false;
1652   }
1653 
1654   out_resource->value = std::move(array);
1655   return true;
1656 }
1657 
ParsePlural(xml::XmlPullParser * parser,ParsedResource * out_resource)1658 bool ResourceParser::ParsePlural(xml::XmlPullParser* parser,
1659                                  ParsedResource* out_resource) {
1660   out_resource->name.type =
1661       ResourceNamedTypeWithDefaultName(ResourceType::kPlurals).ToResourceNamedType();
1662 
1663   std::unique_ptr<Plural> plural = util::make_unique<Plural>();
1664 
1665   bool error = false;
1666   const size_t depth = parser->depth();
1667   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
1668     if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
1669       // Skip text and comments.
1670       continue;
1671     }
1672 
1673     const android::Source item_source = source_.WithLine(parser->line_number());
1674     const std::string& element_namespace = parser->element_namespace();
1675     const std::string& element_name = parser->element_name();
1676     if (element_namespace.empty() && element_name == "item") {
1677       std::optional<StringPiece> maybe_quantity = xml::FindNonEmptyAttribute(parser, "quantity");
1678       if (!maybe_quantity) {
1679         diag_->Error(android::DiagMessage(item_source) << "<item> in <plurals> requires attribute "
1680                                                        << "'quantity'");
1681         error = true;
1682         continue;
1683       }
1684 
1685       StringPiece trimmed_quantity =
1686           util::TrimWhitespace(maybe_quantity.value());
1687       size_t index = 0;
1688       if (trimmed_quantity == "zero") {
1689         index = Plural::Zero;
1690       } else if (trimmed_quantity == "one") {
1691         index = Plural::One;
1692       } else if (trimmed_quantity == "two") {
1693         index = Plural::Two;
1694       } else if (trimmed_quantity == "few") {
1695         index = Plural::Few;
1696       } else if (trimmed_quantity == "many") {
1697         index = Plural::Many;
1698       } else if (trimmed_quantity == "other") {
1699         index = Plural::Other;
1700       } else {
1701         diag_->Error(android::DiagMessage(item_source)
1702                      << "<item> in <plural> has invalid value '" << trimmed_quantity
1703                      << "' for attribute 'quantity'");
1704         error = true;
1705         continue;
1706       }
1707 
1708       if (plural->values[index]) {
1709         diag_->Error(android::DiagMessage(item_source)
1710                      << "duplicate quantity '" << trimmed_quantity << "'");
1711         error = true;
1712         continue;
1713       }
1714 
1715       if (!(plural->values[index] = ParseXml(
1716                 parser, android::ResTable_map::TYPE_STRING, kNoRawString))) {
1717         error = true;
1718         continue;
1719       }
1720 
1721       plural->values[index]->SetSource(item_source);
1722 
1723     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
1724       diag_->Error(android::DiagMessage(item_source)
1725                    << "unknown tag <" << element_namespace << ":" << element_name << ">");
1726       error = true;
1727     }
1728   }
1729 
1730   if (error) {
1731     return false;
1732   }
1733 
1734   out_resource->value = std::move(plural);
1735   return true;
1736 }
1737 
ParseDeclareStyleable(xml::XmlPullParser * parser,ParsedResource * out_resource)1738 bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser,
1739                                            ParsedResource* out_resource) {
1740   out_resource->name.type =
1741       ResourceNamedTypeWithDefaultName(ResourceType::kStyleable).ToResourceNamedType();
1742 
1743   if (!options_.preserve_visibility_of_styleables) {
1744     // This was added in change Idd21b5de4d20be06c6f8c8eb5a22ccd68afc4927 to mimic aapt1, but no one
1745     // knows exactly what for.
1746     //
1747     // FWIW, styleables only appear in generated R classes.  For custom views these should always be
1748     // package-private (to be used only by the view class); themes are a different story.
1749     out_resource->visibility_level = Visibility::Level::kPublic;
1750   }
1751 
1752   // Declare-styleable only ends up in default config;
1753   if (out_resource->config != ConfigDescription::DefaultConfig()) {
1754     diag_->Warn(android::DiagMessage(out_resource->source)
1755                 << "ignoring configuration '" << out_resource->config << "' for styleable "
1756                 << out_resource->name.entry);
1757     out_resource->config = ConfigDescription::DefaultConfig();
1758   }
1759 
1760   std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
1761 
1762   std::string comment;
1763   bool error = false;
1764   const size_t depth = parser->depth();
1765   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
1766     if (parser->event() == xml::XmlPullParser::Event::kComment) {
1767       comment = std::string(util::TrimWhitespace(parser->comment()));
1768       continue;
1769     } else if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
1770       // Ignore text.
1771       continue;
1772     }
1773 
1774     const android::Source item_source = source_.WithLine(parser->line_number());
1775     const std::string& element_namespace = parser->element_namespace();
1776     const std::string& element_name = parser->element_name();
1777     if (element_namespace.empty() && element_name == "attr") {
1778       std::optional<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
1779       if (!maybe_name) {
1780         diag_->Error(android::DiagMessage(item_source)
1781                      << "<attr> tag must have a 'name' attribute");
1782         error = true;
1783         continue;
1784       }
1785 
1786       // If this is a declaration, the package name may be in the name. Separate
1787       // these out.
1788       // Eg. <attr name="android:text" />
1789       std::optional<Reference> maybe_ref = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
1790       if (!maybe_ref) {
1791         diag_->Error(android::DiagMessage(item_source)
1792                      << "<attr> tag has invalid name '" << maybe_name.value() << "'");
1793         error = true;
1794         continue;
1795       }
1796 
1797       Reference& child_ref = maybe_ref.value();
1798       xml::ResolvePackage(parser, &child_ref);
1799 
1800       // Create the ParsedResource that will add the attribute to the table.
1801       ParsedResource child_resource;
1802       child_resource.name = child_ref.name.value();
1803       child_resource.source = item_source;
1804       child_resource.comment = std::move(comment);
1805       comment.clear();
1806       if (options_.visibility) {
1807         child_resource.visibility_level = options_.visibility.value();
1808       }
1809 
1810       if (!ParseAttrImpl(parser, &child_resource, true)) {
1811         error = true;
1812         continue;
1813       }
1814 
1815       // Create the reference to this attribute.
1816       child_ref.SetComment(child_resource.comment);
1817       child_ref.SetSource(item_source);
1818       styleable->entries.push_back(std::move(child_ref));
1819 
1820       // Do not add referenced attributes that do not define a format to the table.
1821       CHECK(child_resource.value != nullptr);
1822       Attribute* attr = ValueCast<Attribute>(child_resource.value.get());
1823 
1824       CHECK(attr != nullptr);
1825       if (attr->type_mask != android::ResTable_map::TYPE_ANY) {
1826         out_resource->child_resources.push_back(std::move(child_resource));
1827       }
1828 
1829     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
1830       diag_->Error(android::DiagMessage(item_source)
1831                    << "unknown tag <" << element_namespace << ":" << element_name << ">");
1832       error = true;
1833     }
1834 
1835     comment = {};
1836   }
1837 
1838   if (error) {
1839     return false;
1840   }
1841 
1842   out_resource->value = std::move(styleable);
1843   return true;
1844 }
1845 
1846 }  // namespace aapt
1847