blob: 1735a504e55398f8e538fd71b6104d819488a32c [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>
23#include <utility>
24
Adam Lesinskiefeb7af2017-08-02 14:57:43 -070025#include "android-base/file.h"
26#include "android-base/logging.h"
Shane Farmer74cdea32017-05-12 16:22:36 -070027
28#include "ConfigDescription.h"
29#include "Diagnostics.h"
Shane Farmerb1027272017-06-14 09:10:28 -070030#include "io/File.h"
31#include "io/FileSystem.h"
Adam Lesinskiefeb7af2017-08-02 14:57:43 -070032#include "io/StringInputStream.h"
Shane Farmer9ecc0752017-08-24 15:55:36 -070033#include "util/Files.h"
Shane Farmer9f0e7f12017-06-22 12:26:44 -070034#include "util/Maybe.h"
Shane Farmer74cdea32017-05-12 16:22:36 -070035#include "util/Util.h"
36#include "xml/XmlActionExecutor.h"
37#include "xml/XmlDom.h"
38#include "xml/XmlUtil.h"
39
40namespace aapt {
41
42namespace {
43
44using ::aapt::configuration::Abi;
45using ::aapt::configuration::AndroidManifest;
46using ::aapt::configuration::AndroidSdk;
47using ::aapt::configuration::Artifact;
Shane Farmer280be342017-06-21 15:20:15 -070048using ::aapt::configuration::PostProcessingConfiguration;
Shane Farmer74cdea32017-05-12 16:22:36 -070049using ::aapt::configuration::GlTexture;
50using ::aapt::configuration::Group;
51using ::aapt::configuration::Locale;
Shane Farmerb1027272017-06-14 09:10:28 -070052using ::aapt::io::IFile;
53using ::aapt::io::RegularFile;
Adam Lesinskiefeb7af2017-08-02 14:57:43 -070054using ::aapt::io::StringInputStream;
Shane Farmer74cdea32017-05-12 16:22:36 -070055using ::aapt::util::TrimWhitespace;
56using ::aapt::xml::Element;
Shane Farmer74cdea32017-05-12 16:22:36 -070057using ::aapt::xml::NodeCast;
58using ::aapt::xml::XmlActionExecutor;
59using ::aapt::xml::XmlActionExecutorPolicy;
60using ::aapt::xml::XmlNodeAction;
Shane Farmerb1027272017-06-14 09:10:28 -070061using ::android::base::ReadFileToString;
Shane Farmer0a5b2012017-06-22 12:24:12 -070062using ::android::StringPiece;
Shane Farmer74cdea32017-05-12 16:22:36 -070063
Shane Farmer57669432017-06-19 12:52:04 -070064const std::unordered_map<std::string, Abi> kStringToAbiMap = {
65 {"armeabi", Abi::kArmeV6}, {"armeabi-v7a", Abi::kArmV7a}, {"arm64-v8a", Abi::kArm64V8a},
66 {"x86", Abi::kX86}, {"x86_64", Abi::kX86_64}, {"mips", Abi::kMips},
67 {"mips64", Abi::kMips64}, {"universal", Abi::kUniversal},
68};
69const std::map<Abi, std::string> kAbiToStringMap = {
70 {Abi::kArmeV6, "armeabi"}, {Abi::kArmV7a, "armeabi-v7a"}, {Abi::kArm64V8a, "arm64-v8a"},
71 {Abi::kX86, "x86"}, {Abi::kX86_64, "x86_64"}, {Abi::kMips, "mips"},
72 {Abi::kMips64, "mips64"}, {Abi::kUniversal, "universal"},
Shane Farmer74cdea32017-05-12 16:22:36 -070073};
74
75constexpr const char* kAaptXmlNs = "http://schemas.android.com/tools/aapt";
76
77/** A default noop diagnostics context. */
78class NoopDiagnostics : public IDiagnostics {
79 public:
80 void Log(Level level, DiagMessageActual& actualMsg) override {}
81};
82NoopDiagnostics noop_;
83
84std::string GetLabel(const Element* element, IDiagnostics* diag) {
85 std::string label;
86 for (const auto& attr : element->attributes) {
87 if (attr.name == "label") {
88 label = attr.value;
89 break;
90 }
91 }
92
93 if (label.empty()) {
94 diag->Error(DiagMessage() << "No label found for element " << element->name);
95 }
96 return label;
97}
98
99/** XML node visitor that removes all of the namespace URIs from the node and all children. */
100class NamespaceVisitor : public xml::Visitor {
101 public:
102 void Visit(xml::Element* node) override {
103 node->namespace_uri.clear();
104 VisitChildren(node);
105 }
106};
107
108} // namespace
109
Shane Farmer57669432017-06-19 12:52:04 -0700110namespace configuration {
Shane Farmerb1027272017-06-14 09:10:28 -0700111
Shane Farmer57669432017-06-19 12:52:04 -0700112const std::string& AbiToString(Abi abi) {
113 return kAbiToStringMap.find(abi)->second;
114}
115
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700116/**
117 * Attempts to replace the placeholder in the name string with the provided value. Returns true on
118 * success, or false if the either the placeholder is not found in the name, or the value is not
119 * present and the placeholder was.
120 */
Shane Farmer0a5b2012017-06-22 12:24:12 -0700121static bool ReplacePlaceholder(const StringPiece& placeholder, const Maybe<StringPiece>& value,
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700122 std::string* name, IDiagnostics* diag) {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700123 size_t offset = name->find(placeholder.data());
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700124 bool found = (offset != std::string::npos);
125
126 // Make sure the placeholder was present if the desired value is present.
127 if (!found) {
128 if (value) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700129 diag->Error(DiagMessage() << "Missing placeholder for artifact: " << placeholder);
130 return false;
131 }
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700132 return true;
133 }
134
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700135 DCHECK(found) << "Missing return path for placeholder not found";
136
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700137 // Make sure the placeholder was not present if the desired value was not present.
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700138 if (!value) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700139 diag->Error(DiagMessage() << "Placeholder present but no value for artifact: " << placeholder);
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700140 return false;
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700141 }
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700142
Shane Farmer0a5b2012017-06-22 12:24:12 -0700143 name->replace(offset, placeholder.length(), value.value().data());
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700144
145 // Make sure there was only one instance of the placeholder.
Shane Farmer0a5b2012017-06-22 12:24:12 -0700146 if (name->find(placeholder.data()) != std::string::npos) {
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700147 diag->Error(DiagMessage() << "Placeholder present multiple times: " << placeholder);
148 return false;
149 }
150 return true;
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700151}
152
Shane Farmer9ecc0752017-08-24 15:55:36 -0700153/**
154 * Returns the common artifact base name from a template string.
155 */
156Maybe<std::string> ToBaseName(std::string result, const StringPiece& apk_name, IDiagnostics* diag) {
157 const StringPiece ext = file::GetExtension(apk_name);
158 size_t end_index = apk_name.to_string().rfind(ext.to_string());
159 const std::string base_name =
160 (end_index != std::string::npos) ? std::string{apk_name.begin(), end_index} : "";
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700161
Shane Farmer9ecc0752017-08-24 15:55:36 -0700162 // Base name is optional.
163 if (result.find("${basename}") != std::string::npos) {
164 Maybe<StringPiece> maybe_base_name =
165 base_name.empty() ? Maybe<StringPiece>{} : Maybe<StringPiece>{base_name};
166 if (!ReplacePlaceholder("${basename}", maybe_base_name, &result, diag)) {
167 return {};
168 }
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700169 }
170
Shane Farmer0a5b2012017-06-22 12:24:12 -0700171 // Extension is optional.
172 if (result.find("${ext}") != std::string::npos) {
Shane Farmer9ecc0752017-08-24 15:55:36 -0700173 // Make sure we disregard the '.' in the extension when replacing the placeholder.
174 if (!ReplacePlaceholder("${ext}", {ext.substr(1)}, &result, diag)) {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700175 return {};
176 }
Shane Farmer9ecc0752017-08-24 15:55:36 -0700177 } else {
178 // If no extension is specified, and the name template does not end in the current extension,
179 // add the existing extension.
180 if (!util::EndsWith(result, ext)) {
181 result.append(ext.to_string());
182 }
Shane Farmer0a5b2012017-06-22 12:24:12 -0700183 }
184
Shane Farmer9ecc0752017-08-24 15:55:36 -0700185 return result;
186}
187
188Maybe<std::string> Artifact::ToArtifactName(const StringPiece& format, const StringPiece& apk_name,
189 IDiagnostics* diag) const {
190 Maybe<std::string> base = ToBaseName(format.to_string(), apk_name, diag);
191 if (!base) {
192 return {};
193 }
194 std::string result = std::move(base.value());
195
Shane Farmer0a5b2012017-06-22 12:24:12 -0700196 if (!ReplacePlaceholder("${abi}", abi_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700197 return {};
198 }
199
Shane Farmer0a5b2012017-06-22 12:24:12 -0700200 if (!ReplacePlaceholder("${density}", screen_density_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700201 return {};
202 }
203
Shane Farmer0a5b2012017-06-22 12:24:12 -0700204 if (!ReplacePlaceholder("${locale}", locale_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700205 return {};
206 }
207
Shane Farmer0a5b2012017-06-22 12:24:12 -0700208 if (!ReplacePlaceholder("${sdk}", android_sdk_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700209 return {};
210 }
211
Shane Farmer0a5b2012017-06-22 12:24:12 -0700212 if (!ReplacePlaceholder("${feature}", device_feature_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700213 return {};
214 }
215
Shane Farmer0a5b2012017-06-22 12:24:12 -0700216 if (!ReplacePlaceholder("${gl}", gl_texture_group, &result, diag)) {
217 return {};
218 }
219
220 return result;
221}
222
Shane Farmer9ecc0752017-08-24 15:55:36 -0700223Maybe<std::string> Artifact::Name(const StringPiece& apk_name, IDiagnostics* diag) const {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700224 if (!name) {
225 return {};
226 }
227
Shane Farmer9ecc0752017-08-24 15:55:36 -0700228 return ToBaseName(name.value(), apk_name, diag);
229}
Shane Farmer0a5b2012017-06-22 12:24:12 -0700230
Shane Farmer9ecc0752017-08-24 15:55:36 -0700231bool PostProcessingConfiguration::AllArtifactNames(const StringPiece& apk_name,
232 std::vector<std::string>* artifact_names,
233 IDiagnostics* diag) const {
234 for (const auto& artifact : artifacts) {
235 Maybe<std::string> name;
236 if (artifact.name) {
237 name = artifact.Name(apk_name, diag);
238 } else {
239 if (!artifact_format) {
240 diag->Error(DiagMessage() << "No global artifact template and an artifact name is missing");
241 return false;
242 }
243 name = artifact.ToArtifactName(artifact_format.value(), apk_name, diag);
Shane Farmer0a5b2012017-06-22 12:24:12 -0700244 }
Shane Farmer9ecc0752017-08-24 15:55:36 -0700245
246 if (!name) {
247 return false;
248 }
249
250 artifact_names->push_back(std::move(name.value()));
Shane Farmer0a5b2012017-06-22 12:24:12 -0700251 }
252
Shane Farmer9ecc0752017-08-24 15:55:36 -0700253 return true;
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700254}
255
Shane Farmer57669432017-06-19 12:52:04 -0700256} // namespace configuration
Shane Farmerb1027272017-06-14 09:10:28 -0700257
258/** Returns a ConfigurationParser for the file located at the provided path. */
259Maybe<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) {
260 std::string contents;
261 if (!ReadFileToString(path, &contents, true)) {
262 return {};
263 }
264 return ConfigurationParser(contents);
265}
266
Shane Farmer74cdea32017-05-12 16:22:36 -0700267ConfigurationParser::ConfigurationParser(std::string contents)
268 : contents_(std::move(contents)),
269 diag_(&noop_) {
270}
271
Shane Farmer280be342017-06-21 15:20:15 -0700272Maybe<PostProcessingConfiguration> ConfigurationParser::Parse() {
Adam Lesinskiefeb7af2017-08-02 14:57:43 -0700273 StringInputStream in(contents_);
Adam Lesinski6b372992017-08-09 10:54:23 -0700274 std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, diag_, Source("config.xml"));
Shane Farmer74cdea32017-05-12 16:22:36 -0700275 if (!doc) {
276 return {};
277 }
278
279 // Strip any namespaces from the XML as the XmlActionExecutor ignores anything with a namespace.
Adam Lesinski6b372992017-08-09 10:54:23 -0700280 Element* root = doc->root.get();
Shane Farmer74cdea32017-05-12 16:22:36 -0700281 if (root == nullptr) {
282 diag_->Error(DiagMessage() << "Could not find the root element in the XML document");
283 return {};
284 }
285
286 std::string& xml_ns = root->namespace_uri;
287 if (!xml_ns.empty()) {
288 if (xml_ns != kAaptXmlNs) {
289 diag_->Error(DiagMessage() << "Unknown namespace found on root element: " << xml_ns);
290 return {};
291 }
292
293 xml_ns.clear();
294 NamespaceVisitor visitor;
295 root->Accept(&visitor);
296 }
297
298 XmlActionExecutor executor;
299 XmlNodeAction& root_action = executor["post-process"];
300 XmlNodeAction& artifacts_action = root_action["artifacts"];
301 XmlNodeAction& groups_action = root_action["groups"];
302
Shane Farmer280be342017-06-21 15:20:15 -0700303 PostProcessingConfiguration config;
Shane Farmer74cdea32017-05-12 16:22:36 -0700304
305 // Helper to bind a static method to an action handler in the DOM executor.
Shane Farmer280be342017-06-21 15:20:15 -0700306 auto bind_handler =
307 [&config](std::function<bool(PostProcessingConfiguration*, Element*, IDiagnostics*)> h)
Shane Farmer74cdea32017-05-12 16:22:36 -0700308 -> XmlNodeAction::ActionFuncWithDiag {
309 return std::bind(h, &config, std::placeholders::_1, std::placeholders::_2);
310 };
311
312 // Parse the artifact elements.
313 artifacts_action["artifact"].Action(bind_handler(artifact_handler_));
314 artifacts_action["artifact-format"].Action(bind_handler(artifact_format_handler_));
315
316 // Parse the different configuration groups.
317 groups_action["abi-group"].Action(bind_handler(abi_group_handler_));
318 groups_action["screen-density-group"].Action(bind_handler(screen_density_group_handler_));
319 groups_action["locale-group"].Action(bind_handler(locale_group_handler_));
320 groups_action["android-sdk-group"].Action(bind_handler(android_sdk_group_handler_));
321 groups_action["gl-texture-group"].Action(bind_handler(gl_texture_group_handler_));
322 groups_action["device-feature-group"].Action(bind_handler(device_feature_group_handler_));
323
324 if (!executor.Execute(XmlActionExecutorPolicy::kNone, diag_, doc.get())) {
325 diag_->Error(DiagMessage() << "Could not process XML document");
326 return {};
327 }
328
Shane Farmer57669432017-06-19 12:52:04 -0700329 // TODO: Validate all references in the configuration are valid. It should be safe to assume from
330 // this point on that any references from one section to another will be present.
331
Shane Farmer74cdea32017-05-12 16:22:36 -0700332 return {config};
333}
334
335ConfigurationParser::ActionHandler ConfigurationParser::artifact_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700336 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
337 Artifact artifact{};
338 for (const auto& attr : root_element->attributes) {
339 if (attr.name == "name") {
340 artifact.name = attr.value;
341 } else if (attr.name == "abi-group") {
342 artifact.abi_group = {attr.value};
343 } else if (attr.name == "screen-density-group") {
344 artifact.screen_density_group = {attr.value};
345 } else if (attr.name == "locale-group") {
346 artifact.locale_group = {attr.value};
347 } else if (attr.name == "android-sdk-group") {
348 artifact.android_sdk_group = {attr.value};
349 } else if (attr.name == "gl-texture-group") {
350 artifact.gl_texture_group = {attr.value};
351 } else if (attr.name == "device-feature-group") {
352 artifact.device_feature_group = {attr.value};
353 } else {
354 diag->Note(DiagMessage() << "Unknown artifact attribute: " << attr.name << " = "
355 << attr.value);
356 }
357 }
358 config->artifacts.push_back(artifact);
359 return true;
360};
Shane Farmer74cdea32017-05-12 16:22:36 -0700361
362ConfigurationParser::ActionHandler ConfigurationParser::artifact_format_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700363 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
364 for (auto& node : root_element->children) {
365 xml::Text* t;
366 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
367 config->artifact_format = TrimWhitespace(t->text).to_string();
368 break;
369 }
370 }
371 return true;
372};
373
374ConfigurationParser::ActionHandler ConfigurationParser::abi_group_handler_ =
375 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
376 std::string label = GetLabel(root_element, diag);
377 if (label.empty()) {
378 return false;
379 }
380
381 auto& group = config->abi_groups[label];
382 bool valid = true;
383
384 for (auto* child : root_element->GetChildElements()) {
385 if (child->name != "abi") {
386 diag->Error(DiagMessage() << "Unexpected element in ABI group: " << child->name);
387 valid = false;
388 } else {
389 for (auto& node : child->children) {
Shane Farmer74cdea32017-05-12 16:22:36 -0700390 xml::Text* t;
391 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
Shane Farmer280be342017-06-21 15:20:15 -0700392 group.push_back(kStringToAbiMap.at(TrimWhitespace(t->text).to_string()));
Shane Farmer74cdea32017-05-12 16:22:36 -0700393 break;
394 }
395 }
Shane Farmer280be342017-06-21 15:20:15 -0700396 }
397 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700398
Shane Farmer280be342017-06-21 15:20:15 -0700399 return valid;
400};
Shane Farmer74cdea32017-05-12 16:22:36 -0700401
402ConfigurationParser::ActionHandler ConfigurationParser::screen_density_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700403 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
404 std::string label = GetLabel(root_element, diag);
405 if (label.empty()) {
406 return false;
407 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700408
Shane Farmer280be342017-06-21 15:20:15 -0700409 auto& group = config->screen_density_groups[label];
410 bool valid = true;
Shane Farmer74cdea32017-05-12 16:22:36 -0700411
Shane Farmer280be342017-06-21 15:20:15 -0700412 for (auto* child : root_element->GetChildElements()) {
413 if (child->name != "screen-density") {
414 diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
415 << child->name);
416 valid = false;
417 } else {
418 for (auto& node : child->children) {
419 xml::Text* t;
420 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
421 ConfigDescription config_descriptor;
422 const android::StringPiece& text = TrimWhitespace(t->text);
Shane Farmer0a5b2012017-06-22 12:24:12 -0700423 bool parsed = ConfigDescription::Parse(text, &config_descriptor);
424 if (parsed &&
425 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
426 android::ResTable_config::CONFIG_DENSITY)) {
Shane Farmer280be342017-06-21 15:20:15 -0700427 // Copy the density with the minimum SDK version stripped out.
428 group.push_back(config_descriptor.CopyWithoutSdkVersion());
429 } else {
430 diag->Error(DiagMessage()
431 << "Could not parse config descriptor for screen-density: " << text);
432 valid = false;
Shane Farmer74cdea32017-05-12 16:22:36 -0700433 }
Shane Farmer280be342017-06-21 15:20:15 -0700434 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700435 }
436 }
Shane Farmer280be342017-06-21 15:20:15 -0700437 }
438 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700439
Shane Farmer280be342017-06-21 15:20:15 -0700440 return valid;
441};
Shane Farmer74cdea32017-05-12 16:22:36 -0700442
443ConfigurationParser::ActionHandler ConfigurationParser::locale_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700444 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
445 std::string label = GetLabel(root_element, diag);
446 if (label.empty()) {
447 return false;
448 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700449
Shane Farmer280be342017-06-21 15:20:15 -0700450 auto& group = config->locale_groups[label];
451 bool valid = true;
Shane Farmer74cdea32017-05-12 16:22:36 -0700452
Shane Farmer280be342017-06-21 15:20:15 -0700453 for (auto* child : root_element->GetChildElements()) {
454 if (child->name != "locale") {
455 diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
456 << child->name);
457 valid = false;
458 } else {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700459 for (auto& node : child->children) {
460 xml::Text* t;
461 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
462 ConfigDescription config_descriptor;
463 const android::StringPiece& text = TrimWhitespace(t->text);
464 bool parsed = ConfigDescription::Parse(text, &config_descriptor);
465 if (parsed &&
466 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
467 android::ResTable_config::CONFIG_LOCALE)) {
468 // Copy the locale with the minimum SDK version stripped out.
469 group.push_back(config_descriptor.CopyWithoutSdkVersion());
470 } else {
471 diag->Error(DiagMessage()
472 << "Could not parse config descriptor for screen-density: " << text);
473 valid = false;
474 }
475 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700476 }
477 }
Shane Farmer280be342017-06-21 15:20:15 -0700478 }
479 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700480
Shane Farmer280be342017-06-21 15:20:15 -0700481 return valid;
482};
Shane Farmer74cdea32017-05-12 16:22:36 -0700483
484ConfigurationParser::ActionHandler ConfigurationParser::android_sdk_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700485 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
486 std::string label = GetLabel(root_element, diag);
487 if (label.empty()) {
488 return false;
489 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700490
Shane Farmer280be342017-06-21 15:20:15 -0700491 auto& group = config->android_sdk_groups[label];
492 bool valid = true;
Shane Farmer74cdea32017-05-12 16:22:36 -0700493
Shane Farmer280be342017-06-21 15:20:15 -0700494 for (auto* child : root_element->GetChildElements()) {
495 if (child->name != "android-sdk") {
496 diag->Error(DiagMessage() << "Unexpected root_element in ABI group: " << child->name);
497 valid = false;
498 } else {
499 AndroidSdk entry;
500 for (const auto& attr : child->attributes) {
501 if (attr.name == "minSdkVersion") {
502 entry.min_sdk_version = {attr.value};
503 } else if (attr.name == "targetSdkVersion") {
504 entry.target_sdk_version = {attr.value};
505 } else if (attr.name == "maxSdkVersion") {
506 entry.max_sdk_version = {attr.value};
Shane Farmer74cdea32017-05-12 16:22:36 -0700507 } else {
Shane Farmer280be342017-06-21 15:20:15 -0700508 diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name << " = " << attr.value);
Shane Farmer74cdea32017-05-12 16:22:36 -0700509 }
510 }
511
Shane Farmer280be342017-06-21 15:20:15 -0700512 // TODO: Fill in the manifest details when they are finalised.
513 for (auto node : child->GetChildElements()) {
514 if (node->name == "manifest") {
515 if (entry.manifest) {
516 diag->Warn(DiagMessage() << "Found multiple manifest tags. Ignoring duplicates.");
517 continue;
518 }
519 entry.manifest = {AndroidManifest()};
520 }
521 }
522
523 group.push_back(entry);
524 }
525 }
526
527 return valid;
528};
Shane Farmer74cdea32017-05-12 16:22:36 -0700529
530ConfigurationParser::ActionHandler ConfigurationParser::gl_texture_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700531 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
532 std::string label = GetLabel(root_element, diag);
533 if (label.empty()) {
534 return false;
535 }
536
537 auto& group = config->gl_texture_groups[label];
538 bool valid = true;
539
540 GlTexture result;
541 for (auto* child : root_element->GetChildElements()) {
542 if (child->name != "gl-texture") {
543 diag->Error(DiagMessage() << "Unexpected element in GL texture group: " << child->name);
544 valid = false;
545 } else {
546 for (const auto& attr : child->attributes) {
547 if (attr.name == "name") {
548 result.name = attr.value;
549 break;
550 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700551 }
552
Shane Farmer280be342017-06-21 15:20:15 -0700553 for (auto* element : child->GetChildElements()) {
554 if (element->name != "texture-path") {
555 diag->Error(DiagMessage() << "Unexpected element in gl-texture element: " << child->name);
Shane Farmer74cdea32017-05-12 16:22:36 -0700556 valid = false;
Shane Farmer280be342017-06-21 15:20:15 -0700557 continue;
558 }
559 for (auto& node : element->children) {
560 xml::Text* t;
561 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
562 result.texture_paths.push_back(TrimWhitespace(t->text).to_string());
Shane Farmer74cdea32017-05-12 16:22:36 -0700563 }
564 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700565 }
Shane Farmer280be342017-06-21 15:20:15 -0700566 }
567 group.push_back(result);
568 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700569
Shane Farmer280be342017-06-21 15:20:15 -0700570 return valid;
571};
Shane Farmer74cdea32017-05-12 16:22:36 -0700572
573ConfigurationParser::ActionHandler ConfigurationParser::device_feature_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700574 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
575 std::string label = GetLabel(root_element, diag);
576 if (label.empty()) {
577 return false;
578 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700579
Shane Farmer280be342017-06-21 15:20:15 -0700580 auto& group = config->device_feature_groups[label];
581 bool valid = true;
Shane Farmer74cdea32017-05-12 16:22:36 -0700582
Shane Farmer280be342017-06-21 15:20:15 -0700583 for (auto* child : root_element->GetChildElements()) {
584 if (child->name != "supports-feature") {
585 diag->Error(DiagMessage() << "Unexpected root_element in device feature group: "
586 << child->name);
587 valid = false;
588 } else {
589 for (auto& node : child->children) {
590 xml::Text* t;
591 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
592 group.push_back(TrimWhitespace(t->text).to_string());
593 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700594 }
595 }
Shane Farmer280be342017-06-21 15:20:15 -0700596 }
597 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700598
Shane Farmer280be342017-06-21 15:20:15 -0700599 return valid;
600};
Shane Farmer74cdea32017-05-12 16:22:36 -0700601
602} // namespace aapt