Home | History | Annotate | Download | only in optimize
      1 /*
      2  * Copyright (C) 2016 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 #include "optimize/VersionCollapser.h"
     18 
     19 #include <algorithm>
     20 #include <vector>
     21 
     22 #include "ResourceTable.h"
     23 #include "trace/TraceBuffer.h"
     24 
     25 using android::ConfigDescription;
     26 
     27 namespace aapt {
     28 
     29 template <typename Iterator, typename Pred>
     30 class FilterIterator {
     31  public:
     32   FilterIterator(Iterator begin, Iterator end, Pred pred = Pred())
     33       : current_(begin), end_(end), pred_(pred) {
     34     Advance();
     35   }
     36 
     37   bool HasNext() { return current_ != end_; }
     38 
     39   Iterator NextIter() {
     40     Iterator iter = current_;
     41     ++current_;
     42     Advance();
     43     return iter;
     44   }
     45 
     46   typename Iterator::reference Next() { return *NextIter(); }
     47 
     48  private:
     49   void Advance() {
     50     for (; current_ != end_; ++current_) {
     51       if (pred_(*current_)) {
     52         return;
     53       }
     54     }
     55   }
     56 
     57   Iterator current_, end_;
     58   Pred pred_;
     59 };
     60 
     61 template <typename Iterator, typename Pred>
     62 FilterIterator<Iterator, Pred> make_filter_iterator(Iterator begin,
     63                                                     Iterator end = Iterator(),
     64                                                     Pred pred = Pred()) {
     65   return FilterIterator<Iterator, Pred>(begin, end, pred);
     66 }
     67 
     68 /**
     69  * Every Configuration with an SDK version specified that is less than minSdk will be removed. The
     70  * exception is when there is no exact matching resource for the minSdk. The next smallest one will
     71  * be kept.
     72  */
     73 static void CollapseVersions(int min_sdk, ResourceEntry* entry) {
     74   // First look for all sdks less than minSdk.
     75   for (auto iter = entry->values.rbegin(); iter != entry->values.rend();
     76        ++iter) {
     77     // Check if the item was already marked for removal.
     78     if (!(*iter)) {
     79       continue;
     80     }
     81 
     82     const ConfigDescription& config = (*iter)->config;
     83     if (config.sdkVersion <= min_sdk) {
     84       // This is the first configuration we've found with a smaller or equal SDK level to the
     85       // minimum. We MUST keep this one, but remove all others we find, which get overridden by this
     86       // one.
     87 
     88       ConfigDescription config_without_sdk = config.CopyWithoutSdkVersion();
     89       auto pred = [&](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
     90         // Check that the value hasn't already been marked for removal.
     91         if (!val) {
     92           return false;
     93         }
     94 
     95         // Only return Configs that differ in SDK version.
     96         config_without_sdk.sdkVersion = val->config.sdkVersion;
     97         return config_without_sdk == val->config &&
     98                val->config.sdkVersion <= min_sdk;
     99       };
    100 
    101       // Remove the rest that match.
    102       auto filter_iter =
    103           make_filter_iterator(iter + 1, entry->values.rend(), pred);
    104       while (filter_iter.HasNext()) {
    105         filter_iter.Next() = {};
    106       }
    107     }
    108   }
    109 
    110   // Now erase the nullptr values.
    111   entry->values.erase(
    112       std::remove_if(entry->values.begin(), entry->values.end(),
    113                      [](const std::unique_ptr<ResourceConfigValue>& val)
    114                          -> bool { return val == nullptr; }),
    115       entry->values.end());
    116 
    117   // Strip the version qualifiers for every resource with version <= minSdk. This will ensure that
    118   // the resource entries are all packed together in the same ResTable_type struct and take up less
    119   // space in the resources.arsc table.
    120   bool modified = false;
    121   for (std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
    122     if (config_value->config.sdkVersion != 0 &&
    123         config_value->config.sdkVersion <= min_sdk) {
    124       // Override the resource with a Configuration without an SDK.
    125       std::unique_ptr<ResourceConfigValue> new_value =
    126           util::make_unique<ResourceConfigValue>(
    127               config_value->config.CopyWithoutSdkVersion(),
    128               config_value->product);
    129       new_value->value = std::move(config_value->value);
    130       config_value = std::move(new_value);
    131 
    132       modified = true;
    133     }
    134   }
    135 
    136   if (modified) {
    137     // We've modified the keys (ConfigDescription) by changing the sdkVersion to 0. We MUST re-sort
    138     // to ensure ordering guarantees hold.
    139     std::sort(entry->values.begin(), entry->values.end(),
    140               [](const std::unique_ptr<ResourceConfigValue>& a,
    141                  const std::unique_ptr<ResourceConfigValue>& b) -> bool {
    142                 return a->config.compare(b->config) < 0;
    143               });
    144   }
    145 }
    146 
    147 bool VersionCollapser::Consume(IAaptContext* context, ResourceTable* table) {
    148   TRACE_NAME("VersionCollapser::Consume");
    149   const int min_sdk = context->GetMinSdkVersion();
    150   for (auto& package : table->packages) {
    151     for (auto& type : package->types) {
    152       for (auto& entry : type->entries) {
    153         CollapseVersions(min_sdk, entry.get());
    154       }
    155     }
    156   }
    157   return true;
    158 }
    159 
    160 }  // namespace aapt
    161