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