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