1 /*
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
20 #include "configuration/ConfigurationParser.h"
21
22 #include <algorithm>
23 #include <limits>
24
25 namespace aapt {
26
27 // Forward declaration of classes used in the API.
28 namespace xml {
29 class Element;
30 }
31
32 namespace configuration {
33
34 template <typename T>
35 struct OrderedEntry {
36 int32_t order;
37 std::vector<T> entry;
38 };
39
40 /** A mapping of group label to a single configuration item. */
41 template <class T>
42 using Entry = std::unordered_map<std::string, T>;
43
44 /** A mapping of group labels to group of configuration items. */
45 template <class T>
46 using Group = Entry<OrderedEntry<T>>;
47
48 template<typename T>
IsGroupValid(const Group<T> & group,const std::string & name,IDiagnostics * diag)49 bool IsGroupValid(const Group<T>& group, const std::string& name, IDiagnostics* diag) {
50 std::set<int32_t> orders;
51 for (const auto& p : group) {
52 orders.insert(p.second.order);
53 }
54 bool valid = orders.size() == group.size();
55 if (!valid) {
56 diag->Error(DiagMessage() << name << " have overlapping version-code-order attributes");
57 }
58 return valid;
59 }
60
61 /** Retrieves an entry from the provided Group, creating a new instance if one does not exist. */
62 template <typename T>
GetOrCreateGroup(std::string label,Group<T> * group)63 std::vector<T>& GetOrCreateGroup(std::string label, Group<T>* group) {
64 OrderedEntry<T>& entry = (*group)[label];
65 // If this is a new entry, set the order.
66 if (entry.order == 0) {
67 entry.order = group->size();
68 }
69 return entry.entry;
70 }
71
72 /**
73 * A ComparisonChain is a grouping of comparisons to perform when sorting groups that have a well
74 * defined order of precedence. Comparisons are only made if none of the previous comparisons had a
75 * definite result. A comparison has a result if at least one of the items has an entry for that
76 * value and that they are not equal.
77 */
78 class ComparisonChain {
79 public:
80 /**
81 * Adds a new comparison of items in a group to the chain. The new comparison is only used if we
82 * have not been able to determine the sort order with the previous comparisons.
83 */
84 template <typename T>
Add(const Group<T> & groups,const Maybe<std::string> & lhs,const Maybe<std::string> & rhs)85 ComparisonChain& Add(const Group<T>& groups, const Maybe<std::string>& lhs,
86 const Maybe<std::string>& rhs) {
87 return Add(GetGroupOrder(groups, lhs), GetGroupOrder(groups, rhs));
88 }
89
90 /**
91 * Adds a new comparison to the chain. The new comparison is only used if we have not been able to
92 * determine the sort order with the previous comparisons.
93 */
Add(int lhs,int rhs)94 ComparisonChain& Add(int lhs, int rhs) {
95 if (!has_result_) {
96 has_result_ = (lhs != rhs);
97 result_ = (lhs < rhs);
98 }
99 return *this;
100 }
101
102 /** Returns true if the left hand side should come before the right hand side. */
Compare()103 bool Compare() {
104 return result_;
105 }
106
107 private:
108 template <typename T>
GetGroupOrder(const Entry<T> & groups,const Maybe<std::string> & label)109 inline size_t GetGroupOrder(const Entry<T>& groups, const Maybe<std::string>& label) {
110 if (!label) {
111 return std::numeric_limits<size_t>::max();
112 }
113 return groups.at(label.value()).order;
114 }
115
116 bool has_result_ = false;
117 bool result_ = false;
118 };
119
120 /** Output artifact configuration options. */
121 struct ConfiguredArtifact {
122 /** Name to use for output of processing foo.apk -> foo.<name>.apk. */
123 Maybe<std::string> name;
124 /** If present, uses the ABI group with this name. */
125 Maybe<std::string> abi_group;
126 /** If present, uses the screen density group with this name. */
127 Maybe<std::string> screen_density_group;
128 /** If present, uses the locale group with this name. */
129 Maybe<std::string> locale_group;
130 /** If present, uses the Android SDK with this name. */
131 Maybe<std::string> android_sdk;
132 /** If present, uses the device feature group with this name. */
133 Maybe<std::string> device_feature_group;
134 /** If present, uses the OpenGL texture group with this name. */
135 Maybe<std::string> gl_texture_group;
136
137 /** Convert an artifact name template into a name string based on configuration contents. */
138 Maybe<std::string> ToArtifactName(const android::StringPiece& format,
139 const android::StringPiece& apk_name, IDiagnostics* diag) const;
140
141 /** Convert an artifact name template into a name string based on configuration contents. */
142 Maybe<std::string> Name(const android::StringPiece& apk_name, IDiagnostics* diag) const;
143 };
144
145 /** AAPT2 XML configuration file binary representation. */
146 struct PostProcessingConfiguration {
147 std::vector<ConfiguredArtifact> artifacts;
148 Maybe<std::string> artifact_format;
149
150 Group<Abi> abi_groups;
151 Group<ConfigDescription> screen_density_groups;
152 Group<ConfigDescription> locale_groups;
153 Group<DeviceFeature> device_feature_groups;
154 Group<GlTexture> gl_texture_groups;
155 Entry<AndroidSdk> android_sdks;
156
ValidateVersionCodeOrderingPostProcessingConfiguration157 bool ValidateVersionCodeOrdering(IDiagnostics* diag) {
158 bool valid = IsGroupValid(abi_groups, "abi-groups", diag);
159 valid &= IsGroupValid(screen_density_groups, "screen-density-groups", diag);
160 valid &= IsGroupValid(locale_groups, "locale-groups", diag);
161 valid &= IsGroupValid(device_feature_groups, "device-feature-groups", diag);
162 valid &= IsGroupValid(gl_texture_groups, "gl-texture-groups", diag);
163 return valid;
164 }
165
166 /**
167 * Sorts the configured artifacts based on the ordering of the groups in the configuration file.
168 * The only exception to this rule is Android SDK versions. Larger SDK versions will have a larger
169 * versionCode to ensure users get the correct APK when they upgrade their OS.
170 */
SortArtifactsPostProcessingConfiguration171 void SortArtifacts() {
172 std::sort(artifacts.begin(), artifacts.end(), *this);
173 }
174
175 /** Comparator that ensures artifacts are in the preferred order for versionCode rewriting. */
operatorPostProcessingConfiguration176 bool operator()(const ConfiguredArtifact& lhs, const ConfiguredArtifact& rhs) {
177 // Split dimensions are added in the order of precedence. Items higher in the list result in
178 // higher version codes.
179 return ComparisonChain()
180 // All splits with a minSdkVersion specified must be last to ensure the application will be
181 // updated if a user upgrades the version of Android on their device.
182 .Add(GetMinSdk(lhs), GetMinSdk(rhs))
183 // ABI version is important, especially on x86 phones where they may begin to run in ARM
184 // emulation mode on newer Android versions. This allows us to ensure that the x86 version
185 // is installed on these devices rather than ARM.
186 .Add(abi_groups, lhs.abi_group, rhs.abi_group)
187 // The rest are in arbitrary order based on estimated usage.
188 .Add(screen_density_groups, lhs.screen_density_group, rhs.screen_density_group)
189 .Add(locale_groups, lhs.locale_group, rhs.locale_group)
190 .Add(gl_texture_groups, lhs.gl_texture_group, rhs.gl_texture_group)
191 .Add(device_feature_groups, lhs.device_feature_group, rhs.device_feature_group)
192 .Compare();
193 }
194
195 private:
196 /**
197 * Returns the min_sdk_version from the provided artifact or 0 if none is present. This allows
198 * artifacts that have an Android SDK version to have a higher versionCode than those that do not.
199 */
GetMinSdkPostProcessingConfiguration200 inline int GetMinSdk(const ConfiguredArtifact& artifact) {
201 if (!artifact.android_sdk) {
202 return 0;
203 }
204 const auto& entry = android_sdks.find(artifact.android_sdk.value());
205 if (entry == android_sdks.end()) {
206 return 0;
207 }
208 return entry->second.min_sdk_version;
209 }
210 };
211
212 /** Parses the provided XML document returning the post processing configuration. */
213 Maybe<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
214 const std::string& config_path,
215 IDiagnostics* diag);
216
217 namespace handler {
218
219 /** Handler for <artifact> tags. */
220 bool ArtifactTagHandler(configuration::PostProcessingConfiguration* config, xml::Element* element,
221 IDiagnostics* diag);
222
223 /** Handler for <artifact-format> tags. */
224 bool ArtifactFormatTagHandler(configuration::PostProcessingConfiguration* config,
225 xml::Element* element, IDiagnostics* diag);
226
227 /** Handler for <abi-group> tags. */
228 bool AbiGroupTagHandler(configuration::PostProcessingConfiguration* config, xml::Element* element,
229 IDiagnostics* diag);
230
231 /** Handler for <screen-density-group> tags. */
232 bool ScreenDensityGroupTagHandler(configuration::PostProcessingConfiguration* config,
233 xml::Element* element, IDiagnostics* diag);
234
235 /** Handler for <locale-group> tags. */
236 bool LocaleGroupTagHandler(configuration::PostProcessingConfiguration* config,
237 xml::Element* element, IDiagnostics* diag);
238
239 /** Handler for <android-sdk> tags. */
240 bool AndroidSdkTagHandler(configuration::PostProcessingConfiguration* config, xml::Element* element,
241 IDiagnostics* diag);
242
243 /** Handler for <gl-texture-group> tags. */
244 bool GlTextureGroupTagHandler(configuration::PostProcessingConfiguration* config,
245 xml::Element* element, IDiagnostics* diag);
246
247 /** Handler for <device-feature-group> tags. */
248 bool DeviceFeatureGroupTagHandler(configuration::PostProcessingConfiguration* config,
249 xml::Element* element, IDiagnostics* diag);
250
251 } // namespace handler
252 } // namespace configuration
253 } // namespace aapt
254 #endif // AAPT2_CONFIGURATIONPARSER_INTERNAL_H
255