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