Home | History | Annotate | Download | only in configuration
      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>
     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>
     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>
     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    */
     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. */
    103   bool Compare() {
    104     return result_;
    105   }
    106 
    107  private:
    108   template <typename T>
    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 
    157   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    */
    171   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. */
    176   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    */
    200   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