1 /*
2  * Copyright (C) 2017 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 "configuration/ConfigurationParser.h"
18 
19 #include <algorithm>
20 #include <functional>
21 #include <map>
22 #include <memory>
23 #include <string>
24 #include <utility>
25 
26 #include "ResourceUtils.h"
27 #include "android-base/file.h"
28 #include "android-base/logging.h"
29 #include "androidfw/ConfigDescription.h"
30 #include "androidfw/IDiagnostics.h"
31 #include "configuration/ConfigurationParser.internal.h"
32 #include "io/File.h"
33 #include "io/FileSystem.h"
34 #include "io/StringStream.h"
35 #include "util/Files.h"
36 #include "util/Util.h"
37 #include "xml/XmlActionExecutor.h"
38 #include "xml/XmlDom.h"
39 #include "xml/XmlUtil.h"
40 
41 using ::android::ConfigDescription;
42 
43 namespace aapt {
44 
45 namespace {
46 
47 using ::aapt::configuration::Abi;
48 using ::aapt::configuration::AndroidManifest;
49 using ::aapt::configuration::AndroidSdk;
50 using ::aapt::configuration::ConfiguredArtifact;
51 using ::aapt::configuration::DeviceFeature;
52 using ::aapt::configuration::Entry;
53 using ::aapt::configuration::ExtractConfiguration;
54 using ::aapt::configuration::GlTexture;
55 using ::aapt::configuration::Group;
56 using ::aapt::configuration::Locale;
57 using ::aapt::configuration::OrderedEntry;
58 using ::aapt::configuration::OutputArtifact;
59 using ::aapt::configuration::PostProcessingConfiguration;
60 using ::aapt::configuration::handler::AbiGroupTagHandler;
61 using ::aapt::configuration::handler::AndroidSdkTagHandler;
62 using ::aapt::configuration::handler::ArtifactFormatTagHandler;
63 using ::aapt::configuration::handler::ArtifactTagHandler;
64 using ::aapt::configuration::handler::DeviceFeatureGroupTagHandler;
65 using ::aapt::configuration::handler::GlTextureGroupTagHandler;
66 using ::aapt::configuration::handler::LocaleGroupTagHandler;
67 using ::aapt::configuration::handler::ScreenDensityGroupTagHandler;
68 using ::aapt::io::IFile;
69 using ::aapt::io::RegularFile;
70 using ::aapt::io::StringInputStream;
71 using ::aapt::util::TrimWhitespace;
72 using ::aapt::xml::Element;
73 using ::aapt::xml::NodeCast;
74 using ::aapt::xml::XmlActionExecutor;
75 using ::aapt::xml::XmlActionExecutorPolicy;
76 using ::aapt::xml::XmlNodeAction;
77 using ::android::StringPiece;
78 using ::android::base::ReadFileToString;
79 
80 const std::unordered_map<StringPiece, Abi> kStringToAbiMap = {
81     {"armeabi", Abi::kArmeV6}, {"armeabi-v7a", Abi::kArmV7a},  {"arm64-v8a", Abi::kArm64V8a},
82     {"x86", Abi::kX86},        {"x86_64", Abi::kX86_64},       {"mips", Abi::kMips},
83     {"mips64", Abi::kMips64},  {"universal", Abi::kUniversal},
84 };
85 const std::array<StringPiece, 8> kAbiToStringMap = {
86     {"armeabi", "armeabi-v7a", "arm64-v8a", "x86", "x86_64", "mips", "mips64", "universal"}};
87 
88 constexpr const char* kAaptXmlNs = "http://schemas.android.com/tools/aapt";
89 
90 android::NoOpDiagnostics noop_;
91 
92 /** Returns the value of the label attribute for a given element. */
GetLabel(const Element * element,android::IDiagnostics * diag)93 std::string GetLabel(const Element* element, android::IDiagnostics* diag) {
94   std::string label;
95   for (const auto& attr : element->attributes) {
96     if (attr.name == "label") {
97       label = attr.value;
98       break;
99     }
100   }
101 
102   if (label.empty()) {
103     diag->Error(android::DiagMessage() << "No label found for element " << element->name);
104   }
105   return label;
106 }
107 
108 /** Returns the value of the version-code-order attribute for a given element. */
GetVersionCodeOrder(const Element * element,android::IDiagnostics * diag)109 std::optional<int32_t> GetVersionCodeOrder(const Element* element, android::IDiagnostics* diag) {
110   const xml::Attribute* version = element->FindAttribute("", "version-code-order");
111   if (version == nullptr) {
112     std::string label = GetLabel(element, diag);
113     diag->Error(android::DiagMessage() << "No version-code-order found for element '"
114                                        << element->name << "' with label '" << label << "'");
115     return {};
116   }
117   return std::stoi(version->value);
118 }
119 
120 /** XML node visitor that removes all of the namespace URIs from the node and all children. */
121 class NamespaceVisitor : public xml::Visitor {
122  public:
Visit(xml::Element * node)123   void Visit(xml::Element* node) override {
124     node->namespace_uri.clear();
125     VisitChildren(node);
126   }
127 };
128 
129 /** Copies the values referenced in a configuration group to the target list. */
130 template <typename T>
CopyXmlReferences(const std::optional<std::string> & name,const Group<T> & groups,std::vector<T> * target)131 bool CopyXmlReferences(const std::optional<std::string>& name, const Group<T>& groups,
132                        std::vector<T>* target) {
133   // If there was no item configured, there is nothing to do and no error.
134   if (!name) {
135     return true;
136   }
137 
138   // If the group could not be found, then something is wrong.
139   auto group = groups.find(name.value());
140   if (group == groups.end()) {
141     return false;
142   }
143 
144   for (const T& item : group->second.entry) {
145     target->push_back(item);
146   }
147   return true;
148 }
149 
150 /**
151  * Attempts to replace the placeholder in the name string with the provided value. Returns true on
152  * success, or false if the either the placeholder is not found in the name, or the value is not
153  * present and the placeholder was.
154  */
ReplacePlaceholder(StringPiece placeholder,const std::optional<StringPiece> & value,std::string * name,android::IDiagnostics * diag)155 bool ReplacePlaceholder(StringPiece placeholder, const std::optional<StringPiece>& value,
156                         std::string* name, android::IDiagnostics* diag) {
157   size_t offset = name->find(placeholder.data());
158   bool found = (offset != std::string::npos);
159 
160   // Make sure the placeholder was present if the desired value is present.
161   if (!found) {
162     if (value) {
163       diag->Error(android::DiagMessage() << "Missing placeholder for artifact: " << placeholder);
164       return false;
165     }
166     return true;
167   }
168 
169   DCHECK(found) << "Missing return path for placeholder not found";
170 
171   // Make sure the placeholder was not present if the desired value was not present.
172   if (!value) {
173     diag->Error(android::DiagMessage()
174                 << "Placeholder present but no value for artifact: " << placeholder);
175     return false;
176   }
177 
178   name->replace(offset, placeholder.length(), value.value().data());
179 
180   // Make sure there was only one instance of the placeholder.
181   if (name->find(placeholder.data()) != std::string::npos) {
182     diag->Error(android::DiagMessage() << "Placeholder present multiple times: " << placeholder);
183     return false;
184   }
185   return true;
186 }
187 
188 /**
189  * An ActionHandler for processing XML elements in the XmlActionExecutor. Returns true if the
190  * element was successfully processed, otherwise returns false.
191  */
192 using ActionHandler = std::function<bool(configuration::PostProcessingConfiguration* config,
193                                          xml::Element* element, android::IDiagnostics* diag)>;
194 
195 /** Binds an ActionHandler to the current configuration being populated. */
Bind(configuration::PostProcessingConfiguration * config,const ActionHandler & handler)196 xml::XmlNodeAction::ActionFuncWithDiag Bind(configuration::PostProcessingConfiguration* config,
197                                             const ActionHandler& handler) {
198   return [config, handler](xml::Element* root_element, android::SourcePathDiagnostics* diag) {
199     return handler(config, root_element, diag);
200   };
201 }
202 
203 /** Converts a ConfiguredArtifact into an OutputArtifact. */
ToOutputArtifact(const ConfiguredArtifact & artifact,const std::string & apk_name,const PostProcessingConfiguration & config,android::IDiagnostics * diag)204 std::optional<OutputArtifact> ToOutputArtifact(const ConfiguredArtifact& artifact,
205                                                const std::string& apk_name,
206                                                const PostProcessingConfiguration& config,
207                                                android::IDiagnostics* diag) {
208   if (!artifact.name && !config.artifact_format) {
209     diag->Error(android::DiagMessage()
210                 << "Artifact does not have a name and no global name template defined");
211     return {};
212   }
213 
214   std::optional<std::string> artifact_name =
215       (artifact.name) ? artifact.Name(apk_name, diag)
216                       : artifact.ToArtifactName(config.artifact_format.value(), apk_name, diag);
217 
218   if (!artifact_name) {
219     diag->Error(android::DiagMessage() << "Could not determine split APK artifact name");
220     return {};
221   }
222 
223   OutputArtifact output_artifact;
224   output_artifact.name = artifact_name.value();
225 
226   android::SourcePathDiagnostics src_diag{{output_artifact.name}, diag};
227   bool has_errors = false;
228 
229   if (!CopyXmlReferences(artifact.abi_group, config.abi_groups, &output_artifact.abis)) {
230     src_diag.Error(android::DiagMessage()
231                    << "Could not lookup required ABIs: " << artifact.abi_group.value());
232     has_errors = true;
233   }
234 
235   if (!CopyXmlReferences(artifact.locale_group, config.locale_groups, &output_artifact.locales)) {
236     src_diag.Error(android::DiagMessage()
237                    << "Could not lookup required locales: " << artifact.locale_group.value());
238     has_errors = true;
239   }
240 
241   if (!CopyXmlReferences(artifact.screen_density_group, config.screen_density_groups,
242                          &output_artifact.screen_densities)) {
243     src_diag.Error(android::DiagMessage() << "Could not lookup required screen densities: "
244                                           << artifact.screen_density_group.value());
245     has_errors = true;
246   }
247 
248   if (!CopyXmlReferences(artifact.device_feature_group, config.device_feature_groups,
249                          &output_artifact.features)) {
250     src_diag.Error(android::DiagMessage() << "Could not lookup required device features: "
251                                           << artifact.device_feature_group.value());
252     has_errors = true;
253   }
254 
255   if (!CopyXmlReferences(artifact.gl_texture_group, config.gl_texture_groups,
256                          &output_artifact.textures)) {
257     src_diag.Error(android::DiagMessage() << "Could not lookup required OpenGL texture formats: "
258                                           << artifact.gl_texture_group.value());
259     has_errors = true;
260   }
261 
262   if (artifact.android_sdk) {
263     auto entry = config.android_sdks.find(artifact.android_sdk.value());
264     if (entry == config.android_sdks.end()) {
265       src_diag.Error(android::DiagMessage() << "Could not lookup required Android SDK version: "
266                                             << artifact.android_sdk.value());
267       has_errors = true;
268     } else {
269       output_artifact.android_sdk = {entry->second};
270     }
271   }
272 
273   if (has_errors) {
274     return {};
275   }
276   return {output_artifact};
277 }
278 
279 }  // namespace
280 
281 namespace configuration {
282 
283 /** Returns the binary reprasentation of the XML configuration. */
ExtractConfiguration(const std::string & contents,const std::string & config_path,android::IDiagnostics * diag)284 std::optional<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
285                                                                 const std::string& config_path,
286                                                                 android::IDiagnostics* diag) {
287   StringInputStream in(contents);
288   std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, diag, android::Source(config_path));
289   if (!doc) {
290     return {};
291   }
292 
293   // Strip any namespaces from the XML as the XmlActionExecutor ignores anything with a namespace.
294   Element* root = doc->root.get();
295   if (root == nullptr) {
296     diag->Error(android::DiagMessage() << "Could not find the root element in the XML document");
297     return {};
298   }
299 
300   std::string& xml_ns = root->namespace_uri;
301   if (!xml_ns.empty()) {
302     if (xml_ns != kAaptXmlNs) {
303       diag->Error(android::DiagMessage() << "Unknown namespace found on root element: " << xml_ns);
304       return {};
305     }
306 
307     xml_ns.clear();
308     NamespaceVisitor visitor;
309     root->Accept(&visitor);
310   }
311 
312   XmlActionExecutor executor;
313   XmlNodeAction& root_action = executor["post-process"];
314   XmlNodeAction& artifacts_action = root_action["artifacts"];
315 
316   PostProcessingConfiguration config;
317 
318   // Parse the artifact elements.
319   artifacts_action["artifact"].Action(Bind(&config, ArtifactTagHandler));
320   artifacts_action["artifact-format"].Action(Bind(&config, ArtifactFormatTagHandler));
321 
322   // Parse the different configuration groups.
323   root_action["abi-groups"]["abi-group"].Action(Bind(&config, AbiGroupTagHandler));
324   root_action["screen-density-groups"]["screen-density-group"].Action(
325       Bind(&config, ScreenDensityGroupTagHandler));
326   root_action["locale-groups"]["locale-group"].Action(Bind(&config, LocaleGroupTagHandler));
327   root_action["android-sdks"]["android-sdk"].Action(Bind(&config, AndroidSdkTagHandler));
328   root_action["gl-texture-groups"]["gl-texture-group"].Action(
329       Bind(&config, GlTextureGroupTagHandler));
330   root_action["device-feature-groups"]["device-feature-group"].Action(
331       Bind(&config, DeviceFeatureGroupTagHandler));
332 
333   if (!executor.Execute(XmlActionExecutorPolicy::kNone, diag, doc.get())) {
334     diag->Error(android::DiagMessage() << "Could not process XML document");
335     return {};
336   }
337 
338   return {config};
339 }
340 
AbiToString(Abi abi)341 StringPiece AbiToString(Abi abi) {
342   return kAbiToStringMap.at(static_cast<size_t>(abi));
343 }
344 
345 /**
346  * Returns the common artifact base name from a template string.
347  */
ToBaseName(std::string result,StringPiece apk_name,android::IDiagnostics * diag)348 std::optional<std::string> ToBaseName(std::string result, StringPiece apk_name,
349                                       android::IDiagnostics* diag) {
350   const StringPiece ext = file::GetExtension(apk_name);
351   size_t end_index = apk_name.rfind(ext);
352   const std::string base_name =
353       (end_index != std::string::npos) ? std::string{apk_name.begin(), end_index} : "";
354 
355   // Base name is optional.
356   if (result.find("${basename}") != std::string::npos) {
357     auto maybe_base_name = base_name.empty() ? std::nullopt
358                                              : std::optional<StringPiece>{base_name};
359     if (!ReplacePlaceholder("${basename}", maybe_base_name, &result, diag)) {
360       return {};
361     }
362   }
363 
364   // Extension is optional.
365   if (result.find("${ext}") != std::string::npos) {
366     // Make sure we disregard the '.' in the extension when replacing the placeholder.
367     if (!ReplacePlaceholder("${ext}", {ext.substr(1)}, &result, diag)) {
368       return {};
369     }
370   } else {
371     // If no extension is specified, and the name template does not end in the current extension,
372     // add the existing extension.
373     if (!util::EndsWith(result, ext)) {
374       result.append(ext);
375     }
376   }
377 
378   return result;
379 }
380 
ToArtifactName(StringPiece format,StringPiece apk_name,android::IDiagnostics * diag) const381 std::optional<std::string> ConfiguredArtifact::ToArtifactName(StringPiece format,
382                                                               StringPiece apk_name,
383                                                               android::IDiagnostics* diag) const {
384   std::optional<std::string> base = ToBaseName(std::string(format), apk_name, diag);
385   if (!base) {
386     return {};
387   }
388   std::string result = std::move(base.value());
389 
390   if (!ReplacePlaceholder("${abi}", abi_group, &result, diag)) {
391     return {};
392   }
393 
394   if (!ReplacePlaceholder("${density}", screen_density_group, &result, diag)) {
395     return {};
396   }
397 
398   if (!ReplacePlaceholder("${locale}", locale_group, &result, diag)) {
399     return {};
400   }
401 
402   if (!ReplacePlaceholder("${sdk}", android_sdk, &result, diag)) {
403     return {};
404   }
405 
406   if (!ReplacePlaceholder("${feature}", device_feature_group, &result, diag)) {
407     return {};
408   }
409 
410   if (!ReplacePlaceholder("${gl}", gl_texture_group, &result, diag)) {
411     return {};
412   }
413 
414   return result;
415 }
416 
Name(StringPiece apk_name,android::IDiagnostics * diag) const417 std::optional<std::string> ConfiguredArtifact::Name(StringPiece apk_name,
418                                                     android::IDiagnostics* diag) const {
419   if (!name) {
420     return {};
421   }
422 
423   return ToBaseName(name.value(), apk_name, diag);
424 }
425 
426 }  // namespace configuration
427 
428 /** Returns a ConfigurationParser for the file located at the provided path. */
ForPath(const std::string & path)429 std::optional<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) {
430   std::string contents;
431   if (!ReadFileToString(path, &contents, true)) {
432     return {};
433   }
434   return ConfigurationParser(contents, path);
435 }
436 
ConfigurationParser(std::string contents,const std::string & config_path)437 ConfigurationParser::ConfigurationParser(std::string contents, const std::string& config_path)
438     : contents_(std::move(contents)), config_path_(config_path), diag_(&noop_) {
439 }
440 
Parse(android::StringPiece apk_path)441 std::optional<std::vector<OutputArtifact>> ConfigurationParser::Parse(
442     android::StringPiece apk_path) {
443   std::optional<PostProcessingConfiguration> maybe_config =
444       ExtractConfiguration(contents_, config_path_, diag_);
445   if (!maybe_config) {
446     return {};
447   }
448 
449   // Convert from a parsed configuration to a list of artifacts for processing.
450   const std::string apk_name(file::GetFilename(apk_path));
451   std::vector<OutputArtifact> output_artifacts;
452 
453   PostProcessingConfiguration& config = maybe_config.value();
454 
455   bool valid = true;
456   int version = 1;
457 
458   for (const ConfiguredArtifact& artifact : config.artifacts) {
459     std::optional<OutputArtifact> output_artifact =
460         ToOutputArtifact(artifact, apk_name, config, diag_);
461     if (!output_artifact) {
462       // Defer return an error condition so that all errors are reported.
463       valid = false;
464     } else {
465       output_artifact.value().version = version++;
466       output_artifacts.push_back(std::move(output_artifact.value()));
467     }
468   }
469 
470   if (!config.ValidateVersionCodeOrdering(diag_)) {
471     diag_->Error(android::DiagMessage() << "could not validate post processing configuration");
472     valid = false;
473   }
474 
475   if (valid) {
476     // Sorting artifacts requires that all references are valid as it uses them to determine order.
477     config.SortArtifacts();
478   }
479 
480   if (!valid) {
481     return {};
482   }
483 
484   return {output_artifacts};
485 }
486 
487 namespace configuration {
488 namespace handler {
489 
ArtifactTagHandler(PostProcessingConfiguration * config,Element * root_element,android::IDiagnostics * diag)490 bool ArtifactTagHandler(PostProcessingConfiguration* config, Element* root_element,
491                         android::IDiagnostics* diag) {
492   ConfiguredArtifact artifact{};
493   for (const auto& attr : root_element->attributes) {
494     if (attr.name == "name") {
495       artifact.name = attr.value;
496     } else if (attr.name == "abi-group") {
497       artifact.abi_group = {attr.value};
498     } else if (attr.name == "screen-density-group") {
499       artifact.screen_density_group = {attr.value};
500     } else if (attr.name == "locale-group") {
501       artifact.locale_group = {attr.value};
502     } else if (attr.name == "android-sdk") {
503       artifact.android_sdk = {attr.value};
504     } else if (attr.name == "gl-texture-group") {
505       artifact.gl_texture_group = {attr.value};
506     } else if (attr.name == "device-feature-group") {
507       artifact.device_feature_group = {attr.value};
508     } else {
509       diag->Note(android::DiagMessage()
510                  << "Unknown artifact attribute: " << attr.name << " = " << attr.value);
511     }
512   }
513   config->artifacts.push_back(artifact);
514   return true;
515 };
516 
ArtifactFormatTagHandler(PostProcessingConfiguration * config,Element * root_element,android::IDiagnostics *)517 bool ArtifactFormatTagHandler(PostProcessingConfiguration* config, Element* root_element,
518                               android::IDiagnostics* /* diag */) {
519   for (auto& node : root_element->children) {
520     xml::Text* t;
521     if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
522       config->artifact_format.emplace(TrimWhitespace(t->text));
523       break;
524     }
525   }
526   return true;
527 };
528 
AbiGroupTagHandler(PostProcessingConfiguration * config,Element * root_element,android::IDiagnostics * diag)529 bool AbiGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
530                         android::IDiagnostics* diag) {
531   std::string label = GetLabel(root_element, diag);
532   if (label.empty()) {
533     return false;
534   }
535 
536   bool valid = true;
537   OrderedEntry<Abi>& entry = config->abi_groups[label];
538   std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
539   if (!order) {
540     valid = false;
541   } else {
542     entry.order = order.value();
543   }
544   auto& group = entry.entry;
545 
546   // Special case for empty abi-group tag. Label will be used as the ABI.
547   if (root_element->GetChildElements().empty()) {
548     auto abi = kStringToAbiMap.find(label);
549     if (abi == kStringToAbiMap.end()) {
550       return false;
551     }
552     group.push_back(abi->second);
553     return valid;
554   }
555 
556   for (auto* child : root_element->GetChildElements()) {
557     if (child->name != "abi") {
558       diag->Error(android::DiagMessage() << "Unexpected element in ABI group: " << child->name);
559       valid = false;
560     } else {
561       for (auto& node : child->children) {
562         xml::Text* t;
563         if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
564           auto abi = kStringToAbiMap.find(TrimWhitespace(t->text));
565           if (abi != kStringToAbiMap.end()) {
566             group.push_back(abi->second);
567           } else {
568             diag->Error(android::DiagMessage() << "Could not parse ABI value: " << t->text);
569             valid = false;
570           }
571           break;
572         }
573       }
574     }
575   }
576 
577   return valid;
578 };
579 
ScreenDensityGroupTagHandler(PostProcessingConfiguration * config,Element * root_element,android::IDiagnostics * diag)580 bool ScreenDensityGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
581                                   android::IDiagnostics* diag) {
582   std::string label = GetLabel(root_element, diag);
583   if (label.empty()) {
584     return false;
585   }
586 
587   bool valid = true;
588   OrderedEntry<ConfigDescription>& entry = config->screen_density_groups[label];
589   std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
590   if (!order) {
591     valid = false;
592   } else {
593     entry.order = order.value();
594   }
595   auto& group = entry.entry;
596 
597   // Special case for empty screen-density-group tag. Label will be used as the screen density.
598   if (root_element->GetChildElements().empty()) {
599     ConfigDescription config_descriptor;
600     bool parsed = ConfigDescription::Parse(label, &config_descriptor);
601     if (parsed &&
602         (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
603             android::ResTable_config::CONFIG_DENSITY)) {
604       // Copy the density with the minimum SDK version stripped out.
605       group.push_back(config_descriptor.CopyWithoutSdkVersion());
606     } else {
607       diag->Error(android::DiagMessage()
608                   << "Could not parse config descriptor for empty screen-density-group: " << label);
609       valid = false;
610     }
611 
612     return valid;
613   }
614 
615   for (auto* child : root_element->GetChildElements()) {
616     if (child->name != "screen-density") {
617       diag->Error(android::DiagMessage()
618                   << "Unexpected root_element in screen density group: " << child->name);
619       valid = false;
620     } else {
621       for (auto& node : child->children) {
622         xml::Text* t;
623         if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
624           ConfigDescription config_descriptor;
625           android::StringPiece text = TrimWhitespace(t->text);
626           bool parsed = ConfigDescription::Parse(text, &config_descriptor);
627           if (parsed &&
628               (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
629                android::ResTable_config::CONFIG_DENSITY)) {
630             // Copy the density with the minimum SDK version stripped out.
631             group.push_back(config_descriptor.CopyWithoutSdkVersion());
632           } else {
633             diag->Error(android::DiagMessage()
634                         << "Could not parse config descriptor for screen-density: " << text);
635             valid = false;
636           }
637           break;
638         }
639       }
640     }
641   }
642 
643   return valid;
644 };
645 
LocaleGroupTagHandler(PostProcessingConfiguration * config,Element * root_element,android::IDiagnostics * diag)646 bool LocaleGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
647                            android::IDiagnostics* diag) {
648   std::string label = GetLabel(root_element, diag);
649   if (label.empty()) {
650     return false;
651   }
652 
653   bool valid = true;
654   OrderedEntry<ConfigDescription>& entry = config->locale_groups[label];
655   std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
656   if (!order) {
657     valid = false;
658   } else {
659     entry.order = order.value();
660   }
661   auto& group = entry.entry;
662 
663   // Special case to auto insert a locale for an empty group. Label will be used for locale.
664   if (root_element->GetChildElements().empty()) {
665     ConfigDescription config_descriptor;
666     bool parsed = ConfigDescription::Parse(label, &config_descriptor);
667     if (parsed &&
668         (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
669             android::ResTable_config::CONFIG_LOCALE)) {
670       // Copy the locale with the minimum SDK version stripped out.
671       group.push_back(config_descriptor.CopyWithoutSdkVersion());
672     } else {
673       diag->Error(android::DiagMessage()
674                   << "Could not parse config descriptor for empty screen-density-group: " << label);
675       valid = false;
676     }
677 
678     return valid;
679   }
680 
681   for (auto* child : root_element->GetChildElements()) {
682     if (child->name != "locale") {
683       diag->Error(android::DiagMessage()
684                   << "Unexpected root_element in screen density group: " << child->name);
685       valid = false;
686     } else {
687       for (auto& node : child->children) {
688         xml::Text* t;
689         if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
690           ConfigDescription config_descriptor;
691           android::StringPiece text = TrimWhitespace(t->text);
692           bool parsed = ConfigDescription::Parse(text, &config_descriptor);
693           if (parsed &&
694               (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
695                android::ResTable_config::CONFIG_LOCALE)) {
696             // Copy the locale with the minimum SDK version stripped out.
697             group.push_back(config_descriptor.CopyWithoutSdkVersion());
698           } else {
699             diag->Error(android::DiagMessage()
700                         << "Could not parse config descriptor for screen-density: " << text);
701             valid = false;
702           }
703           break;
704         }
705       }
706     }
707   }
708 
709   return valid;
710 };
711 
AndroidSdkTagHandler(PostProcessingConfiguration * config,Element * root_element,android::IDiagnostics * diag)712 bool AndroidSdkTagHandler(PostProcessingConfiguration* config, Element* root_element,
713                           android::IDiagnostics* diag) {
714   AndroidSdk entry = AndroidSdk::ForMinSdk(-1);
715   bool valid = true;
716   for (const auto& attr : root_element->attributes) {
717     bool valid_attr = false;
718     if (attr.name == "label") {
719       entry.label = attr.value;
720       valid_attr = true;
721     } else if (attr.name == "minSdkVersion") {
722       std::optional<int> version = ResourceUtils::ParseSdkVersion(attr.value);
723       if (version) {
724         valid_attr = true;
725         entry.min_sdk_version = version.value();
726       }
727     } else if (attr.name == "targetSdkVersion") {
728       std::optional<int> version = ResourceUtils::ParseSdkVersion(attr.value);
729       if (version) {
730         valid_attr = true;
731         entry.target_sdk_version = version;
732       }
733     } else if (attr.name == "maxSdkVersion") {
734       std::optional<int> version = ResourceUtils::ParseSdkVersion(attr.value);
735       if (version) {
736         valid_attr = true;
737         entry.max_sdk_version = version;
738       }
739     }
740 
741     if (!valid_attr) {
742       diag->Error(android::DiagMessage()
743                   << "Invalid attribute: " << attr.name << " = " << attr.value);
744       valid = false;
745     }
746   }
747 
748   if (entry.min_sdk_version == -1) {
749     diag->Error(android::DiagMessage() << "android-sdk is missing minSdkVersion attribute");
750     valid = false;
751   }
752 
753   // TODO: Fill in the manifest details when they are finalised.
754   for (auto node : root_element->GetChildElements()) {
755     if (node->name == "manifest") {
756       if (entry.manifest) {
757         diag->Warn(android::DiagMessage() << "Found multiple manifest tags. Ignoring duplicates.");
758         continue;
759       }
760       entry.manifest = {AndroidManifest()};
761     }
762   }
763 
764   config->android_sdks[entry.label] = entry;
765   return valid;
766 };
767 
GlTextureGroupTagHandler(PostProcessingConfiguration * config,Element * root_element,android::IDiagnostics * diag)768 bool GlTextureGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
769                               android::IDiagnostics* diag) {
770   std::string label = GetLabel(root_element, diag);
771   if (label.empty()) {
772     return false;
773   }
774 
775   bool valid = true;
776   OrderedEntry<GlTexture>& entry = config->gl_texture_groups[label];
777   std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
778   if (!order) {
779     valid = false;
780   } else {
781     entry.order = order.value();
782   }
783   auto& group = entry.entry;
784 
785   GlTexture result;
786   for (auto* child : root_element->GetChildElements()) {
787     if (child->name != "gl-texture") {
788       diag->Error(android::DiagMessage()
789                   << "Unexpected element in GL texture group: " << child->name);
790       valid = false;
791     } else {
792       for (const auto& attr : child->attributes) {
793         if (attr.name == "name") {
794           result.name = attr.value;
795           break;
796         }
797       }
798 
799       for (auto* element : child->GetChildElements()) {
800         if (element->name != "texture-path") {
801           diag->Error(android::DiagMessage()
802                       << "Unexpected element in gl-texture element: " << child->name);
803           valid = false;
804           continue;
805         }
806         for (auto& node : element->children) {
807           xml::Text* t;
808           if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
809             result.texture_paths.emplace_back(TrimWhitespace(t->text));
810           }
811         }
812       }
813     }
814     group.push_back(result);
815   }
816 
817   return valid;
818 };
819 
DeviceFeatureGroupTagHandler(PostProcessingConfiguration * config,Element * root_element,android::IDiagnostics * diag)820 bool DeviceFeatureGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
821                                   android::IDiagnostics* diag) {
822   std::string label = GetLabel(root_element, diag);
823   if (label.empty()) {
824     return false;
825   }
826 
827   bool valid = true;
828   OrderedEntry<DeviceFeature>& entry = config->device_feature_groups[label];
829   std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
830   if (!order) {
831     valid = false;
832   } else {
833     entry.order = order.value();
834   }
835   auto& group = entry.entry;
836 
837   for (auto* child : root_element->GetChildElements()) {
838     if (child->name != "supports-feature") {
839       diag->Error(android::DiagMessage()
840                   << "Unexpected root_element in device feature group: " << child->name);
841       valid = false;
842     } else {
843       for (auto& node : child->children) {
844         xml::Text* t;
845         if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
846           group.emplace_back(TrimWhitespace(t->text));
847           break;
848         }
849       }
850     }
851   }
852 
853   return valid;
854 };
855 
856 }  // namespace handler
857 }  // namespace configuration
858 
859 }  // namespace aapt
860