blob: 949d656f44a099899c74b08da864af0a5568efdd [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 "ResourceTable.h"
#include "link/Linkers.h"
#include <algorithm>
#include <vector>
namespace aapt {
template <typename Iterator, typename Pred>
class FilterIterator {
public:
FilterIterator(Iterator begin, Iterator end, Pred pred=Pred()) :
mCurrent(begin), mEnd(end), mPred(pred) {
advance();
}
bool hasNext() {
return mCurrent != mEnd;
}
Iterator nextIter() {
Iterator iter = mCurrent;
++mCurrent;
advance();
return iter;
}
typename Iterator::reference next() {
return *nextIter();
}
private:
void advance() {
for (; mCurrent != mEnd; ++mCurrent) {
if (mPred(*mCurrent)) {
return;
}
}
}
Iterator mCurrent, mEnd;
Pred mPred;
};
template <typename Iterator, typename Pred>
FilterIterator<Iterator, Pred> makeFilterIterator(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 minSdk, 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 <= minSdk) {
// 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 configWithoutSdk = config;
configWithoutSdk.sdkVersion = 0;
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.
configWithoutSdk.sdkVersion = val->config.sdkVersion;
return configWithoutSdk == val->config && val->config.sdkVersion <= minSdk;
};
// Remove the rest that match.
auto filterIter = makeFilterIterator(iter + 1, entry->values.rend(), pred);
while (filterIter.hasNext()) {
filterIter.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>& configValue : entry->values) {
if (configValue->config.sdkVersion != 0 && configValue->config.sdkVersion <= minSdk) {
// Override the resource with a Configuration without an SDK.
std::unique_ptr<ResourceConfigValue> newValue = util::make_unique<ResourceConfigValue>(
configValue->config.copyWithoutSdkVersion(), configValue->product);
newValue->value = std::move(configValue->value);
configValue = std::move(newValue);
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 minSdk = context->getMinSdkVersion();
for (auto& package : table->packages) {
for (auto& type : package->types) {
for (auto& entry : type->entries) {
collapseVersions(minSdk, entry.get());
}
}
}
return true;
}
} // namespace aapt