blob: f9856047ac403bf1fb426d5c4f04d3f0cc12fb4c [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "optimize/VersionCollapser.h"
#include <algorithm>
#include <vector>
#include "ResourceTable.h"
using android::ConfigDescription;
namespace aapt {
template <typename Iterator, typename Pred>
class FilterIterator {
public:
FilterIterator(Iterator begin, Iterator end, Pred pred = Pred())
: current_(begin), end_(end), pred_(pred) {
Advance();
}
bool HasNext() { return current_ != end_; }
Iterator NextIter() {
Iterator iter = current_;
++current_;
Advance();
return iter;
}
typename Iterator::reference Next() { return *NextIter(); }
private:
void Advance() {
for (; current_ != end_; ++current_) {
if (pred_(*current_)) {
return;
}
}
}
Iterator current_, end_;
Pred pred_;
};
template <typename Iterator, typename Pred>
FilterIterator<Iterator, Pred> make_filter_iterator(Iterator begin,
Iterator end = Iterator(),
Pred pred = Pred()) {
return FilterIterator<Iterator, Pred>(begin, end, pred);
}
/**
* Every Configuration with an SDK version specified that is less than minSdk will be removed. The
* exception is when there is no exact matching resource for the minSdk. The next smallest one will
* be kept.
*/
static void CollapseVersions(int min_sdk, ResourceEntry* entry) {
// First look for all sdks less than minSdk.
for (auto iter = entry->values.rbegin(); iter != entry->values.rend();
++iter) {
// Check if the item was already marked for removal.
if (!(*iter)) {
continue;
}
const ConfigDescription& config = (*iter)->config;
if (config.sdkVersion <= min_sdk) {
// This is the first configuration we've found with a smaller or equal SDK level to the
// minimum. We MUST keep this one, but remove all others we find, which get overridden by this
// one.
ConfigDescription config_without_sdk = config.CopyWithoutSdkVersion();
auto pred = [&](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
// Check that the value hasn't already been marked for removal.
if (!val) {
return false;
}
// Only return Configs that differ in SDK version.
config_without_sdk.sdkVersion = val->config.sdkVersion;
return config_without_sdk == val->config &&
val->config.sdkVersion <= min_sdk;
};
// Remove the rest that match.
auto filter_iter =
make_filter_iterator(iter + 1, entry->values.rend(), pred);
while (filter_iter.HasNext()) {
filter_iter.Next() = {};
}
}
}
// Now erase the nullptr values.
entry->values.erase(
std::remove_if(entry->values.begin(), entry->values.end(),
[](const std::unique_ptr<ResourceConfigValue>& val)
-> bool { return val == nullptr; }),
entry->values.end());
// Strip the version qualifiers for every resource with version <= minSdk. This will ensure that
// the resource entries are all packed together in the same ResTable_type struct and take up less
// space in the resources.arsc table.
bool modified = false;
for (std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
if (config_value->config.sdkVersion != 0 &&
config_value->config.sdkVersion <= min_sdk) {
// Override the resource with a Configuration without an SDK.
std::unique_ptr<ResourceConfigValue> new_value =
util::make_unique<ResourceConfigValue>(
config_value->config.CopyWithoutSdkVersion(),
config_value->product);
new_value->value = std::move(config_value->value);
config_value = std::move(new_value);
modified = true;
}
}
if (modified) {
// We've modified the keys (ConfigDescription) by changing the sdkVersion to 0. We MUST re-sort
// to ensure ordering guarantees hold.
std::sort(entry->values.begin(), entry->values.end(),
[](const std::unique_ptr<ResourceConfigValue>& a,
const std::unique_ptr<ResourceConfigValue>& b) -> bool {
return a->config.compare(b->config) < 0;
});
}
}
bool VersionCollapser::Consume(IAaptContext* context, ResourceTable* table) {
const int min_sdk = context->GetMinSdkVersion();
for (auto& package : table->packages) {
for (auto& type : package->types) {
for (auto& entry : type->entries) {
CollapseVersions(min_sdk, entry.get());
}
}
}
return true;
}
} // namespace aapt