xref: /aosp_15_r20/frameworks/base/tools/aapt2/optimize/VersionCollapser.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker  * Copyright (C) 2016 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker  *
4*d57664e9SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker  *
8*d57664e9SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker  *
10*d57664e9SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker  * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker  */
16*d57664e9SAndroid Build Coastguard Worker 
17*d57664e9SAndroid Build Coastguard Worker #include "optimize/VersionCollapser.h"
18*d57664e9SAndroid Build Coastguard Worker 
19*d57664e9SAndroid Build Coastguard Worker #include <algorithm>
20*d57664e9SAndroid Build Coastguard Worker #include <vector>
21*d57664e9SAndroid Build Coastguard Worker 
22*d57664e9SAndroid Build Coastguard Worker #include "ResourceTable.h"
23*d57664e9SAndroid Build Coastguard Worker #include "trace/TraceBuffer.h"
24*d57664e9SAndroid Build Coastguard Worker 
25*d57664e9SAndroid Build Coastguard Worker using android::ConfigDescription;
26*d57664e9SAndroid Build Coastguard Worker 
27*d57664e9SAndroid Build Coastguard Worker namespace aapt {
28*d57664e9SAndroid Build Coastguard Worker 
29*d57664e9SAndroid Build Coastguard Worker template <typename Iterator, typename Pred>
30*d57664e9SAndroid Build Coastguard Worker class FilterIterator {
31*d57664e9SAndroid Build Coastguard Worker  public:
FilterIterator(Iterator begin,Iterator end,Pred pred=Pred ())32*d57664e9SAndroid Build Coastguard Worker   FilterIterator(Iterator begin, Iterator end, Pred pred = Pred())
33*d57664e9SAndroid Build Coastguard Worker       : current_(begin), end_(end), pred_(pred) {
34*d57664e9SAndroid Build Coastguard Worker     Advance();
35*d57664e9SAndroid Build Coastguard Worker   }
36*d57664e9SAndroid Build Coastguard Worker 
HasNext()37*d57664e9SAndroid Build Coastguard Worker   bool HasNext() { return current_ != end_; }
38*d57664e9SAndroid Build Coastguard Worker 
NextIter()39*d57664e9SAndroid Build Coastguard Worker   Iterator NextIter() {
40*d57664e9SAndroid Build Coastguard Worker     Iterator iter = current_;
41*d57664e9SAndroid Build Coastguard Worker     ++current_;
42*d57664e9SAndroid Build Coastguard Worker     Advance();
43*d57664e9SAndroid Build Coastguard Worker     return iter;
44*d57664e9SAndroid Build Coastguard Worker   }
45*d57664e9SAndroid Build Coastguard Worker 
Next()46*d57664e9SAndroid Build Coastguard Worker   typename Iterator::reference Next() { return *NextIter(); }
47*d57664e9SAndroid Build Coastguard Worker 
48*d57664e9SAndroid Build Coastguard Worker  private:
Advance()49*d57664e9SAndroid Build Coastguard Worker   void Advance() {
50*d57664e9SAndroid Build Coastguard Worker     for (; current_ != end_; ++current_) {
51*d57664e9SAndroid Build Coastguard Worker       if (pred_(*current_)) {
52*d57664e9SAndroid Build Coastguard Worker         return;
53*d57664e9SAndroid Build Coastguard Worker       }
54*d57664e9SAndroid Build Coastguard Worker     }
55*d57664e9SAndroid Build Coastguard Worker   }
56*d57664e9SAndroid Build Coastguard Worker 
57*d57664e9SAndroid Build Coastguard Worker   Iterator current_, end_;
58*d57664e9SAndroid Build Coastguard Worker   Pred pred_;
59*d57664e9SAndroid Build Coastguard Worker };
60*d57664e9SAndroid Build Coastguard Worker 
61*d57664e9SAndroid Build Coastguard Worker template <typename Iterator, typename Pred>
make_filter_iterator(Iterator begin,Iterator end=Iterator (),Pred pred=Pred ())62*d57664e9SAndroid Build Coastguard Worker FilterIterator<Iterator, Pred> make_filter_iterator(Iterator begin,
63*d57664e9SAndroid Build Coastguard Worker                                                     Iterator end = Iterator(),
64*d57664e9SAndroid Build Coastguard Worker                                                     Pred pred = Pred()) {
65*d57664e9SAndroid Build Coastguard Worker   return FilterIterator<Iterator, Pred>(begin, end, pred);
66*d57664e9SAndroid Build Coastguard Worker }
67*d57664e9SAndroid Build Coastguard Worker 
68*d57664e9SAndroid Build Coastguard Worker /**
69*d57664e9SAndroid Build Coastguard Worker  * Every Configuration with an SDK version specified that is less than minSdk will be removed. The
70*d57664e9SAndroid Build Coastguard Worker  * exception is when there is no exact matching resource for the minSdk. The next smallest one will
71*d57664e9SAndroid Build Coastguard Worker  * be kept.
72*d57664e9SAndroid Build Coastguard Worker  */
CollapseVersions(IAaptContext * context,int min_sdk,ResourceEntry * entry)73*d57664e9SAndroid Build Coastguard Worker static void CollapseVersions(IAaptContext* context, int min_sdk, ResourceEntry* entry) {
74*d57664e9SAndroid Build Coastguard Worker   // First look for all sdks less than minSdk.
75*d57664e9SAndroid Build Coastguard Worker   for (auto iter = entry->values.rbegin(); iter != entry->values.rend();
76*d57664e9SAndroid Build Coastguard Worker        ++iter) {
77*d57664e9SAndroid Build Coastguard Worker     // Check if the item was already marked for removal.
78*d57664e9SAndroid Build Coastguard Worker     if (!(*iter)) {
79*d57664e9SAndroid Build Coastguard Worker       continue;
80*d57664e9SAndroid Build Coastguard Worker     }
81*d57664e9SAndroid Build Coastguard Worker 
82*d57664e9SAndroid Build Coastguard Worker     const ConfigDescription& config = (*iter)->config;
83*d57664e9SAndroid Build Coastguard Worker     if (config.sdkVersion <= min_sdk) {
84*d57664e9SAndroid Build Coastguard Worker       // This is the first configuration we've found with a smaller or equal SDK level to the
85*d57664e9SAndroid Build Coastguard Worker       // minimum. We MUST keep this one, but remove all others we find, which get overridden by this
86*d57664e9SAndroid Build Coastguard Worker       // one.
87*d57664e9SAndroid Build Coastguard Worker 
88*d57664e9SAndroid Build Coastguard Worker       ConfigDescription config_without_sdk = config.CopyWithoutSdkVersion();
89*d57664e9SAndroid Build Coastguard Worker       auto pred = [&](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
90*d57664e9SAndroid Build Coastguard Worker         // Check that the value hasn't already been marked for removal.
91*d57664e9SAndroid Build Coastguard Worker         if (!val) {
92*d57664e9SAndroid Build Coastguard Worker           return false;
93*d57664e9SAndroid Build Coastguard Worker         }
94*d57664e9SAndroid Build Coastguard Worker 
95*d57664e9SAndroid Build Coastguard Worker         // Only return Configs that differ in SDK version.
96*d57664e9SAndroid Build Coastguard Worker         config_without_sdk.sdkVersion = val->config.sdkVersion;
97*d57664e9SAndroid Build Coastguard Worker         return config_without_sdk == val->config &&
98*d57664e9SAndroid Build Coastguard Worker                val->config.sdkVersion <= min_sdk;
99*d57664e9SAndroid Build Coastguard Worker       };
100*d57664e9SAndroid Build Coastguard Worker 
101*d57664e9SAndroid Build Coastguard Worker       // Remove the rest that match.
102*d57664e9SAndroid Build Coastguard Worker       auto filter_iter =
103*d57664e9SAndroid Build Coastguard Worker           make_filter_iterator(iter + 1, entry->values.rend(), pred);
104*d57664e9SAndroid Build Coastguard Worker       while (filter_iter.HasNext()) {
105*d57664e9SAndroid Build Coastguard Worker         auto& next = filter_iter.Next();
106*d57664e9SAndroid Build Coastguard Worker         if (context->IsVerbose()) {
107*d57664e9SAndroid Build Coastguard Worker           context->GetDiagnostics()->Note(android::DiagMessage()
108*d57664e9SAndroid Build Coastguard Worker                                           << "removing configuration " << next->config.to_string()
109*d57664e9SAndroid Build Coastguard Worker                                           << " for entry: " << entry->name
110*d57664e9SAndroid Build Coastguard Worker                                           << ", because its SDK version is smaller than minSdk");
111*d57664e9SAndroid Build Coastguard Worker         }
112*d57664e9SAndroid Build Coastguard Worker         next = {};
113*d57664e9SAndroid Build Coastguard Worker       }
114*d57664e9SAndroid Build Coastguard Worker     }
115*d57664e9SAndroid Build Coastguard Worker   }
116*d57664e9SAndroid Build Coastguard Worker 
117*d57664e9SAndroid Build Coastguard Worker   // Now erase the nullptr values.
118*d57664e9SAndroid Build Coastguard Worker   entry->values.erase(
119*d57664e9SAndroid Build Coastguard Worker       std::remove_if(entry->values.begin(), entry->values.end(),
120*d57664e9SAndroid Build Coastguard Worker                      [](const std::unique_ptr<ResourceConfigValue>& val)
121*d57664e9SAndroid Build Coastguard Worker                          -> bool { return val == nullptr; }),
122*d57664e9SAndroid Build Coastguard Worker       entry->values.end());
123*d57664e9SAndroid Build Coastguard Worker 
124*d57664e9SAndroid Build Coastguard Worker   // Strip the version qualifiers for every resource with version <= minSdk. This will ensure that
125*d57664e9SAndroid Build Coastguard Worker   // the resource entries are all packed together in the same ResTable_type struct and take up less
126*d57664e9SAndroid Build Coastguard Worker   // space in the resources.arsc table.
127*d57664e9SAndroid Build Coastguard Worker   bool modified = false;
128*d57664e9SAndroid Build Coastguard Worker   for (std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
129*d57664e9SAndroid Build Coastguard Worker     if (config_value->config.sdkVersion != 0 &&
130*d57664e9SAndroid Build Coastguard Worker         config_value->config.sdkVersion <= min_sdk) {
131*d57664e9SAndroid Build Coastguard Worker       // Override the resource with a Configuration without an SDK.
132*d57664e9SAndroid Build Coastguard Worker       std::unique_ptr<ResourceConfigValue> new_value =
133*d57664e9SAndroid Build Coastguard Worker           util::make_unique<ResourceConfigValue>(
134*d57664e9SAndroid Build Coastguard Worker               config_value->config.CopyWithoutSdkVersion(),
135*d57664e9SAndroid Build Coastguard Worker               config_value->product);
136*d57664e9SAndroid Build Coastguard Worker       if (context->IsVerbose()) {
137*d57664e9SAndroid Build Coastguard Worker         context->GetDiagnostics()->Note(android::DiagMessage()
138*d57664e9SAndroid Build Coastguard Worker                                         << "overriding resource: " << entry->name
139*d57664e9SAndroid Build Coastguard Worker                                         << ", removing SDK version from configuration "
140*d57664e9SAndroid Build Coastguard Worker                                         << config_value->config.to_string());
141*d57664e9SAndroid Build Coastguard Worker       }
142*d57664e9SAndroid Build Coastguard Worker       new_value->value = std::move(config_value->value);
143*d57664e9SAndroid Build Coastguard Worker       config_value = std::move(new_value);
144*d57664e9SAndroid Build Coastguard Worker 
145*d57664e9SAndroid Build Coastguard Worker       modified = true;
146*d57664e9SAndroid Build Coastguard Worker     }
147*d57664e9SAndroid Build Coastguard Worker   }
148*d57664e9SAndroid Build Coastguard Worker 
149*d57664e9SAndroid Build Coastguard Worker   if (modified) {
150*d57664e9SAndroid Build Coastguard Worker     // We've modified the keys (ConfigDescription) by changing the sdkVersion to 0. We MUST re-sort
151*d57664e9SAndroid Build Coastguard Worker     // to ensure ordering guarantees hold.
152*d57664e9SAndroid Build Coastguard Worker     std::sort(entry->values.begin(), entry->values.end(),
153*d57664e9SAndroid Build Coastguard Worker               [](const std::unique_ptr<ResourceConfigValue>& a,
154*d57664e9SAndroid Build Coastguard Worker                  const std::unique_ptr<ResourceConfigValue>& b) -> bool {
155*d57664e9SAndroid Build Coastguard Worker                 return a->config.compare(b->config) < 0;
156*d57664e9SAndroid Build Coastguard Worker               });
157*d57664e9SAndroid Build Coastguard Worker   }
158*d57664e9SAndroid Build Coastguard Worker }
159*d57664e9SAndroid Build Coastguard Worker 
Consume(IAaptContext * context,ResourceTable * table)160*d57664e9SAndroid Build Coastguard Worker bool VersionCollapser::Consume(IAaptContext* context, ResourceTable* table) {
161*d57664e9SAndroid Build Coastguard Worker   TRACE_NAME("VersionCollapser::Consume");
162*d57664e9SAndroid Build Coastguard Worker   const int min_sdk = context->GetMinSdkVersion();
163*d57664e9SAndroid Build Coastguard Worker   if (context->IsVerbose()) {
164*d57664e9SAndroid Build Coastguard Worker     context->GetDiagnostics()->Note(android::DiagMessage()
165*d57664e9SAndroid Build Coastguard Worker                                     << "Running VersionCollapser with minSdk = " << min_sdk);
166*d57664e9SAndroid Build Coastguard Worker   }
167*d57664e9SAndroid Build Coastguard Worker   for (auto& package : table->packages) {
168*d57664e9SAndroid Build Coastguard Worker     for (auto& type : package->types) {
169*d57664e9SAndroid Build Coastguard Worker       for (auto& entry : type->entries) {
170*d57664e9SAndroid Build Coastguard Worker         CollapseVersions(context, min_sdk, entry.get());
171*d57664e9SAndroid Build Coastguard Worker       }
172*d57664e9SAndroid Build Coastguard Worker     }
173*d57664e9SAndroid Build Coastguard Worker   }
174*d57664e9SAndroid Build Coastguard Worker   return true;
175*d57664e9SAndroid Build Coastguard Worker }
176*d57664e9SAndroid Build Coastguard Worker 
177*d57664e9SAndroid Build Coastguard Worker }  // namespace aapt
178