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