blob: a79a577c663b8ede75123d8b99c0a8f0c6c752a8 [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 Farmer810fd182017-09-21 14:37:44 -0700333 // TODO: Automatically arrange artifacts so that they match Play Store multi-APK requirements.
334 // see: https://developer.android.com/google/play/publishing/multiple-apks.html
335 //
336 // For now, make sure the version codes are unique.
337 std::vector<Artifact>& artifacts = config.artifacts;
338 std::sort(artifacts.begin(), artifacts.end());
339 if (std::adjacent_find(artifacts.begin(), artifacts.end()) != artifacts.end()) {
340 diag_->Error(DiagMessage() << "Configuration has duplicate versions");
341 return {};
342 }
343
Shane Farmer74cdea32017-05-12 16:22:36 -0700344 return {config};
345}
346
347ConfigurationParser::ActionHandler ConfigurationParser::artifact_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700348 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
Shane Farmer810fd182017-09-21 14:37:44 -0700349 // This will be incremented later so the first version will always be different to the base APK.
350 int current_version = (config->artifacts.empty()) ? 0 : config->artifacts.back().version;
351
Shane Farmer280be342017-06-21 15:20:15 -0700352 Artifact artifact{};
Shane Farmer810fd182017-09-21 14:37:44 -0700353 Maybe<int> version;
Shane Farmer280be342017-06-21 15:20:15 -0700354 for (const auto& attr : root_element->attributes) {
355 if (attr.name == "name") {
356 artifact.name = attr.value;
Shane Farmer810fd182017-09-21 14:37:44 -0700357 } else if (attr.name == "version") {
358 version = std::stoi(attr.value);
Shane Farmer280be342017-06-21 15:20:15 -0700359 } else if (attr.name == "abi-group") {
360 artifact.abi_group = {attr.value};
361 } else if (attr.name == "screen-density-group") {
362 artifact.screen_density_group = {attr.value};
363 } else if (attr.name == "locale-group") {
364 artifact.locale_group = {attr.value};
365 } else if (attr.name == "android-sdk-group") {
366 artifact.android_sdk_group = {attr.value};
367 } else if (attr.name == "gl-texture-group") {
368 artifact.gl_texture_group = {attr.value};
369 } else if (attr.name == "device-feature-group") {
370 artifact.device_feature_group = {attr.value};
371 } else {
372 diag->Note(DiagMessage() << "Unknown artifact attribute: " << attr.name << " = "
373 << attr.value);
374 }
375 }
Shane Farmer810fd182017-09-21 14:37:44 -0700376
377 artifact.version = (version) ? version.value() : current_version + 1;
378
Shane Farmer280be342017-06-21 15:20:15 -0700379 config->artifacts.push_back(artifact);
380 return true;
381};
Shane Farmer74cdea32017-05-12 16:22:36 -0700382
383ConfigurationParser::ActionHandler ConfigurationParser::artifact_format_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700384 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
385 for (auto& node : root_element->children) {
386 xml::Text* t;
387 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
388 config->artifact_format = TrimWhitespace(t->text).to_string();
389 break;
390 }
391 }
392 return true;
393};
394
395ConfigurationParser::ActionHandler ConfigurationParser::abi_group_handler_ =
396 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
397 std::string label = GetLabel(root_element, diag);
398 if (label.empty()) {
399 return false;
400 }
401
402 auto& group = config->abi_groups[label];
403 bool valid = true;
404
405 for (auto* child : root_element->GetChildElements()) {
406 if (child->name != "abi") {
407 diag->Error(DiagMessage() << "Unexpected element in ABI group: " << child->name);
408 valid = false;
409 } else {
410 for (auto& node : child->children) {
Shane Farmer74cdea32017-05-12 16:22:36 -0700411 xml::Text* t;
412 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
Shane Farmer280be342017-06-21 15:20:15 -0700413 group.push_back(kStringToAbiMap.at(TrimWhitespace(t->text).to_string()));
Shane Farmer74cdea32017-05-12 16:22:36 -0700414 break;
415 }
416 }
Shane Farmer280be342017-06-21 15:20:15 -0700417 }
418 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700419
Shane Farmer280be342017-06-21 15:20:15 -0700420 return valid;
421};
Shane Farmer74cdea32017-05-12 16:22:36 -0700422
423ConfigurationParser::ActionHandler ConfigurationParser::screen_density_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700424 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
425 std::string label = GetLabel(root_element, diag);
426 if (label.empty()) {
427 return false;
428 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700429
Shane Farmer280be342017-06-21 15:20:15 -0700430 auto& group = config->screen_density_groups[label];
431 bool valid = true;
Shane Farmer74cdea32017-05-12 16:22:36 -0700432
Shane Farmer280be342017-06-21 15:20:15 -0700433 for (auto* child : root_element->GetChildElements()) {
434 if (child->name != "screen-density") {
435 diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
436 << child->name);
437 valid = false;
438 } else {
439 for (auto& node : child->children) {
440 xml::Text* t;
441 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
442 ConfigDescription config_descriptor;
443 const android::StringPiece& text = TrimWhitespace(t->text);
Shane Farmer0a5b2012017-06-22 12:24:12 -0700444 bool parsed = ConfigDescription::Parse(text, &config_descriptor);
445 if (parsed &&
446 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
447 android::ResTable_config::CONFIG_DENSITY)) {
Shane Farmer280be342017-06-21 15:20:15 -0700448 // Copy the density with the minimum SDK version stripped out.
449 group.push_back(config_descriptor.CopyWithoutSdkVersion());
450 } else {
451 diag->Error(DiagMessage()
452 << "Could not parse config descriptor for screen-density: " << text);
453 valid = false;
Shane Farmer74cdea32017-05-12 16:22:36 -0700454 }
Shane Farmer280be342017-06-21 15:20:15 -0700455 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700456 }
457 }
Shane Farmer280be342017-06-21 15:20:15 -0700458 }
459 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700460
Shane Farmer280be342017-06-21 15:20:15 -0700461 return valid;
462};
Shane Farmer74cdea32017-05-12 16:22:36 -0700463
464ConfigurationParser::ActionHandler ConfigurationParser::locale_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700465 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
466 std::string label = GetLabel(root_element, diag);
467 if (label.empty()) {
468 return false;
469 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700470
Shane Farmer280be342017-06-21 15:20:15 -0700471 auto& group = config->locale_groups[label];
472 bool valid = true;
Shane Farmer74cdea32017-05-12 16:22:36 -0700473
Shane Farmer280be342017-06-21 15:20:15 -0700474 for (auto* child : root_element->GetChildElements()) {
475 if (child->name != "locale") {
476 diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
477 << child->name);
478 valid = false;
479 } else {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700480 for (auto& node : child->children) {
481 xml::Text* t;
482 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
483 ConfigDescription config_descriptor;
484 const android::StringPiece& text = TrimWhitespace(t->text);
485 bool parsed = ConfigDescription::Parse(text, &config_descriptor);
486 if (parsed &&
487 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
488 android::ResTable_config::CONFIG_LOCALE)) {
489 // Copy the locale with the minimum SDK version stripped out.
490 group.push_back(config_descriptor.CopyWithoutSdkVersion());
491 } else {
492 diag->Error(DiagMessage()
493 << "Could not parse config descriptor for screen-density: " << text);
494 valid = false;
495 }
496 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700497 }
498 }
Shane Farmer280be342017-06-21 15:20:15 -0700499 }
500 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700501
Shane Farmer280be342017-06-21 15:20:15 -0700502 return valid;
503};
Shane Farmer74cdea32017-05-12 16:22:36 -0700504
505ConfigurationParser::ActionHandler ConfigurationParser::android_sdk_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700506 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
507 std::string label = GetLabel(root_element, diag);
508 if (label.empty()) {
509 return false;
510 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700511
Shane Farmer280be342017-06-21 15:20:15 -0700512 bool valid = true;
Shane Farmerefe45392017-08-21 14:39:28 -0700513 bool found = false;
Shane Farmer74cdea32017-05-12 16:22:36 -0700514
Shane Farmer280be342017-06-21 15:20:15 -0700515 for (auto* child : root_element->GetChildElements()) {
516 if (child->name != "android-sdk") {
517 diag->Error(DiagMessage() << "Unexpected root_element in ABI group: " << child->name);
518 valid = false;
519 } else {
520 AndroidSdk entry;
521 for (const auto& attr : child->attributes) {
522 if (attr.name == "minSdkVersion") {
Shane Farmer3edd4722017-09-01 14:34:22 -0700523 entry.min_sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
Shane Farmer280be342017-06-21 15:20:15 -0700524 } else if (attr.name == "targetSdkVersion") {
Shane Farmer3edd4722017-09-01 14:34:22 -0700525 entry.target_sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
Shane Farmer280be342017-06-21 15:20:15 -0700526 } else if (attr.name == "maxSdkVersion") {
Shane Farmer3edd4722017-09-01 14:34:22 -0700527 entry.max_sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
Shane Farmer74cdea32017-05-12 16:22:36 -0700528 } else {
Shane Farmer280be342017-06-21 15:20:15 -0700529 diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name << " = " << attr.value);
Shane Farmer74cdea32017-05-12 16:22:36 -0700530 }
531 }
532
Shane Farmer280be342017-06-21 15:20:15 -0700533 // TODO: Fill in the manifest details when they are finalised.
534 for (auto node : child->GetChildElements()) {
535 if (node->name == "manifest") {
536 if (entry.manifest) {
537 diag->Warn(DiagMessage() << "Found multiple manifest tags. Ignoring duplicates.");
538 continue;
539 }
540 entry.manifest = {AndroidManifest()};
541 }
542 }
543
Shane Farmerefe45392017-08-21 14:39:28 -0700544 config->android_sdk_groups[label] = entry;
545 if (found) {
546 valid = false;
547 }
548 found = true;
Shane Farmer280be342017-06-21 15:20:15 -0700549 }
550 }
551
552 return valid;
553};
Shane Farmer74cdea32017-05-12 16:22:36 -0700554
555ConfigurationParser::ActionHandler ConfigurationParser::gl_texture_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700556 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
557 std::string label = GetLabel(root_element, diag);
558 if (label.empty()) {
559 return false;
560 }
561
562 auto& group = config->gl_texture_groups[label];
563 bool valid = true;
564
565 GlTexture result;
566 for (auto* child : root_element->GetChildElements()) {
567 if (child->name != "gl-texture") {
568 diag->Error(DiagMessage() << "Unexpected element in GL texture group: " << child->name);
569 valid = false;
570 } else {
571 for (const auto& attr : child->attributes) {
572 if (attr.name == "name") {
573 result.name = attr.value;
574 break;
575 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700576 }
577
Shane Farmer280be342017-06-21 15:20:15 -0700578 for (auto* element : child->GetChildElements()) {
579 if (element->name != "texture-path") {
580 diag->Error(DiagMessage() << "Unexpected element in gl-texture element: " << child->name);
Shane Farmer74cdea32017-05-12 16:22:36 -0700581 valid = false;
Shane Farmer280be342017-06-21 15:20:15 -0700582 continue;
583 }
584 for (auto& node : element->children) {
585 xml::Text* t;
586 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
587 result.texture_paths.push_back(TrimWhitespace(t->text).to_string());
Shane Farmer74cdea32017-05-12 16:22:36 -0700588 }
589 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700590 }
Shane Farmer280be342017-06-21 15:20:15 -0700591 }
592 group.push_back(result);
593 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700594
Shane Farmer280be342017-06-21 15:20:15 -0700595 return valid;
596};
Shane Farmer74cdea32017-05-12 16:22:36 -0700597
598ConfigurationParser::ActionHandler ConfigurationParser::device_feature_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700599 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
600 std::string label = GetLabel(root_element, diag);
601 if (label.empty()) {
602 return false;
603 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700604
Shane Farmer280be342017-06-21 15:20:15 -0700605 auto& group = config->device_feature_groups[label];
606 bool valid = true;
Shane Farmer74cdea32017-05-12 16:22:36 -0700607
Shane Farmer280be342017-06-21 15:20:15 -0700608 for (auto* child : root_element->GetChildElements()) {
609 if (child->name != "supports-feature") {
610 diag->Error(DiagMessage() << "Unexpected root_element in device feature group: "
611 << child->name);
612 valid = false;
613 } else {
614 for (auto& node : child->children) {
615 xml::Text* t;
616 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
617 group.push_back(TrimWhitespace(t->text).to_string());
618 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700619 }
620 }
Shane Farmer280be342017-06-21 15:20:15 -0700621 }
622 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700623
Shane Farmer280be342017-06-21 15:20:15 -0700624 return valid;
625};
Shane Farmer74cdea32017-05-12 16:22:36 -0700626
627} // namespace aapt