You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
161 lines
5.2 KiB
161 lines
5.2 KiB
/*
|
|
* 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"
|
|
#include "trace/TraceBuffer.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) {
|
|
TRACE_NAME("VersionCollapser::Consume");
|
|
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
|