blob: 198f730f1e12d6c1288479ebb5110cfed7f178a4 [file] [log] [blame]
Shane Farmercb6c3f92017-11-27 13:19:36 -08001/*
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#ifndef AAPT2_CONFIGURATIONPARSER_INTERNAL_H
18#define AAPT2_CONFIGURATIONPARSER_INTERNAL_H
19
Mårten Kongstad24c9aa62018-06-20 08:46:41 +020020#include "androidfw/ConfigDescription.h"
21
Shane Farmer78c43d72017-12-04 09:08:38 -080022#include "configuration/ConfigurationParser.h"
23
24#include <algorithm>
25#include <limits>
26
Shane Farmercb6c3f92017-11-27 13:19:36 -080027namespace aapt {
Shane Farmer78c43d72017-12-04 09:08:38 -080028
29// Forward declaration of classes used in the API.
30namespace xml {
31class Element;
32}
33
Shane Farmercb6c3f92017-11-27 13:19:36 -080034namespace configuration {
35
Shane Farmer78c43d72017-12-04 09:08:38 -080036template <typename T>
37struct OrderedEntry {
Shane Farmer11cdc1c2018-01-31 16:43:24 -080038 int32_t order;
Shane Farmer78c43d72017-12-04 09:08:38 -080039 std::vector<T> entry;
40};
41
Shane Farmercb6c3f92017-11-27 13:19:36 -080042/** A mapping of group label to a single configuration item. */
43template <class T>
44using Entry = std::unordered_map<std::string, T>;
45
Shane Farmer11cdc1c2018-01-31 16:43:24 -080046/** A mapping of group labels to group of configuration items. */
47template <class T>
48using Group = Entry<OrderedEntry<T>>;
49
Jeremy Meyer56f36e82022-05-20 20:35:42 +000050template <typename T>
51bool IsGroupValid(const Group<T>& group, const std::string& name, android::IDiagnostics* diag) {
Shane Farmer11cdc1c2018-01-31 16:43:24 -080052 std::set<int32_t> orders;
53 for (const auto& p : group) {
54 orders.insert(p.second.order);
55 }
56 bool valid = orders.size() == group.size();
57 if (!valid) {
Jeremy Meyer56f36e82022-05-20 20:35:42 +000058 diag->Error(android::DiagMessage()
59 << name << " have overlapping version-code-order attributes");
Shane Farmer11cdc1c2018-01-31 16:43:24 -080060 }
61 return valid;
62}
63
Shane Farmer78c43d72017-12-04 09:08:38 -080064/** Retrieves an entry from the provided Group, creating a new instance if one does not exist. */
65template <typename T>
66std::vector<T>& GetOrCreateGroup(std::string label, Group<T>* group) {
67 OrderedEntry<T>& entry = (*group)[label];
68 // If this is a new entry, set the order.
69 if (entry.order == 0) {
70 entry.order = group->size();
71 }
72 return entry.entry;
73}
74
75/**
76 * A ComparisonChain is a grouping of comparisons to perform when sorting groups that have a well
77 * defined order of precedence. Comparisons are only made if none of the previous comparisons had a
78 * definite result. A comparison has a result if at least one of the items has an entry for that
79 * value and that they are not equal.
80 */
81class ComparisonChain {
82 public:
83 /**
84 * Adds a new comparison of items in a group to the chain. The new comparison is only used if we
85 * have not been able to determine the sort order with the previous comparisons.
86 */
87 template <typename T>
Ryan Mitchell4382e442021-07-14 12:53:01 -070088 ComparisonChain& Add(const Group<T>& groups, const std::optional<std::string>& lhs,
89 const std::optional<std::string>& rhs) {
Shane Farmer78c43d72017-12-04 09:08:38 -080090 return Add(GetGroupOrder(groups, lhs), GetGroupOrder(groups, rhs));
91 }
92
93 /**
94 * Adds a new comparison to the chain. The new comparison is only used if we have not been able to
95 * determine the sort order with the previous comparisons.
96 */
97 ComparisonChain& Add(int lhs, int rhs) {
98 if (!has_result_) {
99 has_result_ = (lhs != rhs);
100 result_ = (lhs < rhs);
101 }
102 return *this;
103 }
104
105 /** Returns true if the left hand side should come before the right hand side. */
106 bool Compare() {
107 return result_;
108 }
109
110 private:
111 template <typename T>
Ryan Mitchell4382e442021-07-14 12:53:01 -0700112 inline size_t GetGroupOrder(const Entry<T>& groups, const std::optional<std::string>& label) {
Shane Farmer78c43d72017-12-04 09:08:38 -0800113 if (!label) {
114 return std::numeric_limits<size_t>::max();
115 }
116 return groups.at(label.value()).order;
117 }
118
119 bool has_result_ = false;
120 bool result_ = false;
121};
122
Shane Farmercb6c3f92017-11-27 13:19:36 -0800123/** Output artifact configuration options. */
124struct ConfiguredArtifact {
125 /** Name to use for output of processing foo.apk -> foo.<name>.apk. */
Ryan Mitchell4382e442021-07-14 12:53:01 -0700126 std::optional<std::string> name;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800127 /** If present, uses the ABI group with this name. */
Ryan Mitchell4382e442021-07-14 12:53:01 -0700128 std::optional<std::string> abi_group;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800129 /** If present, uses the screen density group with this name. */
Ryan Mitchell4382e442021-07-14 12:53:01 -0700130 std::optional<std::string> screen_density_group;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800131 /** If present, uses the locale group with this name. */
Ryan Mitchell4382e442021-07-14 12:53:01 -0700132 std::optional<std::string> locale_group;
Shane Farmer78c43d72017-12-04 09:08:38 -0800133 /** If present, uses the Android SDK with this name. */
Ryan Mitchell4382e442021-07-14 12:53:01 -0700134 std::optional<std::string> android_sdk;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800135 /** If present, uses the device feature group with this name. */
Ryan Mitchell4382e442021-07-14 12:53:01 -0700136 std::optional<std::string> device_feature_group;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800137 /** If present, uses the OpenGL texture group with this name. */
Ryan Mitchell4382e442021-07-14 12:53:01 -0700138 std::optional<std::string> gl_texture_group;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800139
140 /** Convert an artifact name template into a name string based on configuration contents. */
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700141 std::optional<std::string> ToArtifactName(android::StringPiece format,
142 android::StringPiece apk_name,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000143 android::IDiagnostics* diag) const;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800144
145 /** Convert an artifact name template into a name string based on configuration contents. */
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700146 std::optional<std::string> Name(android::StringPiece apk_name, android::IDiagnostics* diag) const;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800147};
148
149/** AAPT2 XML configuration file binary representation. */
150struct PostProcessingConfiguration {
Shane Farmercb6c3f92017-11-27 13:19:36 -0800151 std::vector<ConfiguredArtifact> artifacts;
Ryan Mitchell4382e442021-07-14 12:53:01 -0700152 std::optional<std::string> artifact_format;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800153
154 Group<Abi> abi_groups;
Mårten Kongstad24c9aa62018-06-20 08:46:41 +0200155 Group<android::ConfigDescription> screen_density_groups;
156 Group<android::ConfigDescription> locale_groups;
Shane Farmercb6c3f92017-11-27 13:19:36 -0800157 Group<DeviceFeature> device_feature_groups;
158 Group<GlTexture> gl_texture_groups;
Shane Farmer78c43d72017-12-04 09:08:38 -0800159 Entry<AndroidSdk> android_sdks;
160
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000161 bool ValidateVersionCodeOrdering(android::IDiagnostics* diag) {
Shane Farmer11cdc1c2018-01-31 16:43:24 -0800162 bool valid = IsGroupValid(abi_groups, "abi-groups", diag);
163 valid &= IsGroupValid(screen_density_groups, "screen-density-groups", diag);
164 valid &= IsGroupValid(locale_groups, "locale-groups", diag);
165 valid &= IsGroupValid(device_feature_groups, "device-feature-groups", diag);
166 valid &= IsGroupValid(gl_texture_groups, "gl-texture-groups", diag);
167 return valid;
168 }
169
Shane Farmer78c43d72017-12-04 09:08:38 -0800170 /**
171 * Sorts the configured artifacts based on the ordering of the groups in the configuration file.
172 * The only exception to this rule is Android SDK versions. Larger SDK versions will have a larger
173 * versionCode to ensure users get the correct APK when they upgrade their OS.
174 */
175 void SortArtifacts() {
176 std::sort(artifacts.begin(), artifacts.end(), *this);
177 }
178
179 /** Comparator that ensures artifacts are in the preferred order for versionCode rewriting. */
180 bool operator()(const ConfiguredArtifact& lhs, const ConfiguredArtifact& rhs) {
181 // Split dimensions are added in the order of precedence. Items higher in the list result in
182 // higher version codes.
183 return ComparisonChain()
184 // All splits with a minSdkVersion specified must be last to ensure the application will be
185 // updated if a user upgrades the version of Android on their device.
186 .Add(GetMinSdk(lhs), GetMinSdk(rhs))
187 // ABI version is important, especially on x86 phones where they may begin to run in ARM
188 // emulation mode on newer Android versions. This allows us to ensure that the x86 version
189 // is installed on these devices rather than ARM.
190 .Add(abi_groups, lhs.abi_group, rhs.abi_group)
191 // The rest are in arbitrary order based on estimated usage.
192 .Add(screen_density_groups, lhs.screen_density_group, rhs.screen_density_group)
193 .Add(locale_groups, lhs.locale_group, rhs.locale_group)
194 .Add(gl_texture_groups, lhs.gl_texture_group, rhs.gl_texture_group)
195 .Add(device_feature_groups, lhs.device_feature_group, rhs.device_feature_group)
196 .Compare();
197 }
198
199 private:
200 /**
201 * Returns the min_sdk_version from the provided artifact or 0 if none is present. This allows
202 * artifacts that have an Android SDK version to have a higher versionCode than those that do not.
203 */
204 inline int GetMinSdk(const ConfiguredArtifact& artifact) {
205 if (!artifact.android_sdk) {
206 return 0;
207 }
208 const auto& entry = android_sdks.find(artifact.android_sdk.value());
209 if (entry == android_sdks.end()) {
210 return 0;
211 }
212 return entry->second.min_sdk_version;
213 }
Shane Farmercb6c3f92017-11-27 13:19:36 -0800214};
215
Shane Farmer78c43d72017-12-04 09:08:38 -0800216/** Parses the provided XML document returning the post processing configuration. */
Ryan Mitchell4382e442021-07-14 12:53:01 -0700217std::optional<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
218 const std::string& config_path,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000219 android::IDiagnostics* diag);
Shane Farmer78c43d72017-12-04 09:08:38 -0800220
Shane Farmercb6c3f92017-11-27 13:19:36 -0800221namespace handler {
222
223/** Handler for <artifact> tags. */
224bool ArtifactTagHandler(configuration::PostProcessingConfiguration* config, xml::Element* element,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000225 android::IDiagnostics* diag);
Shane Farmercb6c3f92017-11-27 13:19:36 -0800226
227/** Handler for <artifact-format> tags. */
228bool ArtifactFormatTagHandler(configuration::PostProcessingConfiguration* config,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000229 xml::Element* element, android::IDiagnostics* diag);
Shane Farmercb6c3f92017-11-27 13:19:36 -0800230
231/** Handler for <abi-group> tags. */
232bool AbiGroupTagHandler(configuration::PostProcessingConfiguration* config, xml::Element* element,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000233 android::IDiagnostics* diag);
Shane Farmercb6c3f92017-11-27 13:19:36 -0800234
235/** Handler for <screen-density-group> tags. */
236bool ScreenDensityGroupTagHandler(configuration::PostProcessingConfiguration* config,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000237 xml::Element* element, android::IDiagnostics* diag);
Shane Farmercb6c3f92017-11-27 13:19:36 -0800238
239/** Handler for <locale-group> tags. */
240bool LocaleGroupTagHandler(configuration::PostProcessingConfiguration* config,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000241 xml::Element* element, android::IDiagnostics* diag);
Shane Farmercb6c3f92017-11-27 13:19:36 -0800242
Shane Farmer78c43d72017-12-04 09:08:38 -0800243/** Handler for <android-sdk> tags. */
244bool AndroidSdkTagHandler(configuration::PostProcessingConfiguration* config, xml::Element* element,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000245 android::IDiagnostics* diag);
Shane Farmercb6c3f92017-11-27 13:19:36 -0800246
247/** Handler for <gl-texture-group> tags. */
248bool GlTextureGroupTagHandler(configuration::PostProcessingConfiguration* config,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000249 xml::Element* element, android::IDiagnostics* diag);
Shane Farmercb6c3f92017-11-27 13:19:36 -0800250
251/** Handler for <device-feature-group> tags. */
252bool DeviceFeatureGroupTagHandler(configuration::PostProcessingConfiguration* config,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000253 xml::Element* element, android::IDiagnostics* diag);
Shane Farmercb6c3f92017-11-27 13:19:36 -0800254
255} // namespace handler
256} // namespace configuration
257} // namespace aapt
258#endif // AAPT2_CONFIGURATIONPARSER_INTERNAL_H