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 "androidfw/ConfigDescription.h"
21 
22 #include "configuration/ConfigurationParser.h"
23 
24 #include <algorithm>
25 #include <limits>
26 
27 namespace aapt {
28 
29 // Forward declaration of classes used in the API.
30 namespace xml {
31 class Element;
32 }
33 
34 namespace configuration {
35 
36 template <typename T>
37 struct OrderedEntry {
38   int32_t order;
39   std::vector<T> entry;
40 };
41 
42 /** A mapping of group label to a single configuration item. */
43 template <class T>
44 using Entry = std::unordered_map<std::string, T>;
45 
46 /** A mapping of group labels to group of configuration items. */
47 template <class T>
48 using Group = Entry<OrderedEntry<T>>;
49 
50 template <typename T>
IsGroupValid(const Group<T> & group,const std::string & name,android::IDiagnostics * diag)51 bool IsGroupValid(const Group<T>& group, const std::string& name, android::IDiagnostics* diag) {
52   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) {
58     diag->Error(android::DiagMessage()
59                 << name << " have overlapping version-code-order attributes");
60   }
61   return valid;
62 }
63 
64 /** Retrieves an entry from the provided Group, creating a new instance if one does not exist. */
65 template <typename T>
GetOrCreateGroup(std::string label,Group<T> * group)66 std::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  */
81 class 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>
Add(const Group<T> & groups,const std::optional<std::string> & lhs,const std::optional<std::string> & rhs)88   ComparisonChain& Add(const Group<T>& groups, const std::optional<std::string>& lhs,
89                        const std::optional<std::string>& rhs) {
90     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    */
Add(int lhs,int rhs)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. */
Compare()106   bool Compare() {
107     return result_;
108   }
109 
110  private:
111   template <typename T>
GetGroupOrder(const Entry<T> & groups,const std::optional<std::string> & label)112   inline size_t GetGroupOrder(const Entry<T>& groups, const std::optional<std::string>& label) {
113     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 
123 /** Output artifact configuration options. */
124 struct ConfiguredArtifact {
125   /** Name to use for output of processing foo.apk -> foo.<name>.apk. */
126   std::optional<std::string> name;
127   /** If present, uses the ABI group with this name. */
128   std::optional<std::string> abi_group;
129   /** If present, uses the screen density group with this name. */
130   std::optional<std::string> screen_density_group;
131   /** If present, uses the locale group with this name. */
132   std::optional<std::string> locale_group;
133   /** If present, uses the Android SDK with this name. */
134   std::optional<std::string> android_sdk;
135   /** If present, uses the device feature group with this name. */
136   std::optional<std::string> device_feature_group;
137   /** If present, uses the OpenGL texture group with this name. */
138   std::optional<std::string> gl_texture_group;
139 
140   /** Convert an artifact name template into a name string based on configuration contents. */
141   std::optional<std::string> ToArtifactName(android::StringPiece format,
142                                             android::StringPiece apk_name,
143                                             android::IDiagnostics* diag) const;
144 
145   /** Convert an artifact name template into a name string based on configuration contents. */
146   std::optional<std::string> Name(android::StringPiece apk_name, android::IDiagnostics* diag) const;
147 };
148 
149 /** AAPT2 XML configuration file binary representation. */
150 struct PostProcessingConfiguration {
151   std::vector<ConfiguredArtifact> artifacts;
152   std::optional<std::string> artifact_format;
153 
154   Group<Abi> abi_groups;
155   Group<android::ConfigDescription> screen_density_groups;
156   Group<android::ConfigDescription> locale_groups;
157   Group<DeviceFeature> device_feature_groups;
158   Group<GlTexture> gl_texture_groups;
159   Entry<AndroidSdk> android_sdks;
160 
ValidateVersionCodeOrderingPostProcessingConfiguration161   bool ValidateVersionCodeOrdering(android::IDiagnostics* diag) {
162     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 
170   /**
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    */
SortArtifactsPostProcessingConfiguration175   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. */
operatorPostProcessingConfiguration180   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    */
GetMinSdkPostProcessingConfiguration204   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   }
214 };
215 
216 /** Parses the provided XML document returning the post processing configuration. */
217 std::optional<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
218                                                                 const std::string& config_path,
219                                                                 android::IDiagnostics* diag);
220 
221 namespace handler {
222 
223 /** Handler for <artifact> tags. */
224 bool ArtifactTagHandler(configuration::PostProcessingConfiguration* config, xml::Element* element,
225                         android::IDiagnostics* diag);
226 
227 /** Handler for <artifact-format> tags. */
228 bool ArtifactFormatTagHandler(configuration::PostProcessingConfiguration* config,
229                               xml::Element* element, android::IDiagnostics* diag);
230 
231 /** Handler for <abi-group> tags. */
232 bool AbiGroupTagHandler(configuration::PostProcessingConfiguration* config, xml::Element* element,
233                         android::IDiagnostics* diag);
234 
235 /** Handler for <screen-density-group> tags. */
236 bool ScreenDensityGroupTagHandler(configuration::PostProcessingConfiguration* config,
237                                   xml::Element* element, android::IDiagnostics* diag);
238 
239 /** Handler for <locale-group> tags. */
240 bool LocaleGroupTagHandler(configuration::PostProcessingConfiguration* config,
241                            xml::Element* element, android::IDiagnostics* diag);
242 
243 /** Handler for <android-sdk> tags. */
244 bool AndroidSdkTagHandler(configuration::PostProcessingConfiguration* config, xml::Element* element,
245                           android::IDiagnostics* diag);
246 
247 /** Handler for <gl-texture-group> tags. */
248 bool GlTextureGroupTagHandler(configuration::PostProcessingConfiguration* config,
249                               xml::Element* element, android::IDiagnostics* diag);
250 
251 /** Handler for <device-feature-group> tags. */
252 bool DeviceFeatureGroupTagHandler(configuration::PostProcessingConfiguration* config,
253                                   xml::Element* element, android::IDiagnostics* diag);
254 
255 }  // namespace handler
256 }  // namespace configuration
257 }  // namespace aapt
258 #endif  // AAPT2_CONFIGURATIONPARSER_INTERNAL_H
259