blob: 1b032532577855dbb806a1a6f07e78c16e74eefd [file] [log] [blame]
Shane Farmer74cdea32017-05-12 16:22:36 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "configuration/ConfigurationParser.h"
18
19#include <algorithm>
20#include <functional>
Shane Farmer57669432017-06-19 12:52:04 -070021#include <map>
Shane Farmer74cdea32017-05-12 16:22:36 -070022#include <memory>
Shane Farmer11cdc1c2018-01-31 16:43:24 -080023#include <string>
Shane Farmer74cdea32017-05-12 16:22:36 -070024#include <utility>
25
Jeremy Meyer56f36e82022-05-20 20:35:42 +000026#include "ResourceUtils.h"
Adam Lesinskiefeb7af2017-08-02 14:57:43 -070027#include "android-base/file.h"
28#include "android-base/logging.h"
Mårten Kongstad24c9aa62018-06-20 08:46:41 +020029#include "androidfw/ConfigDescription.h"
Jeremy Meyer56f36e82022-05-20 20:35:42 +000030#include "androidfw/IDiagnostics.h"
Shane Farmercb6c3f92017-11-27 13:19:36 -080031#include "configuration/ConfigurationParser.internal.h"
Shane Farmerb1027272017-06-14 09:10:28 -070032#include "io/File.h"
33#include "io/FileSystem.h"
Adam Lesinski00451162017-10-03 07:44:08 -070034#include "io/StringStream.h"
Shane Farmer9ecc0752017-08-24 15:55:36 -070035#include "util/Files.h"
Shane Farmer74cdea32017-05-12 16:22:36 -070036#include "util/Util.h"
37#include "xml/XmlActionExecutor.h"
38#include "xml/XmlDom.h"
39#include "xml/XmlUtil.h"
40
Mårten Kongstad24c9aa62018-06-20 08:46:41 +020041using ::android::ConfigDescription;
42
Shane Farmer74cdea32017-05-12 16:22:36 -070043namespace aapt {
44
45namespace {
46
47using ::aapt::configuration::Abi;
48using ::aapt::configuration::AndroidManifest;
49using ::aapt::configuration::AndroidSdk;
Shane Farmercb6c3f92017-11-27 13:19:36 -080050using ::aapt::configuration::ConfiguredArtifact;
51using ::aapt::configuration::DeviceFeature;
52using ::aapt::configuration::Entry;
Shane Farmer78c43d72017-12-04 09:08:38 -080053using ::aapt::configuration::ExtractConfiguration;
Shane Farmer74cdea32017-05-12 16:22:36 -070054using ::aapt::configuration::GlTexture;
55using ::aapt::configuration::Group;
56using ::aapt::configuration::Locale;
Shane Farmer78c43d72017-12-04 09:08:38 -080057using ::aapt::configuration::OrderedEntry;
Shane Farmercb6c3f92017-11-27 13:19:36 -080058using ::aapt::configuration::OutputArtifact;
59using ::aapt::configuration::PostProcessingConfiguration;
60using ::aapt::configuration::handler::AbiGroupTagHandler;
Shane Farmer78c43d72017-12-04 09:08:38 -080061using ::aapt::configuration::handler::AndroidSdkTagHandler;
Shane Farmercb6c3f92017-11-27 13:19:36 -080062using ::aapt::configuration::handler::ArtifactFormatTagHandler;
63using ::aapt::configuration::handler::ArtifactTagHandler;
64using ::aapt::configuration::handler::DeviceFeatureGroupTagHandler;
65using ::aapt::configuration::handler::GlTextureGroupTagHandler;
66using ::aapt::configuration::handler::LocaleGroupTagHandler;
67using ::aapt::configuration::handler::ScreenDensityGroupTagHandler;
Shane Farmerb1027272017-06-14 09:10:28 -070068using ::aapt::io::IFile;
69using ::aapt::io::RegularFile;
Adam Lesinskiefeb7af2017-08-02 14:57:43 -070070using ::aapt::io::StringInputStream;
Shane Farmer74cdea32017-05-12 16:22:36 -070071using ::aapt::util::TrimWhitespace;
72using ::aapt::xml::Element;
Shane Farmer74cdea32017-05-12 16:22:36 -070073using ::aapt::xml::NodeCast;
74using ::aapt::xml::XmlActionExecutor;
75using ::aapt::xml::XmlActionExecutorPolicy;
76using ::aapt::xml::XmlNodeAction;
Shane Farmer0a5b2012017-06-22 12:24:12 -070077using ::android::StringPiece;
Shane Farmercb6c3f92017-11-27 13:19:36 -080078using ::android::base::ReadFileToString;
Shane Farmer74cdea32017-05-12 16:22:36 -070079
Shane Farmercb6c3f92017-11-27 13:19:36 -080080const std::unordered_map<StringPiece, Abi> kStringToAbiMap = {
Shane Farmer57669432017-06-19 12:52:04 -070081 {"armeabi", Abi::kArmeV6}, {"armeabi-v7a", Abi::kArmV7a}, {"arm64-v8a", Abi::kArm64V8a},
82 {"x86", Abi::kX86}, {"x86_64", Abi::kX86_64}, {"mips", Abi::kMips},
83 {"mips64", Abi::kMips64}, {"universal", Abi::kUniversal},
84};
Shane Farmercb6c3f92017-11-27 13:19:36 -080085const std::array<StringPiece, 8> kAbiToStringMap = {
86 {"armeabi", "armeabi-v7a", "arm64-v8a", "x86", "x86_64", "mips", "mips64", "universal"}};
Shane Farmer74cdea32017-05-12 16:22:36 -070087
88constexpr const char* kAaptXmlNs = "http://schemas.android.com/tools/aapt";
89
Jeremy Meyer56f36e82022-05-20 20:35:42 +000090android::NoOpDiagnostics noop_;
Shane Farmer74cdea32017-05-12 16:22:36 -070091
Shane Farmer11cdc1c2018-01-31 16:43:24 -080092/** Returns the value of the label attribute for a given element. */
Jeremy Meyer56f36e82022-05-20 20:35:42 +000093std::string GetLabel(const Element* element, android::IDiagnostics* diag) {
Shane Farmer74cdea32017-05-12 16:22:36 -070094 std::string label;
95 for (const auto& attr : element->attributes) {
96 if (attr.name == "label") {
97 label = attr.value;
98 break;
99 }
100 }
101
102 if (label.empty()) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000103 diag->Error(android::DiagMessage() << "No label found for element " << element->name);
Shane Farmer74cdea32017-05-12 16:22:36 -0700104 }
105 return label;
106}
107
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800108/** Returns the value of the version-code-order attribute for a given element. */
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000109std::optional<int32_t> GetVersionCodeOrder(const Element* element, android::IDiagnostics* diag) {
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800110 const xml::Attribute* version = element->FindAttribute("", "version-code-order");
111 if (version == nullptr) {
112 std::string label = GetLabel(element, diag);
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000113 diag->Error(android::DiagMessage() << "No version-code-order found for element '"
114 << element->name << "' with label '" << label << "'");
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800115 return {};
116 }
117 return std::stoi(version->value);
118}
119
Shane Farmer74cdea32017-05-12 16:22:36 -0700120/** XML node visitor that removes all of the namespace URIs from the node and all children. */
121class NamespaceVisitor : public xml::Visitor {
122 public:
123 void Visit(xml::Element* node) override {
124 node->namespace_uri.clear();
125 VisitChildren(node);
126 }
127};
128
Shane Farmercb6c3f92017-11-27 13:19:36 -0800129/** Copies the values referenced in a configuration group to the target list. */
130template <typename T>
Ryan Mitchell4382e442021-07-14 12:53:01 -0700131bool CopyXmlReferences(const std::optional<std::string>& name, const Group<T>& groups,
Shane Farmercb6c3f92017-11-27 13:19:36 -0800132 std::vector<T>* target) {
133 // If there was no item configured, there is nothing to do and no error.
134 if (!name) {
135 return true;
136 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700137
Shane Farmercb6c3f92017-11-27 13:19:36 -0800138 // If the group could not be found, then something is wrong.
139 auto group = groups.find(name.value());
140 if (group == groups.end()) {
141 return false;
142 }
Shane Farmerb1027272017-06-14 09:10:28 -0700143
Shane Farmer78c43d72017-12-04 09:08:38 -0800144 for (const T& item : group->second.entry) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800145 target->push_back(item);
146 }
147 return true;
Shane Farmer57669432017-06-19 12:52:04 -0700148}
149
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700150/**
151 * Attempts to replace the placeholder in the name string with the provided value. Returns true on
152 * success, or false if the either the placeholder is not found in the name, or the value is not
153 * present and the placeholder was.
154 */
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700155bool ReplacePlaceholder(StringPiece placeholder, const std::optional<StringPiece>& value,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000156 std::string* name, android::IDiagnostics* diag) {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700157 size_t offset = name->find(placeholder.data());
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700158 bool found = (offset != std::string::npos);
159
160 // Make sure the placeholder was present if the desired value is present.
161 if (!found) {
162 if (value) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000163 diag->Error(android::DiagMessage() << "Missing placeholder for artifact: " << placeholder);
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700164 return false;
165 }
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700166 return true;
167 }
168
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700169 DCHECK(found) << "Missing return path for placeholder not found";
170
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700171 // Make sure the placeholder was not present if the desired value was not present.
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700172 if (!value) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000173 diag->Error(android::DiagMessage()
174 << "Placeholder present but no value for artifact: " << placeholder);
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700175 return false;
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700176 }
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700177
Shane Farmer0a5b2012017-06-22 12:24:12 -0700178 name->replace(offset, placeholder.length(), value.value().data());
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700179
180 // Make sure there was only one instance of the placeholder.
Shane Farmer0a5b2012017-06-22 12:24:12 -0700181 if (name->find(placeholder.data()) != std::string::npos) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000182 diag->Error(android::DiagMessage() << "Placeholder present multiple times: " << placeholder);
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700183 return false;
184 }
185 return true;
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700186}
187
Shane Farmer9ecc0752017-08-24 15:55:36 -0700188/**
Shane Farmercb6c3f92017-11-27 13:19:36 -0800189 * An ActionHandler for processing XML elements in the XmlActionExecutor. Returns true if the
190 * element was successfully processed, otherwise returns false.
191 */
192using ActionHandler = std::function<bool(configuration::PostProcessingConfiguration* config,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000193 xml::Element* element, android::IDiagnostics* diag)>;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800194
195/** Binds an ActionHandler to the current configuration being populated. */
196xml::XmlNodeAction::ActionFuncWithDiag Bind(configuration::PostProcessingConfiguration* config,
197 const ActionHandler& handler) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000198 return [config, handler](xml::Element* root_element, android::SourcePathDiagnostics* diag) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800199 return handler(config, root_element, diag);
200 };
201}
202
Shane Farmercb6c3f92017-11-27 13:19:36 -0800203/** Converts a ConfiguredArtifact into an OutputArtifact. */
Ryan Mitchell4382e442021-07-14 12:53:01 -0700204std::optional<OutputArtifact> ToOutputArtifact(const ConfiguredArtifact& artifact,
205 const std::string& apk_name,
206 const PostProcessingConfiguration& config,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000207 android::IDiagnostics* diag) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800208 if (!artifact.name && !config.artifact_format) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000209 diag->Error(android::DiagMessage()
210 << "Artifact does not have a name and no global name template defined");
Shane Farmercb6c3f92017-11-27 13:19:36 -0800211 return {};
212 }
213
Ryan Mitchell4382e442021-07-14 12:53:01 -0700214 std::optional<std::string> artifact_name =
Shane Farmercb6c3f92017-11-27 13:19:36 -0800215 (artifact.name) ? artifact.Name(apk_name, diag)
216 : artifact.ToArtifactName(config.artifact_format.value(), apk_name, diag);
217
218 if (!artifact_name) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000219 diag->Error(android::DiagMessage() << "Could not determine split APK artifact name");
Shane Farmercb6c3f92017-11-27 13:19:36 -0800220 return {};
221 }
222
223 OutputArtifact output_artifact;
224 output_artifact.name = artifact_name.value();
225
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000226 android::SourcePathDiagnostics src_diag{{output_artifact.name}, diag};
Shane Farmercb6c3f92017-11-27 13:19:36 -0800227 bool has_errors = false;
228
229 if (!CopyXmlReferences(artifact.abi_group, config.abi_groups, &output_artifact.abis)) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000230 src_diag.Error(android::DiagMessage()
231 << "Could not lookup required ABIs: " << artifact.abi_group.value());
Shane Farmercb6c3f92017-11-27 13:19:36 -0800232 has_errors = true;
233 }
234
235 if (!CopyXmlReferences(artifact.locale_group, config.locale_groups, &output_artifact.locales)) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000236 src_diag.Error(android::DiagMessage()
237 << "Could not lookup required locales: " << artifact.locale_group.value());
Shane Farmercb6c3f92017-11-27 13:19:36 -0800238 has_errors = true;
239 }
240
241 if (!CopyXmlReferences(artifact.screen_density_group, config.screen_density_groups,
242 &output_artifact.screen_densities)) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000243 src_diag.Error(android::DiagMessage() << "Could not lookup required screen densities: "
244 << artifact.screen_density_group.value());
Shane Farmercb6c3f92017-11-27 13:19:36 -0800245 has_errors = true;
246 }
247
248 if (!CopyXmlReferences(artifact.device_feature_group, config.device_feature_groups,
249 &output_artifact.features)) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000250 src_diag.Error(android::DiagMessage() << "Could not lookup required device features: "
251 << artifact.device_feature_group.value());
Shane Farmercb6c3f92017-11-27 13:19:36 -0800252 has_errors = true;
253 }
254
255 if (!CopyXmlReferences(artifact.gl_texture_group, config.gl_texture_groups,
256 &output_artifact.textures)) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000257 src_diag.Error(android::DiagMessage() << "Could not lookup required OpenGL texture formats: "
258 << artifact.gl_texture_group.value());
Shane Farmercb6c3f92017-11-27 13:19:36 -0800259 has_errors = true;
260 }
261
Shane Farmer78c43d72017-12-04 09:08:38 -0800262 if (artifact.android_sdk) {
263 auto entry = config.android_sdks.find(artifact.android_sdk.value());
264 if (entry == config.android_sdks.end()) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000265 src_diag.Error(android::DiagMessage() << "Could not lookup required Android SDK version: "
266 << artifact.android_sdk.value());
Shane Farmercb6c3f92017-11-27 13:19:36 -0800267 has_errors = true;
268 } else {
269 output_artifact.android_sdk = {entry->second};
270 }
271 }
272
273 if (has_errors) {
274 return {};
275 }
276 return {output_artifact};
277}
278
279} // namespace
280
281namespace configuration {
282
Shane Farmer78c43d72017-12-04 09:08:38 -0800283/** Returns the binary reprasentation of the XML configuration. */
Ryan Mitchell4382e442021-07-14 12:53:01 -0700284std::optional<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
285 const std::string& config_path,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000286 android::IDiagnostics* diag) {
Shane Farmer78c43d72017-12-04 09:08:38 -0800287 StringInputStream in(contents);
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000288 std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, diag, android::Source(config_path));
Shane Farmer78c43d72017-12-04 09:08:38 -0800289 if (!doc) {
290 return {};
291 }
292
293 // Strip any namespaces from the XML as the XmlActionExecutor ignores anything with a namespace.
294 Element* root = doc->root.get();
295 if (root == nullptr) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000296 diag->Error(android::DiagMessage() << "Could not find the root element in the XML document");
Shane Farmer78c43d72017-12-04 09:08:38 -0800297 return {};
298 }
299
300 std::string& xml_ns = root->namespace_uri;
301 if (!xml_ns.empty()) {
302 if (xml_ns != kAaptXmlNs) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000303 diag->Error(android::DiagMessage() << "Unknown namespace found on root element: " << xml_ns);
Shane Farmer78c43d72017-12-04 09:08:38 -0800304 return {};
305 }
306
307 xml_ns.clear();
308 NamespaceVisitor visitor;
309 root->Accept(&visitor);
310 }
311
312 XmlActionExecutor executor;
313 XmlNodeAction& root_action = executor["post-process"];
314 XmlNodeAction& artifacts_action = root_action["artifacts"];
315
316 PostProcessingConfiguration config;
317
318 // Parse the artifact elements.
319 artifacts_action["artifact"].Action(Bind(&config, ArtifactTagHandler));
320 artifacts_action["artifact-format"].Action(Bind(&config, ArtifactFormatTagHandler));
321
322 // Parse the different configuration groups.
323 root_action["abi-groups"]["abi-group"].Action(Bind(&config, AbiGroupTagHandler));
324 root_action["screen-density-groups"]["screen-density-group"].Action(
325 Bind(&config, ScreenDensityGroupTagHandler));
326 root_action["locale-groups"]["locale-group"].Action(Bind(&config, LocaleGroupTagHandler));
327 root_action["android-sdks"]["android-sdk"].Action(Bind(&config, AndroidSdkTagHandler));
328 root_action["gl-texture-groups"]["gl-texture-group"].Action(
329 Bind(&config, GlTextureGroupTagHandler));
330 root_action["device-feature-groups"]["device-feature-group"].Action(
331 Bind(&config, DeviceFeatureGroupTagHandler));
332
333 if (!executor.Execute(XmlActionExecutorPolicy::kNone, diag, doc.get())) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000334 diag->Error(android::DiagMessage() << "Could not process XML document");
Shane Farmer78c43d72017-12-04 09:08:38 -0800335 return {};
336 }
337
338 return {config};
339}
340
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700341StringPiece AbiToString(Abi abi) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800342 return kAbiToStringMap.at(static_cast<size_t>(abi));
343}
344
345/**
Shane Farmer9ecc0752017-08-24 15:55:36 -0700346 * Returns the common artifact base name from a template string.
347 */
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700348std::optional<std::string> ToBaseName(std::string result, StringPiece apk_name,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000349 android::IDiagnostics* diag) {
Shane Farmer9ecc0752017-08-24 15:55:36 -0700350 const StringPiece ext = file::GetExtension(apk_name);
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700351 size_t end_index = apk_name.rfind(ext);
Shane Farmer9ecc0752017-08-24 15:55:36 -0700352 const std::string base_name =
353 (end_index != std::string::npos) ? std::string{apk_name.begin(), end_index} : "";
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700354
Shane Farmer9ecc0752017-08-24 15:55:36 -0700355 // Base name is optional.
356 if (result.find("${basename}") != std::string::npos) {
Ryan Mitchell4382e442021-07-14 12:53:01 -0700357 auto maybe_base_name = base_name.empty() ? std::nullopt
358 : std::optional<StringPiece>{base_name};
Shane Farmer9ecc0752017-08-24 15:55:36 -0700359 if (!ReplacePlaceholder("${basename}", maybe_base_name, &result, diag)) {
360 return {};
361 }
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700362 }
363
Shane Farmer0a5b2012017-06-22 12:24:12 -0700364 // Extension is optional.
365 if (result.find("${ext}") != std::string::npos) {
Shane Farmer9ecc0752017-08-24 15:55:36 -0700366 // Make sure we disregard the '.' in the extension when replacing the placeholder.
367 if (!ReplacePlaceholder("${ext}", {ext.substr(1)}, &result, diag)) {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700368 return {};
369 }
Shane Farmer9ecc0752017-08-24 15:55:36 -0700370 } else {
371 // If no extension is specified, and the name template does not end in the current extension,
372 // add the existing extension.
373 if (!util::EndsWith(result, ext)) {
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700374 result.append(ext);
Shane Farmer9ecc0752017-08-24 15:55:36 -0700375 }
Shane Farmer0a5b2012017-06-22 12:24:12 -0700376 }
377
Shane Farmer9ecc0752017-08-24 15:55:36 -0700378 return result;
379}
380
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700381std::optional<std::string> ConfiguredArtifact::ToArtifactName(StringPiece format,
382 StringPiece apk_name,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000383 android::IDiagnostics* diag) const {
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700384 std::optional<std::string> base = ToBaseName(std::string(format), apk_name, diag);
Shane Farmer9ecc0752017-08-24 15:55:36 -0700385 if (!base) {
386 return {};
387 }
388 std::string result = std::move(base.value());
389
Shane Farmer0a5b2012017-06-22 12:24:12 -0700390 if (!ReplacePlaceholder("${abi}", abi_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700391 return {};
392 }
393
Shane Farmer0a5b2012017-06-22 12:24:12 -0700394 if (!ReplacePlaceholder("${density}", screen_density_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700395 return {};
396 }
397
Shane Farmer0a5b2012017-06-22 12:24:12 -0700398 if (!ReplacePlaceholder("${locale}", locale_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700399 return {};
400 }
401
Shane Farmer78c43d72017-12-04 09:08:38 -0800402 if (!ReplacePlaceholder("${sdk}", android_sdk, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700403 return {};
404 }
405
Shane Farmer0a5b2012017-06-22 12:24:12 -0700406 if (!ReplacePlaceholder("${feature}", device_feature_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700407 return {};
408 }
409
Shane Farmer0a5b2012017-06-22 12:24:12 -0700410 if (!ReplacePlaceholder("${gl}", gl_texture_group, &result, diag)) {
411 return {};
412 }
413
414 return result;
415}
416
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700417std::optional<std::string> ConfiguredArtifact::Name(StringPiece apk_name,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000418 android::IDiagnostics* diag) const {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700419 if (!name) {
420 return {};
421 }
422
Shane Farmer9ecc0752017-08-24 15:55:36 -0700423 return ToBaseName(name.value(), apk_name, diag);
424}
Shane Farmer0a5b2012017-06-22 12:24:12 -0700425
Shane Farmer57669432017-06-19 12:52:04 -0700426} // namespace configuration
Shane Farmerb1027272017-06-14 09:10:28 -0700427
428/** Returns a ConfigurationParser for the file located at the provided path. */
Ryan Mitchell4382e442021-07-14 12:53:01 -0700429std::optional<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) {
Shane Farmerb1027272017-06-14 09:10:28 -0700430 std::string contents;
431 if (!ReadFileToString(path, &contents, true)) {
432 return {};
433 }
Shane Farmer78c43d72017-12-04 09:08:38 -0800434 return ConfigurationParser(contents, path);
Shane Farmerb1027272017-06-14 09:10:28 -0700435}
436
Shane Farmer78c43d72017-12-04 09:08:38 -0800437ConfigurationParser::ConfigurationParser(std::string contents, const std::string& config_path)
438 : contents_(std::move(contents)), config_path_(config_path), diag_(&noop_) {
Shane Farmer74cdea32017-05-12 16:22:36 -0700439}
440
Ryan Mitchell4382e442021-07-14 12:53:01 -0700441std::optional<std::vector<OutputArtifact>> ConfigurationParser::Parse(
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700442 android::StringPiece apk_path) {
Ryan Mitchell4382e442021-07-14 12:53:01 -0700443 std::optional<PostProcessingConfiguration> maybe_config =
Shane Farmer78c43d72017-12-04 09:08:38 -0800444 ExtractConfiguration(contents_, config_path_, diag_);
Shane Farmercb6c3f92017-11-27 13:19:36 -0800445 if (!maybe_config) {
Shane Farmer74cdea32017-05-12 16:22:36 -0700446 return {};
447 }
Shane Farmercb6c3f92017-11-27 13:19:36 -0800448
449 // Convert from a parsed configuration to a list of artifacts for processing.
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700450 const std::string apk_name(file::GetFilename(apk_path));
Shane Farmercb6c3f92017-11-27 13:19:36 -0800451 std::vector<OutputArtifact> output_artifacts;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800452
Shane Farmer78c43d72017-12-04 09:08:38 -0800453 PostProcessingConfiguration& config = maybe_config.value();
Shane Farmer78c43d72017-12-04 09:08:38 -0800454
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800455 bool valid = true;
Shane Farmer78c43d72017-12-04 09:08:38 -0800456 int version = 1;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800457
Shane Farmer78c43d72017-12-04 09:08:38 -0800458 for (const ConfiguredArtifact& artifact : config.artifacts) {
Ryan Mitchell4382e442021-07-14 12:53:01 -0700459 std::optional<OutputArtifact> output_artifact =
460 ToOutputArtifact(artifact, apk_name, config, diag_);
Shane Farmercb6c3f92017-11-27 13:19:36 -0800461 if (!output_artifact) {
462 // Defer return an error condition so that all errors are reported.
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800463 valid = false;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800464 } else {
Shane Farmer78c43d72017-12-04 09:08:38 -0800465 output_artifact.value().version = version++;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800466 output_artifacts.push_back(std::move(output_artifact.value()));
467 }
468 }
469
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800470 if (!config.ValidateVersionCodeOrdering(diag_)) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000471 diag_->Error(android::DiagMessage() << "could not validate post processing configuration");
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800472 valid = false;
473 }
474
475 if (valid) {
476 // Sorting artifacts requires that all references are valid as it uses them to determine order.
477 config.SortArtifacts();
478 }
479
480 if (!valid) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800481 return {};
482 }
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800483
Shane Farmercb6c3f92017-11-27 13:19:36 -0800484 return {output_artifacts};
Shane Farmer74cdea32017-05-12 16:22:36 -0700485}
486
Shane Farmercb6c3f92017-11-27 13:19:36 -0800487namespace configuration {
488namespace handler {
489
490bool ArtifactTagHandler(PostProcessingConfiguration* config, Element* root_element,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000491 android::IDiagnostics* diag) {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800492 ConfiguredArtifact artifact{};
Shane Farmer280be342017-06-21 15:20:15 -0700493 for (const auto& attr : root_element->attributes) {
494 if (attr.name == "name") {
495 artifact.name = attr.value;
496 } else if (attr.name == "abi-group") {
497 artifact.abi_group = {attr.value};
498 } else if (attr.name == "screen-density-group") {
499 artifact.screen_density_group = {attr.value};
500 } else if (attr.name == "locale-group") {
501 artifact.locale_group = {attr.value};
Shane Farmer78c43d72017-12-04 09:08:38 -0800502 } else if (attr.name == "android-sdk") {
503 artifact.android_sdk = {attr.value};
Shane Farmer280be342017-06-21 15:20:15 -0700504 } else if (attr.name == "gl-texture-group") {
505 artifact.gl_texture_group = {attr.value};
506 } else if (attr.name == "device-feature-group") {
507 artifact.device_feature_group = {attr.value};
508 } else {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000509 diag->Note(android::DiagMessage()
510 << "Unknown artifact attribute: " << attr.name << " = " << attr.value);
Shane Farmer280be342017-06-21 15:20:15 -0700511 }
512 }
513 config->artifacts.push_back(artifact);
514 return true;
515};
Shane Farmer74cdea32017-05-12 16:22:36 -0700516
Shane Farmercb6c3f92017-11-27 13:19:36 -0800517bool ArtifactFormatTagHandler(PostProcessingConfiguration* config, Element* root_element,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000518 android::IDiagnostics* /* diag */) {
Shane Farmer280be342017-06-21 15:20:15 -0700519 for (auto& node : root_element->children) {
520 xml::Text* t;
521 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700522 config->artifact_format.emplace(TrimWhitespace(t->text));
Shane Farmer280be342017-06-21 15:20:15 -0700523 break;
524 }
525 }
526 return true;
527};
528
Shane Farmercb6c3f92017-11-27 13:19:36 -0800529bool AbiGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000530 android::IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700531 std::string label = GetLabel(root_element, diag);
532 if (label.empty()) {
533 return false;
534 }
535
Shane Farmer280be342017-06-21 15:20:15 -0700536 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800537 OrderedEntry<Abi>& entry = config->abi_groups[label];
Ryan Mitchell4382e442021-07-14 12:53:01 -0700538 std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800539 if (!order) {
540 valid = false;
541 } else {
542 entry.order = order.value();
543 }
544 auto& group = entry.entry;
Shane Farmer280be342017-06-21 15:20:15 -0700545
Shane Farmer39e474f2017-12-18 14:44:11 -0800546 // Special case for empty abi-group tag. Label will be used as the ABI.
547 if (root_element->GetChildElements().empty()) {
548 auto abi = kStringToAbiMap.find(label);
549 if (abi == kStringToAbiMap.end()) {
550 return false;
551 }
552 group.push_back(abi->second);
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800553 return valid;
Shane Farmer39e474f2017-12-18 14:44:11 -0800554 }
555
Shane Farmer280be342017-06-21 15:20:15 -0700556 for (auto* child : root_element->GetChildElements()) {
557 if (child->name != "abi") {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000558 diag->Error(android::DiagMessage() << "Unexpected element in ABI group: " << child->name);
Shane Farmer280be342017-06-21 15:20:15 -0700559 valid = false;
560 } else {
561 for (auto& node : child->children) {
Shane Farmer74cdea32017-05-12 16:22:36 -0700562 xml::Text* t;
563 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700564 auto abi = kStringToAbiMap.find(TrimWhitespace(t->text));
Shane Farmer39e474f2017-12-18 14:44:11 -0800565 if (abi != kStringToAbiMap.end()) {
566 group.push_back(abi->second);
567 } else {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000568 diag->Error(android::DiagMessage() << "Could not parse ABI value: " << t->text);
Shane Farmer39e474f2017-12-18 14:44:11 -0800569 valid = false;
570 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700571 break;
572 }
573 }
Shane Farmer280be342017-06-21 15:20:15 -0700574 }
575 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700576
Shane Farmer280be342017-06-21 15:20:15 -0700577 return valid;
578};
Shane Farmer74cdea32017-05-12 16:22:36 -0700579
Shane Farmercb6c3f92017-11-27 13:19:36 -0800580bool ScreenDensityGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000581 android::IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700582 std::string label = GetLabel(root_element, diag);
583 if (label.empty()) {
584 return false;
585 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700586
Shane Farmer280be342017-06-21 15:20:15 -0700587 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800588 OrderedEntry<ConfigDescription>& entry = config->screen_density_groups[label];
Ryan Mitchell4382e442021-07-14 12:53:01 -0700589 std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800590 if (!order) {
591 valid = false;
592 } else {
593 entry.order = order.value();
594 }
595 auto& group = entry.entry;
Shane Farmer74cdea32017-05-12 16:22:36 -0700596
Shane Farmer39e474f2017-12-18 14:44:11 -0800597 // Special case for empty screen-density-group tag. Label will be used as the screen density.
598 if (root_element->GetChildElements().empty()) {
599 ConfigDescription config_descriptor;
600 bool parsed = ConfigDescription::Parse(label, &config_descriptor);
601 if (parsed &&
602 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
603 android::ResTable_config::CONFIG_DENSITY)) {
604 // Copy the density with the minimum SDK version stripped out.
605 group.push_back(config_descriptor.CopyWithoutSdkVersion());
606 } else {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000607 diag->Error(android::DiagMessage()
608 << "Could not parse config descriptor for empty screen-density-group: " << label);
Shane Farmer39e474f2017-12-18 14:44:11 -0800609 valid = false;
610 }
611
612 return valid;
613 }
614
Shane Farmer280be342017-06-21 15:20:15 -0700615 for (auto* child : root_element->GetChildElements()) {
616 if (child->name != "screen-density") {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000617 diag->Error(android::DiagMessage()
618 << "Unexpected root_element in screen density group: " << child->name);
Shane Farmer280be342017-06-21 15:20:15 -0700619 valid = false;
620 } else {
621 for (auto& node : child->children) {
622 xml::Text* t;
623 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
624 ConfigDescription config_descriptor;
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700625 android::StringPiece text = TrimWhitespace(t->text);
Shane Farmer0a5b2012017-06-22 12:24:12 -0700626 bool parsed = ConfigDescription::Parse(text, &config_descriptor);
627 if (parsed &&
628 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
629 android::ResTable_config::CONFIG_DENSITY)) {
Shane Farmer280be342017-06-21 15:20:15 -0700630 // Copy the density with the minimum SDK version stripped out.
631 group.push_back(config_descriptor.CopyWithoutSdkVersion());
632 } else {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000633 diag->Error(android::DiagMessage()
Shane Farmer280be342017-06-21 15:20:15 -0700634 << "Could not parse config descriptor for screen-density: " << text);
635 valid = false;
Shane Farmer74cdea32017-05-12 16:22:36 -0700636 }
Shane Farmer280be342017-06-21 15:20:15 -0700637 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700638 }
639 }
Shane Farmer280be342017-06-21 15:20:15 -0700640 }
641 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700642
Shane Farmer280be342017-06-21 15:20:15 -0700643 return valid;
644};
Shane Farmer74cdea32017-05-12 16:22:36 -0700645
Shane Farmercb6c3f92017-11-27 13:19:36 -0800646bool LocaleGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000647 android::IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700648 std::string label = GetLabel(root_element, diag);
649 if (label.empty()) {
650 return false;
651 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700652
Shane Farmer280be342017-06-21 15:20:15 -0700653 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800654 OrderedEntry<ConfigDescription>& entry = config->locale_groups[label];
Ryan Mitchell4382e442021-07-14 12:53:01 -0700655 std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800656 if (!order) {
657 valid = false;
658 } else {
659 entry.order = order.value();
660 }
661 auto& group = entry.entry;
Shane Farmer74cdea32017-05-12 16:22:36 -0700662
Shane Farmer39e474f2017-12-18 14:44:11 -0800663 // Special case to auto insert a locale for an empty group. Label will be used for locale.
664 if (root_element->GetChildElements().empty()) {
665 ConfigDescription config_descriptor;
666 bool parsed = ConfigDescription::Parse(label, &config_descriptor);
667 if (parsed &&
668 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
669 android::ResTable_config::CONFIG_LOCALE)) {
670 // Copy the locale with the minimum SDK version stripped out.
671 group.push_back(config_descriptor.CopyWithoutSdkVersion());
672 } else {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000673 diag->Error(android::DiagMessage()
674 << "Could not parse config descriptor for empty screen-density-group: " << label);
Shane Farmer39e474f2017-12-18 14:44:11 -0800675 valid = false;
676 }
677
678 return valid;
679 }
680
Shane Farmer280be342017-06-21 15:20:15 -0700681 for (auto* child : root_element->GetChildElements()) {
682 if (child->name != "locale") {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000683 diag->Error(android::DiagMessage()
684 << "Unexpected root_element in screen density group: " << child->name);
Shane Farmer280be342017-06-21 15:20:15 -0700685 valid = false;
686 } else {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700687 for (auto& node : child->children) {
688 xml::Text* t;
689 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
690 ConfigDescription config_descriptor;
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700691 android::StringPiece text = TrimWhitespace(t->text);
Shane Farmer0a5b2012017-06-22 12:24:12 -0700692 bool parsed = ConfigDescription::Parse(text, &config_descriptor);
693 if (parsed &&
694 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
695 android::ResTable_config::CONFIG_LOCALE)) {
696 // Copy the locale with the minimum SDK version stripped out.
697 group.push_back(config_descriptor.CopyWithoutSdkVersion());
698 } else {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000699 diag->Error(android::DiagMessage()
Shane Farmer0a5b2012017-06-22 12:24:12 -0700700 << "Could not parse config descriptor for screen-density: " << text);
701 valid = false;
702 }
703 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700704 }
705 }
Shane Farmer280be342017-06-21 15:20:15 -0700706 }
707 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700708
Shane Farmer280be342017-06-21 15:20:15 -0700709 return valid;
710};
Shane Farmer74cdea32017-05-12 16:22:36 -0700711
Shane Farmer78c43d72017-12-04 09:08:38 -0800712bool AndroidSdkTagHandler(PostProcessingConfiguration* config, Element* root_element,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000713 android::IDiagnostics* diag) {
Shane Farmer78c43d72017-12-04 09:08:38 -0800714 AndroidSdk entry = AndroidSdk::ForMinSdk(-1);
Shane Farmer280be342017-06-21 15:20:15 -0700715 bool valid = true;
Shane Farmer78c43d72017-12-04 09:08:38 -0800716 for (const auto& attr : root_element->attributes) {
717 bool valid_attr = false;
718 if (attr.name == "label") {
719 entry.label = attr.value;
720 valid_attr = true;
721 } else if (attr.name == "minSdkVersion") {
Ryan Mitchell4382e442021-07-14 12:53:01 -0700722 std::optional<int> version = ResourceUtils::ParseSdkVersion(attr.value);
Shane Farmer78c43d72017-12-04 09:08:38 -0800723 if (version) {
724 valid_attr = true;
725 entry.min_sdk_version = version.value();
726 }
727 } else if (attr.name == "targetSdkVersion") {
Ryan Mitchell4382e442021-07-14 12:53:01 -0700728 std::optional<int> version = ResourceUtils::ParseSdkVersion(attr.value);
Shane Farmer78c43d72017-12-04 09:08:38 -0800729 if (version) {
730 valid_attr = true;
731 entry.target_sdk_version = version;
732 }
733 } else if (attr.name == "maxSdkVersion") {
Ryan Mitchell4382e442021-07-14 12:53:01 -0700734 std::optional<int> version = ResourceUtils::ParseSdkVersion(attr.value);
Shane Farmer78c43d72017-12-04 09:08:38 -0800735 if (version) {
736 valid_attr = true;
737 entry.max_sdk_version = version;
738 }
739 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700740
Shane Farmer78c43d72017-12-04 09:08:38 -0800741 if (!valid_attr) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000742 diag->Error(android::DiagMessage()
743 << "Invalid attribute: " << attr.name << " = " << attr.value);
Shane Farmer280be342017-06-21 15:20:15 -0700744 valid = false;
Shane Farmer280be342017-06-21 15:20:15 -0700745 }
746 }
747
Shane Farmer78c43d72017-12-04 09:08:38 -0800748 if (entry.min_sdk_version == -1) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000749 diag->Error(android::DiagMessage() << "android-sdk is missing minSdkVersion attribute");
Shane Farmer78c43d72017-12-04 09:08:38 -0800750 valid = false;
751 }
752
753 // TODO: Fill in the manifest details when they are finalised.
754 for (auto node : root_element->GetChildElements()) {
755 if (node->name == "manifest") {
756 if (entry.manifest) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000757 diag->Warn(android::DiagMessage() << "Found multiple manifest tags. Ignoring duplicates.");
Shane Farmer78c43d72017-12-04 09:08:38 -0800758 continue;
759 }
760 entry.manifest = {AndroidManifest()};
761 }
762 }
763
764 config->android_sdks[entry.label] = entry;
Shane Farmer280be342017-06-21 15:20:15 -0700765 return valid;
766};
Shane Farmer74cdea32017-05-12 16:22:36 -0700767
Shane Farmercb6c3f92017-11-27 13:19:36 -0800768bool GlTextureGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000769 android::IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700770 std::string label = GetLabel(root_element, diag);
771 if (label.empty()) {
772 return false;
773 }
774
Shane Farmer280be342017-06-21 15:20:15 -0700775 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800776 OrderedEntry<GlTexture>& entry = config->gl_texture_groups[label];
Ryan Mitchell4382e442021-07-14 12:53:01 -0700777 std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800778 if (!order) {
779 valid = false;
780 } else {
781 entry.order = order.value();
782 }
783 auto& group = entry.entry;
Shane Farmer280be342017-06-21 15:20:15 -0700784
785 GlTexture result;
786 for (auto* child : root_element->GetChildElements()) {
787 if (child->name != "gl-texture") {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000788 diag->Error(android::DiagMessage()
789 << "Unexpected element in GL texture group: " << child->name);
Shane Farmer280be342017-06-21 15:20:15 -0700790 valid = false;
791 } else {
792 for (const auto& attr : child->attributes) {
793 if (attr.name == "name") {
794 result.name = attr.value;
795 break;
796 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700797 }
798
Shane Farmer280be342017-06-21 15:20:15 -0700799 for (auto* element : child->GetChildElements()) {
800 if (element->name != "texture-path") {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000801 diag->Error(android::DiagMessage()
802 << "Unexpected element in gl-texture element: " << child->name);
Shane Farmer74cdea32017-05-12 16:22:36 -0700803 valid = false;
Shane Farmer280be342017-06-21 15:20:15 -0700804 continue;
805 }
806 for (auto& node : element->children) {
807 xml::Text* t;
808 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700809 result.texture_paths.emplace_back(TrimWhitespace(t->text));
Shane Farmer74cdea32017-05-12 16:22:36 -0700810 }
811 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700812 }
Shane Farmer280be342017-06-21 15:20:15 -0700813 }
814 group.push_back(result);
815 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700816
Shane Farmer280be342017-06-21 15:20:15 -0700817 return valid;
818};
Shane Farmer74cdea32017-05-12 16:22:36 -0700819
Shane Farmercb6c3f92017-11-27 13:19:36 -0800820bool DeviceFeatureGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000821 android::IDiagnostics* diag) {
Shane Farmer280be342017-06-21 15:20:15 -0700822 std::string label = GetLabel(root_element, diag);
823 if (label.empty()) {
824 return false;
825 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700826
Shane Farmer280be342017-06-21 15:20:15 -0700827 bool valid = true;
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800828 OrderedEntry<DeviceFeature>& entry = config->device_feature_groups[label];
Ryan Mitchell4382e442021-07-14 12:53:01 -0700829 std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800830 if (!order) {
831 valid = false;
832 } else {
833 entry.order = order.value();
834 }
835 auto& group = entry.entry;
Shane Farmer74cdea32017-05-12 16:22:36 -0700836
Shane Farmer280be342017-06-21 15:20:15 -0700837 for (auto* child : root_element->GetChildElements()) {
838 if (child->name != "supports-feature") {
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000839 diag->Error(android::DiagMessage()
840 << "Unexpected root_element in device feature group: " << child->name);
Shane Farmer280be342017-06-21 15:20:15 -0700841 valid = false;
842 } else {
843 for (auto& node : child->children) {
844 xml::Text* t;
845 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700846 group.emplace_back(TrimWhitespace(t->text));
Shane Farmer280be342017-06-21 15:20:15 -0700847 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700848 }
849 }
Shane Farmer280be342017-06-21 15:20:15 -0700850 }
851 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700852
Shane Farmer280be342017-06-21 15:20:15 -0700853 return valid;
854};
Shane Farmer74cdea32017-05-12 16:22:36 -0700855
Shane Farmercb6c3f92017-11-27 13:19:36 -0800856} // namespace handler
857} // namespace configuration
858
Shane Farmer74cdea32017-05-12 16:22:36 -0700859} // namespace aapt