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