blob: d2b4d725c416154e188aa150fdd2fc0e69bb9891 [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 Farmer3edd4722017-09-01 14:34:22 -070030#include "ResourceUtils.h"
Shane Farmerb1027272017-06-14 09:10:28 -070031#include "io/File.h"
32#include "io/FileSystem.h"
Adam Lesinskiefeb7af2017-08-02 14:57:43 -070033#include "io/StringInputStream.h"
Shane Farmer9ecc0752017-08-24 15:55:36 -070034#include "util/Files.h"
Shane Farmer9f0e7f12017-06-22 12:26:44 -070035#include "util/Maybe.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
41namespace aapt {
42
43namespace {
44
45using ::aapt::configuration::Abi;
46using ::aapt::configuration::AndroidManifest;
47using ::aapt::configuration::AndroidSdk;
48using ::aapt::configuration::Artifact;
Shane Farmer280be342017-06-21 15:20:15 -070049using ::aapt::configuration::PostProcessingConfiguration;
Shane Farmer74cdea32017-05-12 16:22:36 -070050using ::aapt::configuration::GlTexture;
51using ::aapt::configuration::Group;
52using ::aapt::configuration::Locale;
Shane Farmerb1027272017-06-14 09:10:28 -070053using ::aapt::io::IFile;
54using ::aapt::io::RegularFile;
Adam Lesinskiefeb7af2017-08-02 14:57:43 -070055using ::aapt::io::StringInputStream;
Shane Farmer74cdea32017-05-12 16:22:36 -070056using ::aapt::util::TrimWhitespace;
57using ::aapt::xml::Element;
Shane Farmer74cdea32017-05-12 16:22:36 -070058using ::aapt::xml::NodeCast;
59using ::aapt::xml::XmlActionExecutor;
60using ::aapt::xml::XmlActionExecutorPolicy;
61using ::aapt::xml::XmlNodeAction;
Shane Farmerb1027272017-06-14 09:10:28 -070062using ::android::base::ReadFileToString;
Shane Farmer0a5b2012017-06-22 12:24:12 -070063using ::android::StringPiece;
Shane Farmer74cdea32017-05-12 16:22:36 -070064
Shane Farmer57669432017-06-19 12:52:04 -070065const std::unordered_map<std::string, Abi> kStringToAbiMap = {
66 {"armeabi", Abi::kArmeV6}, {"armeabi-v7a", Abi::kArmV7a}, {"arm64-v8a", Abi::kArm64V8a},
67 {"x86", Abi::kX86}, {"x86_64", Abi::kX86_64}, {"mips", Abi::kMips},
68 {"mips64", Abi::kMips64}, {"universal", Abi::kUniversal},
69};
70const std::map<Abi, std::string> kAbiToStringMap = {
71 {Abi::kArmeV6, "armeabi"}, {Abi::kArmV7a, "armeabi-v7a"}, {Abi::kArm64V8a, "arm64-v8a"},
72 {Abi::kX86, "x86"}, {Abi::kX86_64, "x86_64"}, {Abi::kMips, "mips"},
73 {Abi::kMips64, "mips64"}, {Abi::kUniversal, "universal"},
Shane Farmer74cdea32017-05-12 16:22:36 -070074};
75
76constexpr const char* kAaptXmlNs = "http://schemas.android.com/tools/aapt";
77
78/** A default noop diagnostics context. */
79class NoopDiagnostics : public IDiagnostics {
80 public:
81 void Log(Level level, DiagMessageActual& actualMsg) override {}
82};
83NoopDiagnostics noop_;
84
85std::string GetLabel(const Element* element, IDiagnostics* diag) {
86 std::string label;
87 for (const auto& attr : element->attributes) {
88 if (attr.name == "label") {
89 label = attr.value;
90 break;
91 }
92 }
93
94 if (label.empty()) {
95 diag->Error(DiagMessage() << "No label found for element " << element->name);
96 }
97 return label;
98}
99
100/** XML node visitor that removes all of the namespace URIs from the node and all children. */
101class NamespaceVisitor : public xml::Visitor {
102 public:
103 void Visit(xml::Element* node) override {
104 node->namespace_uri.clear();
105 VisitChildren(node);
106 }
107};
108
109} // namespace
110
Shane Farmer57669432017-06-19 12:52:04 -0700111namespace configuration {
Shane Farmerb1027272017-06-14 09:10:28 -0700112
Shane Farmer57669432017-06-19 12:52:04 -0700113const std::string& AbiToString(Abi abi) {
114 return kAbiToStringMap.find(abi)->second;
115}
116
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700117/**
118 * Attempts to replace the placeholder in the name string with the provided value. Returns true on
119 * success, or false if the either the placeholder is not found in the name, or the value is not
120 * present and the placeholder was.
121 */
Shane Farmer0a5b2012017-06-22 12:24:12 -0700122static bool ReplacePlaceholder(const StringPiece& placeholder, const Maybe<StringPiece>& value,
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700123 std::string* name, IDiagnostics* diag) {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700124 size_t offset = name->find(placeholder.data());
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700125 bool found = (offset != std::string::npos);
126
127 // Make sure the placeholder was present if the desired value is present.
128 if (!found) {
129 if (value) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700130 diag->Error(DiagMessage() << "Missing placeholder for artifact: " << placeholder);
131 return false;
132 }
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700133 return true;
134 }
135
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700136 DCHECK(found) << "Missing return path for placeholder not found";
137
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700138 // Make sure the placeholder was not present if the desired value was not present.
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700139 if (!value) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700140 diag->Error(DiagMessage() << "Placeholder present but no value for artifact: " << placeholder);
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700141 return false;
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700142 }
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700143
Shane Farmer0a5b2012017-06-22 12:24:12 -0700144 name->replace(offset, placeholder.length(), value.value().data());
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700145
146 // Make sure there was only one instance of the placeholder.
Shane Farmer0a5b2012017-06-22 12:24:12 -0700147 if (name->find(placeholder.data()) != std::string::npos) {
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700148 diag->Error(DiagMessage() << "Placeholder present multiple times: " << placeholder);
149 return false;
150 }
151 return true;
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700152}
153
Shane Farmer9ecc0752017-08-24 15:55:36 -0700154/**
155 * Returns the common artifact base name from a template string.
156 */
157Maybe<std::string> ToBaseName(std::string result, const StringPiece& apk_name, IDiagnostics* diag) {
158 const StringPiece ext = file::GetExtension(apk_name);
159 size_t end_index = apk_name.to_string().rfind(ext.to_string());
160 const std::string base_name =
161 (end_index != std::string::npos) ? std::string{apk_name.begin(), end_index} : "";
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700162
Shane Farmer9ecc0752017-08-24 15:55:36 -0700163 // Base name is optional.
164 if (result.find("${basename}") != std::string::npos) {
165 Maybe<StringPiece> maybe_base_name =
166 base_name.empty() ? Maybe<StringPiece>{} : Maybe<StringPiece>{base_name};
167 if (!ReplacePlaceholder("${basename}", maybe_base_name, &result, diag)) {
168 return {};
169 }
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700170 }
171
Shane Farmer0a5b2012017-06-22 12:24:12 -0700172 // Extension is optional.
173 if (result.find("${ext}") != std::string::npos) {
Shane Farmer9ecc0752017-08-24 15:55:36 -0700174 // Make sure we disregard the '.' in the extension when replacing the placeholder.
175 if (!ReplacePlaceholder("${ext}", {ext.substr(1)}, &result, diag)) {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700176 return {};
177 }
Shane Farmer9ecc0752017-08-24 15:55:36 -0700178 } else {
179 // If no extension is specified, and the name template does not end in the current extension,
180 // add the existing extension.
181 if (!util::EndsWith(result, ext)) {
182 result.append(ext.to_string());
183 }
Shane Farmer0a5b2012017-06-22 12:24:12 -0700184 }
185
Shane Farmer9ecc0752017-08-24 15:55:36 -0700186 return result;
187}
188
189Maybe<std::string> Artifact::ToArtifactName(const StringPiece& format, const StringPiece& apk_name,
190 IDiagnostics* diag) const {
191 Maybe<std::string> base = ToBaseName(format.to_string(), apk_name, diag);
192 if (!base) {
193 return {};
194 }
195 std::string result = std::move(base.value());
196
Shane Farmer0a5b2012017-06-22 12:24:12 -0700197 if (!ReplacePlaceholder("${abi}", abi_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700198 return {};
199 }
200
Shane Farmer0a5b2012017-06-22 12:24:12 -0700201 if (!ReplacePlaceholder("${density}", screen_density_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700202 return {};
203 }
204
Shane Farmer0a5b2012017-06-22 12:24:12 -0700205 if (!ReplacePlaceholder("${locale}", locale_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700206 return {};
207 }
208
Shane Farmer0a5b2012017-06-22 12:24:12 -0700209 if (!ReplacePlaceholder("${sdk}", android_sdk_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700210 return {};
211 }
212
Shane Farmer0a5b2012017-06-22 12:24:12 -0700213 if (!ReplacePlaceholder("${feature}", device_feature_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700214 return {};
215 }
216
Shane Farmer0a5b2012017-06-22 12:24:12 -0700217 if (!ReplacePlaceholder("${gl}", gl_texture_group, &result, diag)) {
218 return {};
219 }
220
221 return result;
222}
223
Shane Farmer9ecc0752017-08-24 15:55:36 -0700224Maybe<std::string> Artifact::Name(const StringPiece& apk_name, IDiagnostics* diag) const {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700225 if (!name) {
226 return {};
227 }
228
Shane Farmer9ecc0752017-08-24 15:55:36 -0700229 return ToBaseName(name.value(), apk_name, diag);
230}
Shane Farmer0a5b2012017-06-22 12:24:12 -0700231
Shane Farmer9ecc0752017-08-24 15:55:36 -0700232bool PostProcessingConfiguration::AllArtifactNames(const StringPiece& apk_name,
233 std::vector<std::string>* artifact_names,
234 IDiagnostics* diag) const {
235 for (const auto& artifact : artifacts) {
236 Maybe<std::string> name;
237 if (artifact.name) {
238 name = artifact.Name(apk_name, diag);
239 } else {
240 if (!artifact_format) {
241 diag->Error(DiagMessage() << "No global artifact template and an artifact name is missing");
242 return false;
243 }
244 name = artifact.ToArtifactName(artifact_format.value(), apk_name, diag);
Shane Farmer0a5b2012017-06-22 12:24:12 -0700245 }
Shane Farmer9ecc0752017-08-24 15:55:36 -0700246
247 if (!name) {
248 return false;
249 }
250
251 artifact_names->push_back(std::move(name.value()));
Shane Farmer0a5b2012017-06-22 12:24:12 -0700252 }
253
Shane Farmer9ecc0752017-08-24 15:55:36 -0700254 return true;
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700255}
256
Shane Farmer57669432017-06-19 12:52:04 -0700257} // namespace configuration
Shane Farmerb1027272017-06-14 09:10:28 -0700258
259/** Returns a ConfigurationParser for the file located at the provided path. */
260Maybe<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) {
261 std::string contents;
262 if (!ReadFileToString(path, &contents, true)) {
263 return {};
264 }
265 return ConfigurationParser(contents);
266}
267
Shane Farmer74cdea32017-05-12 16:22:36 -0700268ConfigurationParser::ConfigurationParser(std::string contents)
269 : contents_(std::move(contents)),
270 diag_(&noop_) {
271}
272
Shane Farmer280be342017-06-21 15:20:15 -0700273Maybe<PostProcessingConfiguration> ConfigurationParser::Parse() {
Adam Lesinskiefeb7af2017-08-02 14:57:43 -0700274 StringInputStream in(contents_);
Adam Lesinski6b372992017-08-09 10:54:23 -0700275 std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, diag_, Source("config.xml"));
Shane Farmer74cdea32017-05-12 16:22:36 -0700276 if (!doc) {
277 return {};
278 }
279
280 // Strip any namespaces from the XML as the XmlActionExecutor ignores anything with a namespace.
Adam Lesinski6b372992017-08-09 10:54:23 -0700281 Element* root = doc->root.get();
Shane Farmer74cdea32017-05-12 16:22:36 -0700282 if (root == nullptr) {
283 diag_->Error(DiagMessage() << "Could not find the root element in the XML document");
284 return {};
285 }
286
287 std::string& xml_ns = root->namespace_uri;
288 if (!xml_ns.empty()) {
289 if (xml_ns != kAaptXmlNs) {
290 diag_->Error(DiagMessage() << "Unknown namespace found on root element: " << xml_ns);
291 return {};
292 }
293
294 xml_ns.clear();
295 NamespaceVisitor visitor;
296 root->Accept(&visitor);
297 }
298
299 XmlActionExecutor executor;
300 XmlNodeAction& root_action = executor["post-process"];
301 XmlNodeAction& artifacts_action = root_action["artifacts"];
302 XmlNodeAction& groups_action = root_action["groups"];
303
Shane Farmer280be342017-06-21 15:20:15 -0700304 PostProcessingConfiguration config;
Shane Farmer74cdea32017-05-12 16:22:36 -0700305
306 // Helper to bind a static method to an action handler in the DOM executor.
Shane Farmer280be342017-06-21 15:20:15 -0700307 auto bind_handler =
308 [&config](std::function<bool(PostProcessingConfiguration*, Element*, IDiagnostics*)> h)
Shane Farmer74cdea32017-05-12 16:22:36 -0700309 -> XmlNodeAction::ActionFuncWithDiag {
310 return std::bind(h, &config, std::placeholders::_1, std::placeholders::_2);
311 };
312
313 // Parse the artifact elements.
314 artifacts_action["artifact"].Action(bind_handler(artifact_handler_));
315 artifacts_action["artifact-format"].Action(bind_handler(artifact_format_handler_));
316
317 // Parse the different configuration groups.
318 groups_action["abi-group"].Action(bind_handler(abi_group_handler_));
319 groups_action["screen-density-group"].Action(bind_handler(screen_density_group_handler_));
320 groups_action["locale-group"].Action(bind_handler(locale_group_handler_));
321 groups_action["android-sdk-group"].Action(bind_handler(android_sdk_group_handler_));
322 groups_action["gl-texture-group"].Action(bind_handler(gl_texture_group_handler_));
323 groups_action["device-feature-group"].Action(bind_handler(device_feature_group_handler_));
324
325 if (!executor.Execute(XmlActionExecutorPolicy::kNone, diag_, doc.get())) {
326 diag_->Error(DiagMessage() << "Could not process XML document");
327 return {};
328 }
329
Shane Farmer57669432017-06-19 12:52:04 -0700330 // TODO: Validate all references in the configuration are valid. It should be safe to assume from
331 // this point on that any references from one section to another will be present.
332
Shane Farmer74cdea32017-05-12 16:22:36 -0700333 return {config};
334}
335
336ConfigurationParser::ActionHandler ConfigurationParser::artifact_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700337 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
338 Artifact artifact{};
339 for (const auto& attr : root_element->attributes) {
340 if (attr.name == "name") {
341 artifact.name = attr.value;
342 } else if (attr.name == "abi-group") {
343 artifact.abi_group = {attr.value};
344 } else if (attr.name == "screen-density-group") {
345 artifact.screen_density_group = {attr.value};
346 } else if (attr.name == "locale-group") {
347 artifact.locale_group = {attr.value};
348 } else if (attr.name == "android-sdk-group") {
349 artifact.android_sdk_group = {attr.value};
350 } else if (attr.name == "gl-texture-group") {
351 artifact.gl_texture_group = {attr.value};
352 } else if (attr.name == "device-feature-group") {
353 artifact.device_feature_group = {attr.value};
354 } else {
355 diag->Note(DiagMessage() << "Unknown artifact attribute: " << attr.name << " = "
356 << attr.value);
357 }
358 }
359 config->artifacts.push_back(artifact);
360 return true;
361};
Shane Farmer74cdea32017-05-12 16:22:36 -0700362
363ConfigurationParser::ActionHandler ConfigurationParser::artifact_format_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700364 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
365 for (auto& node : root_element->children) {
366 xml::Text* t;
367 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
368 config->artifact_format = TrimWhitespace(t->text).to_string();
369 break;
370 }
371 }
372 return true;
373};
374
375ConfigurationParser::ActionHandler ConfigurationParser::abi_group_handler_ =
376 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
377 std::string label = GetLabel(root_element, diag);
378 if (label.empty()) {
379 return false;
380 }
381
382 auto& group = config->abi_groups[label];
383 bool valid = true;
384
385 for (auto* child : root_element->GetChildElements()) {
386 if (child->name != "abi") {
387 diag->Error(DiagMessage() << "Unexpected element in ABI group: " << child->name);
388 valid = false;
389 } else {
390 for (auto& node : child->children) {
Shane Farmer74cdea32017-05-12 16:22:36 -0700391 xml::Text* t;
392 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
Shane Farmer280be342017-06-21 15:20:15 -0700393 group.push_back(kStringToAbiMap.at(TrimWhitespace(t->text).to_string()));
Shane Farmer74cdea32017-05-12 16:22:36 -0700394 break;
395 }
396 }
Shane Farmer280be342017-06-21 15:20:15 -0700397 }
398 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700399
Shane Farmer280be342017-06-21 15:20:15 -0700400 return valid;
401};
Shane Farmer74cdea32017-05-12 16:22:36 -0700402
403ConfigurationParser::ActionHandler ConfigurationParser::screen_density_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700404 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
405 std::string label = GetLabel(root_element, diag);
406 if (label.empty()) {
407 return false;
408 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700409
Shane Farmer280be342017-06-21 15:20:15 -0700410 auto& group = config->screen_density_groups[label];
411 bool valid = true;
Shane Farmer74cdea32017-05-12 16:22:36 -0700412
Shane Farmer280be342017-06-21 15:20:15 -0700413 for (auto* child : root_element->GetChildElements()) {
414 if (child->name != "screen-density") {
415 diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
416 << child->name);
417 valid = false;
418 } else {
419 for (auto& node : child->children) {
420 xml::Text* t;
421 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
422 ConfigDescription config_descriptor;
423 const android::StringPiece& text = TrimWhitespace(t->text);
Shane Farmer0a5b2012017-06-22 12:24:12 -0700424 bool parsed = ConfigDescription::Parse(text, &config_descriptor);
425 if (parsed &&
426 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
427 android::ResTable_config::CONFIG_DENSITY)) {
Shane Farmer280be342017-06-21 15:20:15 -0700428 // Copy the density with the minimum SDK version stripped out.
429 group.push_back(config_descriptor.CopyWithoutSdkVersion());
430 } else {
431 diag->Error(DiagMessage()
432 << "Could not parse config descriptor for screen-density: " << text);
433 valid = false;
Shane Farmer74cdea32017-05-12 16:22:36 -0700434 }
Shane Farmer280be342017-06-21 15:20:15 -0700435 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700436 }
437 }
Shane Farmer280be342017-06-21 15:20:15 -0700438 }
439 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700440
Shane Farmer280be342017-06-21 15:20:15 -0700441 return valid;
442};
Shane Farmer74cdea32017-05-12 16:22:36 -0700443
444ConfigurationParser::ActionHandler ConfigurationParser::locale_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700445 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
446 std::string label = GetLabel(root_element, diag);
447 if (label.empty()) {
448 return false;
449 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700450
Shane Farmer280be342017-06-21 15:20:15 -0700451 auto& group = config->locale_groups[label];
452 bool valid = true;
Shane Farmer74cdea32017-05-12 16:22:36 -0700453
Shane Farmer280be342017-06-21 15:20:15 -0700454 for (auto* child : root_element->GetChildElements()) {
455 if (child->name != "locale") {
456 diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
457 << child->name);
458 valid = false;
459 } else {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700460 for (auto& node : child->children) {
461 xml::Text* t;
462 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
463 ConfigDescription config_descriptor;
464 const android::StringPiece& text = TrimWhitespace(t->text);
465 bool parsed = ConfigDescription::Parse(text, &config_descriptor);
466 if (parsed &&
467 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
468 android::ResTable_config::CONFIG_LOCALE)) {
469 // Copy the locale with the minimum SDK version stripped out.
470 group.push_back(config_descriptor.CopyWithoutSdkVersion());
471 } else {
472 diag->Error(DiagMessage()
473 << "Could not parse config descriptor for screen-density: " << text);
474 valid = false;
475 }
476 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700477 }
478 }
Shane Farmer280be342017-06-21 15:20:15 -0700479 }
480 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700481
Shane Farmer280be342017-06-21 15:20:15 -0700482 return valid;
483};
Shane Farmer74cdea32017-05-12 16:22:36 -0700484
485ConfigurationParser::ActionHandler ConfigurationParser::android_sdk_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700486 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
487 std::string label = GetLabel(root_element, diag);
488 if (label.empty()) {
489 return false;
490 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700491
Shane Farmer280be342017-06-21 15:20:15 -0700492 bool valid = true;
Shane Farmerefe45392017-08-21 14:39:28 -0700493 bool found = false;
Shane Farmer74cdea32017-05-12 16:22:36 -0700494
Shane Farmer280be342017-06-21 15:20:15 -0700495 for (auto* child : root_element->GetChildElements()) {
496 if (child->name != "android-sdk") {
497 diag->Error(DiagMessage() << "Unexpected root_element in ABI group: " << child->name);
498 valid = false;
499 } else {
500 AndroidSdk entry;
501 for (const auto& attr : child->attributes) {
502 if (attr.name == "minSdkVersion") {
Shane Farmer3edd4722017-09-01 14:34:22 -0700503 entry.min_sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
Shane Farmer280be342017-06-21 15:20:15 -0700504 } else if (attr.name == "targetSdkVersion") {
Shane Farmer3edd4722017-09-01 14:34:22 -0700505 entry.target_sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
Shane Farmer280be342017-06-21 15:20:15 -0700506 } else if (attr.name == "maxSdkVersion") {
Shane Farmer3edd4722017-09-01 14:34:22 -0700507 entry.max_sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
Shane Farmer74cdea32017-05-12 16:22:36 -0700508 } else {
Shane Farmer280be342017-06-21 15:20:15 -0700509 diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name << " = " << attr.value);
Shane Farmer74cdea32017-05-12 16:22:36 -0700510 }
511 }
512
Shane Farmer280be342017-06-21 15:20:15 -0700513 // TODO: Fill in the manifest details when they are finalised.
514 for (auto node : child->GetChildElements()) {
515 if (node->name == "manifest") {
516 if (entry.manifest) {
517 diag->Warn(DiagMessage() << "Found multiple manifest tags. Ignoring duplicates.");
518 continue;
519 }
520 entry.manifest = {AndroidManifest()};
521 }
522 }
523
Shane Farmerefe45392017-08-21 14:39:28 -0700524 config->android_sdk_groups[label] = entry;
525 if (found) {
526 valid = false;
527 }
528 found = true;
Shane Farmer280be342017-06-21 15:20:15 -0700529 }
530 }
531
532 return valid;
533};
Shane Farmer74cdea32017-05-12 16:22:36 -0700534
535ConfigurationParser::ActionHandler ConfigurationParser::gl_texture_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700536 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
537 std::string label = GetLabel(root_element, diag);
538 if (label.empty()) {
539 return false;
540 }
541
542 auto& group = config->gl_texture_groups[label];
543 bool valid = true;
544
545 GlTexture result;
546 for (auto* child : root_element->GetChildElements()) {
547 if (child->name != "gl-texture") {
548 diag->Error(DiagMessage() << "Unexpected element in GL texture group: " << child->name);
549 valid = false;
550 } else {
551 for (const auto& attr : child->attributes) {
552 if (attr.name == "name") {
553 result.name = attr.value;
554 break;
555 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700556 }
557
Shane Farmer280be342017-06-21 15:20:15 -0700558 for (auto* element : child->GetChildElements()) {
559 if (element->name != "texture-path") {
560 diag->Error(DiagMessage() << "Unexpected element in gl-texture element: " << child->name);
Shane Farmer74cdea32017-05-12 16:22:36 -0700561 valid = false;
Shane Farmer280be342017-06-21 15:20:15 -0700562 continue;
563 }
564 for (auto& node : element->children) {
565 xml::Text* t;
566 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
567 result.texture_paths.push_back(TrimWhitespace(t->text).to_string());
Shane Farmer74cdea32017-05-12 16:22:36 -0700568 }
569 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700570 }
Shane Farmer280be342017-06-21 15:20:15 -0700571 }
572 group.push_back(result);
573 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700574
Shane Farmer280be342017-06-21 15:20:15 -0700575 return valid;
576};
Shane Farmer74cdea32017-05-12 16:22:36 -0700577
578ConfigurationParser::ActionHandler ConfigurationParser::device_feature_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700579 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
580 std::string label = GetLabel(root_element, diag);
581 if (label.empty()) {
582 return false;
583 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700584
Shane Farmer280be342017-06-21 15:20:15 -0700585 auto& group = config->device_feature_groups[label];
586 bool valid = true;
Shane Farmer74cdea32017-05-12 16:22:36 -0700587
Shane Farmer280be342017-06-21 15:20:15 -0700588 for (auto* child : root_element->GetChildElements()) {
589 if (child->name != "supports-feature") {
590 diag->Error(DiagMessage() << "Unexpected root_element in device feature group: "
591 << child->name);
592 valid = false;
593 } else {
594 for (auto& node : child->children) {
595 xml::Text* t;
596 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
597 group.push_back(TrimWhitespace(t->text).to_string());
598 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700599 }
600 }
Shane Farmer280be342017-06-21 15:20:15 -0700601 }
602 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700603
Shane Farmer280be342017-06-21 15:20:15 -0700604 return valid;
605};
Shane Farmer74cdea32017-05-12 16:22:36 -0700606
607} // namespace aapt