174cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer/*
274cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer * Copyright (C) 2017 The Android Open Source Project
374cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer *
474cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer * Licensed under the Apache License, Version 2.0 (the "License");
574cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer * you may not use this file except in compliance with the License.
674cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer * You may obtain a copy of the License at
774cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer *
874cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer *      http://www.apache.org/licenses/LICENSE-2.0
974cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer *
1074cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer * Unless required by applicable law or agreed to in writing, software
1174cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer * distributed under the License is distributed on an "AS IS" BASIS,
1274cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1374cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer * See the License for the specific language governing permissions and
1474cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer * limitations under the License.
1574cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer */
1674cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
1774cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer#include "configuration/ConfigurationParser.h"
1874cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
1974cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer#include <algorithm>
2074cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer#include <functional>
215766943f558c8fccc714bb3d0a96af70816a5545Shane Farmer#include <map>
2274cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer#include <memory>
2311cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer#include <string>
2474cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer#include <utility>
2574cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
26efeb7af13be4446ce4a511a2ca707691e9a67c1eAdam Lesinski#include "android-base/file.h"
27efeb7af13be4446ce4a511a2ca707691e9a67c1eAdam Lesinski#include "android-base/logging.h"
2874cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
2974cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer#include "ConfigDescription.h"
3074cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer#include "Diagnostics.h"
313edd47264335cf609ac6e733db77522f7c959c3fShane Farmer#include "ResourceUtils.h"
32cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer#include "configuration/ConfigurationParser.internal.h"
33b102727771b9ac3d28d4f9b0a4a54b98bd1af958Shane Farmer#include "io/File.h"
34b102727771b9ac3d28d4f9b0a4a54b98bd1af958Shane Farmer#include "io/FileSystem.h"
35004511660671511ae88e0e837a6f92db28eadaefAdam Lesinski#include "io/StringStream.h"
369ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer#include "util/Files.h"
379f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer#include "util/Maybe.h"
3874cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer#include "util/Util.h"
3974cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer#include "xml/XmlActionExecutor.h"
4074cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer#include "xml/XmlDom.h"
4174cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer#include "xml/XmlUtil.h"
4274cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
4374cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmernamespace aapt {
4474cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
4574cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmernamespace {
4674cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
4774cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmerusing ::aapt::configuration::Abi;
4874cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmerusing ::aapt::configuration::AndroidManifest;
4974cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmerusing ::aapt::configuration::AndroidSdk;
50cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerusing ::aapt::configuration::ConfiguredArtifact;
51cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerusing ::aapt::configuration::DeviceFeature;
52cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerusing ::aapt::configuration::Entry;
5378c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmerusing ::aapt::configuration::ExtractConfiguration;
5474cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmerusing ::aapt::configuration::GlTexture;
5574cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmerusing ::aapt::configuration::Group;
5674cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmerusing ::aapt::configuration::Locale;
5778c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmerusing ::aapt::configuration::OrderedEntry;
58cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerusing ::aapt::configuration::OutputArtifact;
59cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerusing ::aapt::configuration::PostProcessingConfiguration;
60cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerusing ::aapt::configuration::handler::AbiGroupTagHandler;
6178c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmerusing ::aapt::configuration::handler::AndroidSdkTagHandler;
62cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerusing ::aapt::configuration::handler::ArtifactFormatTagHandler;
63cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerusing ::aapt::configuration::handler::ArtifactTagHandler;
64cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerusing ::aapt::configuration::handler::DeviceFeatureGroupTagHandler;
65cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerusing ::aapt::configuration::handler::GlTextureGroupTagHandler;
66cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerusing ::aapt::configuration::handler::LocaleGroupTagHandler;
67cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerusing ::aapt::configuration::handler::ScreenDensityGroupTagHandler;
68b102727771b9ac3d28d4f9b0a4a54b98bd1af958Shane Farmerusing ::aapt::io::IFile;
69b102727771b9ac3d28d4f9b0a4a54b98bd1af958Shane Farmerusing ::aapt::io::RegularFile;
70efeb7af13be4446ce4a511a2ca707691e9a67c1eAdam Lesinskiusing ::aapt::io::StringInputStream;
7174cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmerusing ::aapt::util::TrimWhitespace;
7274cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmerusing ::aapt::xml::Element;
7374cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmerusing ::aapt::xml::NodeCast;
7474cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmerusing ::aapt::xml::XmlActionExecutor;
7574cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmerusing ::aapt::xml::XmlActionExecutorPolicy;
7674cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmerusing ::aapt::xml::XmlNodeAction;
770a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmerusing ::android::StringPiece;
78cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerusing ::android::base::ReadFileToString;
7974cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
80cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerconst std::unordered_map<StringPiece, Abi> kStringToAbiMap = {
815766943f558c8fccc714bb3d0a96af70816a5545Shane Farmer    {"armeabi", Abi::kArmeV6}, {"armeabi-v7a", Abi::kArmV7a},  {"arm64-v8a", Abi::kArm64V8a},
825766943f558c8fccc714bb3d0a96af70816a5545Shane Farmer    {"x86", Abi::kX86},        {"x86_64", Abi::kX86_64},       {"mips", Abi::kMips},
835766943f558c8fccc714bb3d0a96af70816a5545Shane Farmer    {"mips64", Abi::kMips64},  {"universal", Abi::kUniversal},
845766943f558c8fccc714bb3d0a96af70816a5545Shane Farmer};
85cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerconst std::array<StringPiece, 8> kAbiToStringMap = {
86cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    {"armeabi", "armeabi-v7a", "arm64-v8a", "x86", "x86_64", "mips", "mips64", "universal"}};
8774cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
8874cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmerconstexpr const char* kAaptXmlNs = "http://schemas.android.com/tools/aapt";
8974cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
9074cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer/** A default noop diagnostics context. */
9174cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmerclass NoopDiagnostics : public IDiagnostics {
9274cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer public:
9374cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer  void Log(Level level, DiagMessageActual& actualMsg) override {}
9474cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer};
9574cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane FarmerNoopDiagnostics noop_;
9674cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
9711cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer/** Returns the value of the label attribute for a given element. */
9874cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmerstd::string GetLabel(const Element* element, IDiagnostics* diag) {
9974cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer  std::string label;
10074cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer  for (const auto& attr : element->attributes) {
10174cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer    if (attr.name == "label") {
10274cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer      label = attr.value;
10374cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer      break;
10474cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer    }
10574cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer  }
10674cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
10774cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer  if (label.empty()) {
10874cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer    diag->Error(DiagMessage() << "No label found for element " << element->name);
10974cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer  }
11074cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer  return label;
11174cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer}
11274cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
11311cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer/** Returns the value of the version-code-order attribute for a given element. */
11411cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane FarmerMaybe<int32_t> GetVersionCodeOrder(const Element* element, IDiagnostics* diag) {
11511cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  const xml::Attribute* version = element->FindAttribute("", "version-code-order");
11611cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  if (version == nullptr) {
11711cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer    std::string label = GetLabel(element, diag);
11811cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer    diag->Error(DiagMessage() << "No version-code-order found for element '" << element->name
11911cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer                              << "' with label '" << label << "'");
12011cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer    return {};
12111cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  }
12211cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  return std::stoi(version->value);
12311cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer}
12411cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer
12574cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer/** XML node visitor that removes all of the namespace URIs from the node and all children. */
12674cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmerclass NamespaceVisitor : public xml::Visitor {
12774cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer public:
12874cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer  void Visit(xml::Element* node) override {
12974cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer    node->namespace_uri.clear();
13074cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer    VisitChildren(node);
13174cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer  }
13274cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer};
13374cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
134cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer/** Copies the values referenced in a configuration group to the target list. */
135cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmertemplate <typename T>
136cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerbool CopyXmlReferences(const Maybe<std::string>& name, const Group<T>& groups,
137cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                       std::vector<T>* target) {
138cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  // If there was no item configured, there is nothing to do and no error.
139cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  if (!name) {
140cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    return true;
141cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  }
14274cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
143cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  // If the group could not be found, then something is wrong.
144cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  auto group = groups.find(name.value());
145cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  if (group == groups.end()) {
146cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    return false;
147cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  }
1485766943f558c8fccc714bb3d0a96af70816a5545Shane Farmer
14978c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  for (const T& item : group->second.entry) {
150cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    target->push_back(item);
151cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  }
152cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  return true;
1535766943f558c8fccc714bb3d0a96af70816a5545Shane Farmer}
154b102727771b9ac3d28d4f9b0a4a54b98bd1af958Shane Farmer
1559f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer/**
1569f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer * Attempts to replace the placeholder in the name string with the provided value. Returns true on
1579f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer * success, or false if the either the placeholder is not found in the name, or the value is not
1589f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer * present and the placeholder was.
1599f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer */
160cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerbool ReplacePlaceholder(const StringPiece& placeholder, const Maybe<StringPiece>& value,
161cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                        std::string* name, IDiagnostics* diag) {
1620a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer  size_t offset = name->find(placeholder.data());
1631a21b8c04153b5b73a477ebd0622b24f3bbfc603Shane Farmer  bool found = (offset != std::string::npos);
1641a21b8c04153b5b73a477ebd0622b24f3bbfc603Shane Farmer
1651a21b8c04153b5b73a477ebd0622b24f3bbfc603Shane Farmer  // Make sure the placeholder was present if the desired value is present.
1661a21b8c04153b5b73a477ebd0622b24f3bbfc603Shane Farmer  if (!found) {
1671a21b8c04153b5b73a477ebd0622b24f3bbfc603Shane Farmer    if (value) {
1689f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer      diag->Error(DiagMessage() << "Missing placeholder for artifact: " << placeholder);
1699f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer      return false;
1709f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer    }
1719f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer    return true;
1729f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer  }
1739f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer
1741a21b8c04153b5b73a477ebd0622b24f3bbfc603Shane Farmer  DCHECK(found) << "Missing return path for placeholder not found";
1751a21b8c04153b5b73a477ebd0622b24f3bbfc603Shane Farmer
1769f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer  // Make sure the placeholder was not present if the desired value was not present.
1771a21b8c04153b5b73a477ebd0622b24f3bbfc603Shane Farmer  if (!value) {
1789f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer    diag->Error(DiagMessage() << "Placeholder present but no value for artifact: " << placeholder);
1791a21b8c04153b5b73a477ebd0622b24f3bbfc603Shane Farmer    return false;
1809f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer  }
1811a21b8c04153b5b73a477ebd0622b24f3bbfc603Shane Farmer
1820a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer  name->replace(offset, placeholder.length(), value.value().data());
1831a21b8c04153b5b73a477ebd0622b24f3bbfc603Shane Farmer
1841a21b8c04153b5b73a477ebd0622b24f3bbfc603Shane Farmer  // Make sure there was only one instance of the placeholder.
1850a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer  if (name->find(placeholder.data()) != std::string::npos) {
1861a21b8c04153b5b73a477ebd0622b24f3bbfc603Shane Farmer    diag->Error(DiagMessage() << "Placeholder present multiple times: " << placeholder);
1871a21b8c04153b5b73a477ebd0622b24f3bbfc603Shane Farmer    return false;
1881a21b8c04153b5b73a477ebd0622b24f3bbfc603Shane Farmer  }
1891a21b8c04153b5b73a477ebd0622b24f3bbfc603Shane Farmer  return true;
1909f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer}
1919f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer
1929ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer/**
193cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer * An ActionHandler for processing XML elements in the XmlActionExecutor. Returns true if the
194cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer * element was successfully processed, otherwise returns false.
195cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer */
196cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerusing ActionHandler = std::function<bool(configuration::PostProcessingConfiguration* config,
197cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                                         xml::Element* element, IDiagnostics* diag)>;
198cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
199cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer/** Binds an ActionHandler to the current configuration being populated. */
200cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerxml::XmlNodeAction::ActionFuncWithDiag Bind(configuration::PostProcessingConfiguration* config,
201cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                                            const ActionHandler& handler) {
202cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  return [config, handler](xml::Element* root_element, SourcePathDiagnostics* diag) {
203cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    return handler(config, root_element, diag);
204cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  };
205cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer}
206cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
207cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer/** Converts a ConfiguredArtifact into an OutputArtifact. */
208cb6c3f9b880160c35785b1780b282fdf92952b90Shane FarmerMaybe<OutputArtifact> ToOutputArtifact(const ConfiguredArtifact& artifact,
209cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                                       const std::string& apk_name,
210cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                                       const PostProcessingConfiguration& config,
211cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                                       IDiagnostics* diag) {
212cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  if (!artifact.name && !config.artifact_format) {
213cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    diag->Error(
214cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer        DiagMessage() << "Artifact does not have a name and no global name template defined");
215cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    return {};
216cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  }
217cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
218cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  Maybe<std::string> artifact_name =
219cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer      (artifact.name) ? artifact.Name(apk_name, diag)
220cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                      : artifact.ToArtifactName(config.artifact_format.value(), apk_name, diag);
221cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
222cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  if (!artifact_name) {
223cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    diag->Error(DiagMessage() << "Could not determine split APK artifact name");
224cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    return {};
225cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  }
226cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
227cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  OutputArtifact output_artifact;
228cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  output_artifact.name = artifact_name.value();
229cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
230cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  SourcePathDiagnostics src_diag{{output_artifact.name}, diag};
231cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  bool has_errors = false;
232cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
233cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  if (!CopyXmlReferences(artifact.abi_group, config.abi_groups, &output_artifact.abis)) {
234cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    src_diag.Error(DiagMessage() << "Could not lookup required ABIs: "
235cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                                 << artifact.abi_group.value());
236cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    has_errors = true;
237cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  }
238cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
239cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  if (!CopyXmlReferences(artifact.locale_group, config.locale_groups, &output_artifact.locales)) {
240cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    src_diag.Error(DiagMessage() << "Could not lookup required locales: "
241cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                                 << artifact.locale_group.value());
242cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    has_errors = true;
243cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  }
244cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
245cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  if (!CopyXmlReferences(artifact.screen_density_group, config.screen_density_groups,
246cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                         &output_artifact.screen_densities)) {
247cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    src_diag.Error(DiagMessage() << "Could not lookup required screen densities: "
248cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                                 << artifact.screen_density_group.value());
249cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    has_errors = true;
250cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  }
251cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
252cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  if (!CopyXmlReferences(artifact.device_feature_group, config.device_feature_groups,
253cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                         &output_artifact.features)) {
254cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    src_diag.Error(DiagMessage() << "Could not lookup required device features: "
255cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                                 << artifact.device_feature_group.value());
256cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    has_errors = true;
257cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  }
258cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
259cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  if (!CopyXmlReferences(artifact.gl_texture_group, config.gl_texture_groups,
260cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                         &output_artifact.textures)) {
261cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    src_diag.Error(DiagMessage() << "Could not lookup required OpenGL texture formats: "
262cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                                 << artifact.gl_texture_group.value());
263cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    has_errors = true;
264cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  }
265cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
26678c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  if (artifact.android_sdk) {
26778c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    auto entry = config.android_sdks.find(artifact.android_sdk.value());
26878c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    if (entry == config.android_sdks.end()) {
269cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer      src_diag.Error(DiagMessage() << "Could not lookup required Android SDK version: "
27078c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer                                   << artifact.android_sdk.value());
271cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer      has_errors = true;
272cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    } else {
273cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer      output_artifact.android_sdk = {entry->second};
274cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    }
275cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  }
276cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
277cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  if (has_errors) {
278cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    return {};
279cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  }
280cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  return {output_artifact};
281cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer}
282cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
283cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer}  // namespace
284cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
285cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmernamespace configuration {
286cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
28778c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer/** Returns the binary reprasentation of the XML configuration. */
28878c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane FarmerMaybe<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
28978c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer                                                        const std::string& config_path,
29078c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer                                                        IDiagnostics* diag) {
29178c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  StringInputStream in(contents);
29278c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, diag, Source(config_path));
29378c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  if (!doc) {
29478c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    return {};
29578c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  }
29678c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer
29778c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  // Strip any namespaces from the XML as the XmlActionExecutor ignores anything with a namespace.
29878c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  Element* root = doc->root.get();
29978c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  if (root == nullptr) {
30078c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    diag->Error(DiagMessage() << "Could not find the root element in the XML document");
30178c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    return {};
30278c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  }
30378c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer
30478c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  std::string& xml_ns = root->namespace_uri;
30578c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  if (!xml_ns.empty()) {
30678c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    if (xml_ns != kAaptXmlNs) {
30778c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      diag->Error(DiagMessage() << "Unknown namespace found on root element: " << xml_ns);
30878c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      return {};
30978c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    }
31078c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer
31178c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    xml_ns.clear();
31278c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    NamespaceVisitor visitor;
31378c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    root->Accept(&visitor);
31478c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  }
31578c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer
31678c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  XmlActionExecutor executor;
31778c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  XmlNodeAction& root_action = executor["post-process"];
31878c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  XmlNodeAction& artifacts_action = root_action["artifacts"];
31978c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer
32078c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  PostProcessingConfiguration config;
32178c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer
32278c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  // Parse the artifact elements.
32378c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  artifacts_action["artifact"].Action(Bind(&config, ArtifactTagHandler));
32478c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  artifacts_action["artifact-format"].Action(Bind(&config, ArtifactFormatTagHandler));
32578c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer
32678c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  // Parse the different configuration groups.
32778c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  root_action["abi-groups"]["abi-group"].Action(Bind(&config, AbiGroupTagHandler));
32878c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  root_action["screen-density-groups"]["screen-density-group"].Action(
32978c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      Bind(&config, ScreenDensityGroupTagHandler));
33078c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  root_action["locale-groups"]["locale-group"].Action(Bind(&config, LocaleGroupTagHandler));
33178c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  root_action["android-sdks"]["android-sdk"].Action(Bind(&config, AndroidSdkTagHandler));
33278c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  root_action["gl-texture-groups"]["gl-texture-group"].Action(
33378c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      Bind(&config, GlTextureGroupTagHandler));
33478c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  root_action["device-feature-groups"]["device-feature-group"].Action(
33578c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      Bind(&config, DeviceFeatureGroupTagHandler));
33678c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer
33778c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  if (!executor.Execute(XmlActionExecutorPolicy::kNone, diag, doc.get())) {
33878c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    diag->Error(DiagMessage() << "Could not process XML document");
33978c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    return {};
34078c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  }
34178c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer
34278c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  return {config};
34378c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer}
34478c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer
345cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerconst StringPiece& AbiToString(Abi abi) {
346cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  return kAbiToStringMap.at(static_cast<size_t>(abi));
347cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer}
348cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
349cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer/**
3509ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer * Returns the common artifact base name from a template string.
3519ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer */
3529ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane FarmerMaybe<std::string> ToBaseName(std::string result, const StringPiece& apk_name, IDiagnostics* diag) {
3539ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer  const StringPiece ext = file::GetExtension(apk_name);
3549ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer  size_t end_index = apk_name.to_string().rfind(ext.to_string());
3559ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer  const std::string base_name =
3569ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer      (end_index != std::string::npos) ? std::string{apk_name.begin(), end_index} : "";
3579f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer
3589ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer  // Base name is optional.
3599ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer  if (result.find("${basename}") != std::string::npos) {
3609ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer    Maybe<StringPiece> maybe_base_name =
3619ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer        base_name.empty() ? Maybe<StringPiece>{} : Maybe<StringPiece>{base_name};
3629ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer    if (!ReplacePlaceholder("${basename}", maybe_base_name, &result, diag)) {
3639ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer      return {};
3649ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer    }
3659f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer  }
3669f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer
3670a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer  // Extension is optional.
3680a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer  if (result.find("${ext}") != std::string::npos) {
3699ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer    // Make sure we disregard the '.' in the extension when replacing the placeholder.
3709ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer    if (!ReplacePlaceholder("${ext}", {ext.substr(1)}, &result, diag)) {
3710a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer      return {};
3720a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer    }
3739ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer  } else {
3749ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer    // If no extension is specified, and the name template does not end in the current extension,
3759ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer    // add the existing extension.
3769ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer    if (!util::EndsWith(result, ext)) {
3779ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer      result.append(ext.to_string());
3789ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer    }
3799ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer  }
3809ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer
3819ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer  return result;
3829ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer}
3839ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer
384cb6c3f9b880160c35785b1780b282fdf92952b90Shane FarmerMaybe<std::string> ConfiguredArtifact::ToArtifactName(const StringPiece& format,
385cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                                                      const StringPiece& apk_name,
386cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                                                      IDiagnostics* diag) const {
3879ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer  Maybe<std::string> base = ToBaseName(format.to_string(), apk_name, diag);
3889ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer  if (!base) {
3899ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer    return {};
3900a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer  }
3919ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer  std::string result = std::move(base.value());
3920a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer
3930a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer  if (!ReplacePlaceholder("${abi}", abi_group, &result, diag)) {
3949f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer    return {};
3959f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer  }
3969f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer
3970a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer  if (!ReplacePlaceholder("${density}", screen_density_group, &result, diag)) {
3989f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer    return {};
3999f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer  }
4009f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer
4010a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer  if (!ReplacePlaceholder("${locale}", locale_group, &result, diag)) {
4029f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer    return {};
4039f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer  }
4049f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer
40578c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  if (!ReplacePlaceholder("${sdk}", android_sdk, &result, diag)) {
4069f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer    return {};
4079f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer  }
4089f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer
4090a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer  if (!ReplacePlaceholder("${feature}", device_feature_group, &result, diag)) {
4100a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer    return {};
4110a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer  }
4120a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer
4130a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer  if (!ReplacePlaceholder("${gl}", gl_texture_group, &result, diag)) {
4149f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer    return {};
4159f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer  }
4169f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer
4179f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer  return result;
4189f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer}
4199f0e7f1dbade4b35b85e23954fa2b7c31d5f2009Shane Farmer
420cb6c3f9b880160c35785b1780b282fdf92952b90Shane FarmerMaybe<std::string> ConfiguredArtifact::Name(const StringPiece& apk_name, IDiagnostics* diag) const {
4210a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer  if (!name) {
4220a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer    return {};
4230a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer  }
4240a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer
4259ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer  return ToBaseName(name.value(), apk_name, diag);
4269ecc0751e30086c0668c8fe5758f14fc5acf5ae8Shane Farmer}
4270a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer
4285766943f558c8fccc714bb3d0a96af70816a5545Shane Farmer}  // namespace configuration
429b102727771b9ac3d28d4f9b0a4a54b98bd1af958Shane Farmer
430b102727771b9ac3d28d4f9b0a4a54b98bd1af958Shane Farmer/** Returns a ConfigurationParser for the file located at the provided path. */
431b102727771b9ac3d28d4f9b0a4a54b98bd1af958Shane FarmerMaybe<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) {
432b102727771b9ac3d28d4f9b0a4a54b98bd1af958Shane Farmer  std::string contents;
433b102727771b9ac3d28d4f9b0a4a54b98bd1af958Shane Farmer  if (!ReadFileToString(path, &contents, true)) {
434b102727771b9ac3d28d4f9b0a4a54b98bd1af958Shane Farmer    return {};
435b102727771b9ac3d28d4f9b0a4a54b98bd1af958Shane Farmer  }
43678c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  return ConfigurationParser(contents, path);
437b102727771b9ac3d28d4f9b0a4a54b98bd1af958Shane Farmer}
438b102727771b9ac3d28d4f9b0a4a54b98bd1af958Shane Farmer
43978c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane FarmerConfigurationParser::ConfigurationParser(std::string contents, const std::string& config_path)
44078c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    : contents_(std::move(contents)), config_path_(config_path), diag_(&noop_) {
44174cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer}
44274cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
443cb6c3f9b880160c35785b1780b282fdf92952b90Shane FarmerMaybe<std::vector<OutputArtifact>> ConfigurationParser::Parse(
444cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    const android::StringPiece& apk_path) {
44578c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  Maybe<PostProcessingConfiguration> maybe_config =
44678c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      ExtractConfiguration(contents_, config_path_, diag_);
447cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  if (!maybe_config) {
44874cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer    return {};
44974cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer  }
450cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
451cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  // Convert from a parsed configuration to a list of artifacts for processing.
45278c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  const std::string& apk_name = file::GetFilename(apk_path).to_string();
453cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  std::vector<OutputArtifact> output_artifacts;
454cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
45578c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  PostProcessingConfiguration& config = maybe_config.value();
45678c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer
45711cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  bool valid = true;
45878c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  int version = 1;
45911cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer
46078c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  for (const ConfiguredArtifact& artifact : config.artifacts) {
461cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    Maybe<OutputArtifact> output_artifact = ToOutputArtifact(artifact, apk_name, config, diag_);
462cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    if (!output_artifact) {
463cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer      // Defer return an error condition so that all errors are reported.
46411cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer      valid = false;
465cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    } else {
46678c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      output_artifact.value().version = version++;
467cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer      output_artifacts.push_back(std::move(output_artifact.value()));
468cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    }
469cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  }
470cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
47111cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  if (!config.ValidateVersionCodeOrdering(diag_)) {
47211cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer    diag_->Error(DiagMessage() << "could not validate post processing configuration");
47311cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer    valid = false;
47411cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  }
47511cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer
47611cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  if (valid) {
47711cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer    // Sorting artifacts requires that all references are valid as it uses them to determine order.
47811cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer    config.SortArtifacts();
47911cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  }
48011cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer
48111cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  if (!valid) {
482cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer    return {};
483cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  }
48411cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer
485cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  return {output_artifacts};
48674cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer}
48774cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
488cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmernamespace configuration {
489cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmernamespace handler {
490cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
491cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerbool ArtifactTagHandler(PostProcessingConfiguration* config, Element* root_element,
492cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                        IDiagnostics* diag) {
493cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer  ConfiguredArtifact artifact{};
494280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  for (const auto& attr : root_element->attributes) {
495280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    if (attr.name == "name") {
496280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      artifact.name = attr.value;
497280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    } else if (attr.name == "abi-group") {
498280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      artifact.abi_group = {attr.value};
499280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    } else if (attr.name == "screen-density-group") {
500280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      artifact.screen_density_group = {attr.value};
501280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    } else if (attr.name == "locale-group") {
502280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      artifact.locale_group = {attr.value};
50378c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    } else if (attr.name == "android-sdk") {
50478c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      artifact.android_sdk = {attr.value};
505280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    } else if (attr.name == "gl-texture-group") {
506280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      artifact.gl_texture_group = {attr.value};
507280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    } else if (attr.name == "device-feature-group") {
508280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      artifact.device_feature_group = {attr.value};
509280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    } else {
510280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      diag->Note(DiagMessage() << "Unknown artifact attribute: " << attr.name << " = "
511280be34fa6311de90dff6c595a147fd5a182223dShane Farmer                               << attr.value);
512280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    }
513280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  }
514280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  config->artifacts.push_back(artifact);
515280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  return true;
516280be34fa6311de90dff6c595a147fd5a182223dShane Farmer};
51774cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
518cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerbool ArtifactFormatTagHandler(PostProcessingConfiguration* config, Element* root_element,
519cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                              IDiagnostics* /* diag */) {
520280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  for (auto& node : root_element->children) {
521280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    xml::Text* t;
522280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
523280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      config->artifact_format = TrimWhitespace(t->text).to_string();
524280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      break;
525280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    }
526280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  }
527280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  return true;
528280be34fa6311de90dff6c595a147fd5a182223dShane Farmer};
52974cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
530cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerbool AbiGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
531cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                        IDiagnostics* diag) {
532280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  std::string label = GetLabel(root_element, diag);
533280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  if (label.empty()) {
534280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    return false;
535280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  }
53674cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
537280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  bool valid = true;
53811cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  OrderedEntry<Abi>& entry = config->abi_groups[label];
53911cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
54011cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  if (!order) {
54111cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer    valid = false;
54211cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  } else {
54311cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer    entry.order = order.value();
54411cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  }
54511cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  auto& group = entry.entry;
54674cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
54739e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer  // Special case for empty abi-group tag. Label will be used as the ABI.
54839e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer  if (root_element->GetChildElements().empty()) {
54939e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer    auto abi = kStringToAbiMap.find(label);
55039e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer    if (abi == kStringToAbiMap.end()) {
55139e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer      return false;
55239e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer    }
55339e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer    group.push_back(abi->second);
55411cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer    return valid;
55539e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer  }
55639e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer
557280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  for (auto* child : root_element->GetChildElements()) {
558280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    if (child->name != "abi") {
559280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      diag->Error(DiagMessage() << "Unexpected element in ABI group: " << child->name);
560280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      valid = false;
561280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    } else {
562280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      for (auto& node : child->children) {
563280be34fa6311de90dff6c595a147fd5a182223dShane Farmer        xml::Text* t;
564280be34fa6311de90dff6c595a147fd5a182223dShane Farmer        if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
56539e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer          auto abi = kStringToAbiMap.find(TrimWhitespace(t->text).to_string());
56639e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer          if (abi != kStringToAbiMap.end()) {
56739e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer            group.push_back(abi->second);
56839e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer          } else {
56939e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer            diag->Error(DiagMessage() << "Could not parse ABI value: " << t->text);
57039e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer            valid = false;
57139e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer          }
572280be34fa6311de90dff6c595a147fd5a182223dShane Farmer          break;
57374cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer        }
57474cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer      }
575280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    }
576280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  }
57774cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
578280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  return valid;
579280be34fa6311de90dff6c595a147fd5a182223dShane Farmer};
58074cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
581cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerbool ScreenDensityGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
582cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                                  IDiagnostics* diag) {
583280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  std::string label = GetLabel(root_element, diag);
584280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  if (label.empty()) {
585280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    return false;
586280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  }
58774cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
588280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  bool valid = true;
58911cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  OrderedEntry<ConfigDescription>& entry = config->screen_density_groups[label];
59011cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
59111cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  if (!order) {
59211cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer    valid = false;
59311cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  } else {
59411cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer    entry.order = order.value();
59511cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  }
59611cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  auto& group = entry.entry;
59774cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
59839e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer  // Special case for empty screen-density-group tag. Label will be used as the screen density.
59939e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer  if (root_element->GetChildElements().empty()) {
60039e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer    ConfigDescription config_descriptor;
60139e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer    bool parsed = ConfigDescription::Parse(label, &config_descriptor);
60239e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer    if (parsed &&
60339e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer        (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
60439e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer            android::ResTable_config::CONFIG_DENSITY)) {
60539e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer      // Copy the density with the minimum SDK version stripped out.
60639e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer      group.push_back(config_descriptor.CopyWithoutSdkVersion());
60739e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer    } else {
60839e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer      diag->Error(DiagMessage()
60939e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer                      << "Could not parse config descriptor for empty screen-density-group: "
61039e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer                      << label);
61139e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer      valid = false;
61239e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer    }
61339e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer
61439e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer    return valid;
61539e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer  }
61639e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer
617280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  for (auto* child : root_element->GetChildElements()) {
618280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    if (child->name != "screen-density") {
619280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
620280be34fa6311de90dff6c595a147fd5a182223dShane Farmer                                << child->name);
621280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      valid = false;
622280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    } else {
623280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      for (auto& node : child->children) {
624280be34fa6311de90dff6c595a147fd5a182223dShane Farmer        xml::Text* t;
625280be34fa6311de90dff6c595a147fd5a182223dShane Farmer        if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
626280be34fa6311de90dff6c595a147fd5a182223dShane Farmer          ConfigDescription config_descriptor;
627280be34fa6311de90dff6c595a147fd5a182223dShane Farmer          const android::StringPiece& text = TrimWhitespace(t->text);
6280a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer          bool parsed = ConfigDescription::Parse(text, &config_descriptor);
6290a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer          if (parsed &&
6300a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer              (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
6310a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer               android::ResTable_config::CONFIG_DENSITY)) {
632280be34fa6311de90dff6c595a147fd5a182223dShane Farmer            // Copy the density with the minimum SDK version stripped out.
633280be34fa6311de90dff6c595a147fd5a182223dShane Farmer            group.push_back(config_descriptor.CopyWithoutSdkVersion());
634280be34fa6311de90dff6c595a147fd5a182223dShane Farmer          } else {
635280be34fa6311de90dff6c595a147fd5a182223dShane Farmer            diag->Error(DiagMessage()
636280be34fa6311de90dff6c595a147fd5a182223dShane Farmer                        << "Could not parse config descriptor for screen-density: " << text);
637280be34fa6311de90dff6c595a147fd5a182223dShane Farmer            valid = false;
63874cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer          }
639280be34fa6311de90dff6c595a147fd5a182223dShane Farmer          break;
64074cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer        }
64174cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer      }
642280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    }
643280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  }
64474cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
645280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  return valid;
646280be34fa6311de90dff6c595a147fd5a182223dShane Farmer};
64774cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
648cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerbool LocaleGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
649cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                           IDiagnostics* diag) {
650280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  std::string label = GetLabel(root_element, diag);
651280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  if (label.empty()) {
652280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    return false;
653280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  }
65474cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
655280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  bool valid = true;
65611cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  OrderedEntry<ConfigDescription>& entry = config->locale_groups[label];
65711cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
65811cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  if (!order) {
65911cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer    valid = false;
66011cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  } else {
66111cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer    entry.order = order.value();
66211cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  }
66311cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  auto& group = entry.entry;
66474cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
66539e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer  // Special case to auto insert a locale for an empty group. Label will be used for locale.
66639e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer  if (root_element->GetChildElements().empty()) {
66739e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer    ConfigDescription config_descriptor;
66839e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer    bool parsed = ConfigDescription::Parse(label, &config_descriptor);
66939e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer    if (parsed &&
67039e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer        (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
67139e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer            android::ResTable_config::CONFIG_LOCALE)) {
67239e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer      // Copy the locale with the minimum SDK version stripped out.
67339e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer      group.push_back(config_descriptor.CopyWithoutSdkVersion());
67439e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer    } else {
67539e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer      diag->Error(DiagMessage()
67639e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer                      << "Could not parse config descriptor for empty screen-density-group: "
67739e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer                      << label);
67839e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer      valid = false;
67939e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer    }
68039e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer
68139e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer    return valid;
68239e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer  }
68339e474f4b4a975d8befa1e8f4cbedab5f47a43faShane Farmer
684280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  for (auto* child : root_element->GetChildElements()) {
685280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    if (child->name != "locale") {
686280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
687280be34fa6311de90dff6c595a147fd5a182223dShane Farmer                                << child->name);
688280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      valid = false;
689280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    } else {
6900a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer      for (auto& node : child->children) {
6910a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer        xml::Text* t;
6920a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer        if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
6930a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer          ConfigDescription config_descriptor;
6940a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer          const android::StringPiece& text = TrimWhitespace(t->text);
6950a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer          bool parsed = ConfigDescription::Parse(text, &config_descriptor);
6960a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer          if (parsed &&
6970a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer              (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
6980a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer               android::ResTable_config::CONFIG_LOCALE)) {
6990a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer            // Copy the locale with the minimum SDK version stripped out.
7000a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer            group.push_back(config_descriptor.CopyWithoutSdkVersion());
7010a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer          } else {
7020a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer            diag->Error(DiagMessage()
7030a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer                        << "Could not parse config descriptor for screen-density: " << text);
7040a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer            valid = false;
7050a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer          }
7060a5b201156f1dd01a7da7b7674798ed57cfafc5aShane Farmer          break;
70774cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer        }
70874cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer      }
709280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    }
710280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  }
71174cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
712280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  return valid;
713280be34fa6311de90dff6c595a147fd5a182223dShane Farmer};
71474cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
71578c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmerbool AndroidSdkTagHandler(PostProcessingConfiguration* config, Element* root_element,
71678c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer                          IDiagnostics* diag) {
71778c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  AndroidSdk entry = AndroidSdk::ForMinSdk(-1);
718280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  bool valid = true;
71978c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  for (const auto& attr : root_element->attributes) {
72078c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    bool valid_attr = false;
72178c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    if (attr.name == "label") {
72278c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      entry.label = attr.value;
72378c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      valid_attr = true;
72478c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    } else if (attr.name == "minSdkVersion") {
72578c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
72678c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      if (version) {
72778c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer        valid_attr = true;
72878c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer        entry.min_sdk_version = version.value();
72978c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      }
73078c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    } else if (attr.name == "targetSdkVersion") {
73178c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
73278c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      if (version) {
73378c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer        valid_attr = true;
73478c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer        entry.target_sdk_version = version;
73578c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      }
73678c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    } else if (attr.name == "maxSdkVersion") {
73778c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
73878c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      if (version) {
73978c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer        valid_attr = true;
74078c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer        entry.max_sdk_version = version;
74178c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      }
74278c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    }
743280be34fa6311de90dff6c595a147fd5a182223dShane Farmer
74478c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    if (!valid_attr) {
74578c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      diag->Error(DiagMessage() << "Invalid attribute: " << attr.name << " = " << attr.value);
746280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      valid = false;
74778c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    }
74878c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  }
74974cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
75078c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  if (entry.min_sdk_version == -1) {
75178c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    diag->Error(DiagMessage() << "android-sdk is missing minSdkVersion attribute");
75278c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    valid = false;
75378c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  }
75474cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
75578c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  // TODO: Fill in the manifest details when they are finalised.
75678c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  for (auto node : root_element->GetChildElements()) {
75778c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer    if (node->name == "manifest") {
75878c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      if (entry.manifest) {
75978c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer        diag->Warn(DiagMessage() << "Found multiple manifest tags. Ignoring duplicates.");
76078c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer        continue;
761efe45392c300f922e8328281a0aab8260c1d171dShane Farmer      }
76278c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer      entry.manifest = {AndroidManifest()};
763280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    }
764280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  }
765280be34fa6311de90dff6c595a147fd5a182223dShane Farmer
76678c43d7bee2a476d6edffe0a4368eb6fcef38c31Shane Farmer  config->android_sdks[entry.label] = entry;
767280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  return valid;
768280be34fa6311de90dff6c595a147fd5a182223dShane Farmer};
76974cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
770cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerbool GlTextureGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
771cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                              IDiagnostics* diag) {
772280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  std::string label = GetLabel(root_element, diag);
773280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  if (label.empty()) {
774280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    return false;
775280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  }
77674cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
777280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  bool valid = true;
77811cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  OrderedEntry<GlTexture>& entry = config->gl_texture_groups[label];
77911cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
78011cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  if (!order) {
78111cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer    valid = false;
78211cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  } else {
78311cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer    entry.order = order.value();
78411cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  }
78511cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  auto& group = entry.entry;
78674cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
787280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  GlTexture result;
788280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  for (auto* child : root_element->GetChildElements()) {
789280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    if (child->name != "gl-texture") {
790280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      diag->Error(DiagMessage() << "Unexpected element in GL texture group: " << child->name);
791280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      valid = false;
792280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    } else {
793280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      for (const auto& attr : child->attributes) {
794280be34fa6311de90dff6c595a147fd5a182223dShane Farmer        if (attr.name == "name") {
795280be34fa6311de90dff6c595a147fd5a182223dShane Farmer          result.name = attr.value;
796280be34fa6311de90dff6c595a147fd5a182223dShane Farmer          break;
797280be34fa6311de90dff6c595a147fd5a182223dShane Farmer        }
798280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      }
79974cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
800280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      for (auto* element : child->GetChildElements()) {
801280be34fa6311de90dff6c595a147fd5a182223dShane Farmer        if (element->name != "texture-path") {
802280be34fa6311de90dff6c595a147fd5a182223dShane Farmer          diag->Error(DiagMessage() << "Unexpected element in gl-texture element: " << child->name);
803280be34fa6311de90dff6c595a147fd5a182223dShane Farmer          valid = false;
804280be34fa6311de90dff6c595a147fd5a182223dShane Farmer          continue;
805280be34fa6311de90dff6c595a147fd5a182223dShane Farmer        }
806280be34fa6311de90dff6c595a147fd5a182223dShane Farmer        for (auto& node : element->children) {
807280be34fa6311de90dff6c595a147fd5a182223dShane Farmer          xml::Text* t;
808280be34fa6311de90dff6c595a147fd5a182223dShane Farmer          if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
809280be34fa6311de90dff6c595a147fd5a182223dShane Farmer            result.texture_paths.push_back(TrimWhitespace(t->text).to_string());
81074cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer          }
81174cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer        }
81274cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer      }
813280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    }
814280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    group.push_back(result);
815280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  }
81674cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
817280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  return valid;
818280be34fa6311de90dff6c595a147fd5a182223dShane Farmer};
81974cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
820cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmerbool DeviceFeatureGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
821cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer                                  IDiagnostics* diag) {
822280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  std::string label = GetLabel(root_element, diag);
823280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  if (label.empty()) {
824280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    return false;
825280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  }
82674cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
827280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  bool valid = true;
82811cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  OrderedEntry<DeviceFeature>& entry = config->device_feature_groups[label];
82911cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
83011cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  if (!order) {
83111cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer    valid = false;
83211cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  } else {
83311cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer    entry.order = order.value();
83411cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  }
83511cdc1cf7ab2e915611e2bb7264bd312012a9b64Shane Farmer  auto& group = entry.entry;
83674cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
837280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  for (auto* child : root_element->GetChildElements()) {
838280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    if (child->name != "supports-feature") {
839280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      diag->Error(DiagMessage() << "Unexpected root_element in device feature group: "
840280be34fa6311de90dff6c595a147fd5a182223dShane Farmer                                << child->name);
841280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      valid = false;
842280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    } else {
843280be34fa6311de90dff6c595a147fd5a182223dShane Farmer      for (auto& node : child->children) {
844280be34fa6311de90dff6c595a147fd5a182223dShane Farmer        xml::Text* t;
845280be34fa6311de90dff6c595a147fd5a182223dShane Farmer        if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
846280be34fa6311de90dff6c595a147fd5a182223dShane Farmer          group.push_back(TrimWhitespace(t->text).to_string());
847280be34fa6311de90dff6c595a147fd5a182223dShane Farmer          break;
84874cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer        }
84974cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer      }
850280be34fa6311de90dff6c595a147fd5a182223dShane Farmer    }
851280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  }
85274cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
853280be34fa6311de90dff6c595a147fd5a182223dShane Farmer  return valid;
854280be34fa6311de90dff6c595a147fd5a182223dShane Farmer};
85574cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer
856cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer}  // namespace handler
857cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer}  // namespace configuration
858cb6c3f9b880160c35785b1780b282fdf92952b90Shane Farmer
85974cdea34cd6dce8bf9b4adaa2cd7cb02330120d5Shane Farmer}  // namespace aapt
860