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