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 "split/TableSplitter.h"
18*d57664e9SAndroid Build Coastguard Worker
19*d57664e9SAndroid Build Coastguard Worker #include <algorithm>
20*d57664e9SAndroid Build Coastguard Worker #include <map>
21*d57664e9SAndroid Build Coastguard Worker #include <set>
22*d57664e9SAndroid Build Coastguard Worker #include <unordered_set>
23*d57664e9SAndroid Build Coastguard Worker #include <unordered_map>
24*d57664e9SAndroid Build Coastguard Worker #include <vector>
25*d57664e9SAndroid Build Coastguard Worker
26*d57664e9SAndroid Build Coastguard Worker #include "android-base/logging.h"
27*d57664e9SAndroid Build Coastguard Worker #include "androidfw/ConfigDescription.h"
28*d57664e9SAndroid Build Coastguard Worker
29*d57664e9SAndroid Build Coastguard Worker #include "ResourceTable.h"
30*d57664e9SAndroid Build Coastguard Worker #include "trace/TraceBuffer.h"
31*d57664e9SAndroid Build Coastguard Worker #include "util/Util.h"
32*d57664e9SAndroid Build Coastguard Worker
33*d57664e9SAndroid Build Coastguard Worker using ::android::ConfigDescription;
34*d57664e9SAndroid Build Coastguard Worker
35*d57664e9SAndroid Build Coastguard Worker namespace aapt {
36*d57664e9SAndroid Build Coastguard Worker
37*d57664e9SAndroid Build Coastguard Worker using ConfigClaimedMap = std::unordered_map<ResourceConfigValue*, bool>;
38*d57664e9SAndroid Build Coastguard Worker using ConfigDensityGroups = std::map<ConfigDescription, std::vector<ResourceConfigValue*>>;
39*d57664e9SAndroid Build Coastguard Worker
CopyWithoutDensity(const ConfigDescription & config)40*d57664e9SAndroid Build Coastguard Worker static ConfigDescription CopyWithoutDensity(const ConfigDescription& config) {
41*d57664e9SAndroid Build Coastguard Worker ConfigDescription without_density = config;
42*d57664e9SAndroid Build Coastguard Worker without_density.density = 0;
43*d57664e9SAndroid Build Coastguard Worker return without_density;
44*d57664e9SAndroid Build Coastguard Worker }
45*d57664e9SAndroid Build Coastguard Worker
46*d57664e9SAndroid Build Coastguard Worker /**
47*d57664e9SAndroid Build Coastguard Worker * Selects values that match exactly the constraints given.
48*d57664e9SAndroid Build Coastguard Worker */
49*d57664e9SAndroid Build Coastguard Worker class SplitValueSelector {
50*d57664e9SAndroid Build Coastguard Worker public:
SplitValueSelector(const SplitConstraints & constraints)51*d57664e9SAndroid Build Coastguard Worker explicit SplitValueSelector(const SplitConstraints& constraints) {
52*d57664e9SAndroid Build Coastguard Worker for (const ConfigDescription& config : constraints.configs) {
53*d57664e9SAndroid Build Coastguard Worker if (config.density == 0) {
54*d57664e9SAndroid Build Coastguard Worker density_independent_configs_.insert(config);
55*d57664e9SAndroid Build Coastguard Worker } else {
56*d57664e9SAndroid Build Coastguard Worker density_dependent_config_to_density_map_[CopyWithoutDensity(config)] = config.density;
57*d57664e9SAndroid Build Coastguard Worker }
58*d57664e9SAndroid Build Coastguard Worker }
59*d57664e9SAndroid Build Coastguard Worker }
60*d57664e9SAndroid Build Coastguard Worker
SelectValues(const ConfigDensityGroups & density_groups,ConfigClaimedMap * claimed_values)61*d57664e9SAndroid Build Coastguard Worker std::vector<ResourceConfigValue*> SelectValues(
62*d57664e9SAndroid Build Coastguard Worker const ConfigDensityGroups& density_groups,
63*d57664e9SAndroid Build Coastguard Worker ConfigClaimedMap* claimed_values) {
64*d57664e9SAndroid Build Coastguard Worker std::vector<ResourceConfigValue*> selected;
65*d57664e9SAndroid Build Coastguard Worker
66*d57664e9SAndroid Build Coastguard Worker // Select the regular values.
67*d57664e9SAndroid Build Coastguard Worker for (auto& entry : *claimed_values) {
68*d57664e9SAndroid Build Coastguard Worker // Check if the entry has a density.
69*d57664e9SAndroid Build Coastguard Worker ResourceConfigValue* config_value = entry.first;
70*d57664e9SAndroid Build Coastguard Worker if (config_value->config.density == 0 && !entry.second) {
71*d57664e9SAndroid Build Coastguard Worker // This is still available.
72*d57664e9SAndroid Build Coastguard Worker if (density_independent_configs_.find(config_value->config) !=
73*d57664e9SAndroid Build Coastguard Worker density_independent_configs_.end()) {
74*d57664e9SAndroid Build Coastguard Worker selected.push_back(config_value);
75*d57664e9SAndroid Build Coastguard Worker
76*d57664e9SAndroid Build Coastguard Worker // Mark the entry as taken.
77*d57664e9SAndroid Build Coastguard Worker entry.second = true;
78*d57664e9SAndroid Build Coastguard Worker }
79*d57664e9SAndroid Build Coastguard Worker }
80*d57664e9SAndroid Build Coastguard Worker }
81*d57664e9SAndroid Build Coastguard Worker
82*d57664e9SAndroid Build Coastguard Worker // Now examine the densities
83*d57664e9SAndroid Build Coastguard Worker for (auto& entry : density_groups) {
84*d57664e9SAndroid Build Coastguard Worker // We do not care if the value is claimed, since density values can be
85*d57664e9SAndroid Build Coastguard Worker // in multiple splits.
86*d57664e9SAndroid Build Coastguard Worker const ConfigDescription& config = entry.first;
87*d57664e9SAndroid Build Coastguard Worker const std::vector<ResourceConfigValue*>& related_values = entry.second;
88*d57664e9SAndroid Build Coastguard Worker auto density_value_iter =
89*d57664e9SAndroid Build Coastguard Worker density_dependent_config_to_density_map_.find(config);
90*d57664e9SAndroid Build Coastguard Worker if (density_value_iter !=
91*d57664e9SAndroid Build Coastguard Worker density_dependent_config_to_density_map_.end()) {
92*d57664e9SAndroid Build Coastguard Worker // Select the best one!
93*d57664e9SAndroid Build Coastguard Worker ConfigDescription target_density = config;
94*d57664e9SAndroid Build Coastguard Worker target_density.density = density_value_iter->second;
95*d57664e9SAndroid Build Coastguard Worker
96*d57664e9SAndroid Build Coastguard Worker ResourceConfigValue* best_value = nullptr;
97*d57664e9SAndroid Build Coastguard Worker for (ResourceConfigValue* this_value : related_values) {
98*d57664e9SAndroid Build Coastguard Worker if (!best_value || this_value->config.isBetterThan(best_value->config, &target_density)) {
99*d57664e9SAndroid Build Coastguard Worker best_value = this_value;
100*d57664e9SAndroid Build Coastguard Worker }
101*d57664e9SAndroid Build Coastguard Worker }
102*d57664e9SAndroid Build Coastguard Worker CHECK(best_value != nullptr);
103*d57664e9SAndroid Build Coastguard Worker
104*d57664e9SAndroid Build Coastguard Worker // When we select one of these, they are all claimed such that the base
105*d57664e9SAndroid Build Coastguard Worker // doesn't include any anymore.
106*d57664e9SAndroid Build Coastguard Worker (*claimed_values)[best_value] = true;
107*d57664e9SAndroid Build Coastguard Worker selected.push_back(best_value);
108*d57664e9SAndroid Build Coastguard Worker }
109*d57664e9SAndroid Build Coastguard Worker }
110*d57664e9SAndroid Build Coastguard Worker return selected;
111*d57664e9SAndroid Build Coastguard Worker }
112*d57664e9SAndroid Build Coastguard Worker
113*d57664e9SAndroid Build Coastguard Worker private:
114*d57664e9SAndroid Build Coastguard Worker DISALLOW_COPY_AND_ASSIGN(SplitValueSelector);
115*d57664e9SAndroid Build Coastguard Worker
116*d57664e9SAndroid Build Coastguard Worker std::set<ConfigDescription> density_independent_configs_;
117*d57664e9SAndroid Build Coastguard Worker std::map<ConfigDescription, uint16_t>
118*d57664e9SAndroid Build Coastguard Worker density_dependent_config_to_density_map_;
119*d57664e9SAndroid Build Coastguard Worker };
120*d57664e9SAndroid Build Coastguard Worker
121*d57664e9SAndroid Build Coastguard Worker /**
122*d57664e9SAndroid Build Coastguard Worker * Marking non-preferred densities as claimed will make sure the base doesn't include them, leaving
123*d57664e9SAndroid Build Coastguard Worker * only the preferred density behind.
124*d57664e9SAndroid Build Coastguard Worker */
MarkNonPreferredDensitiesAsClaimed(const std::vector<uint16_t> & preferred_densities,const ConfigDensityGroups & density_groups,ConfigClaimedMap * config_claimed_map)125*d57664e9SAndroid Build Coastguard Worker static void MarkNonPreferredDensitiesAsClaimed(
126*d57664e9SAndroid Build Coastguard Worker const std::vector<uint16_t>& preferred_densities, const ConfigDensityGroups& density_groups,
127*d57664e9SAndroid Build Coastguard Worker ConfigClaimedMap* config_claimed_map) {
128*d57664e9SAndroid Build Coastguard Worker for (auto& entry : density_groups) {
129*d57664e9SAndroid Build Coastguard Worker const ConfigDescription& config = entry.first;
130*d57664e9SAndroid Build Coastguard Worker const std::vector<ResourceConfigValue*>& related_values = entry.second;
131*d57664e9SAndroid Build Coastguard Worker
132*d57664e9SAndroid Build Coastguard Worker // There can be multiple best values if there are multiple preferred densities.
133*d57664e9SAndroid Build Coastguard Worker std::unordered_set<ResourceConfigValue*> best_values;
134*d57664e9SAndroid Build Coastguard Worker
135*d57664e9SAndroid Build Coastguard Worker // For each preferred density, find the value that is the best.
136*d57664e9SAndroid Build Coastguard Worker for (uint16_t preferred_density : preferred_densities) {
137*d57664e9SAndroid Build Coastguard Worker ConfigDescription target_density = config;
138*d57664e9SAndroid Build Coastguard Worker target_density.density = preferred_density;
139*d57664e9SAndroid Build Coastguard Worker ResourceConfigValue* best_value = nullptr;
140*d57664e9SAndroid Build Coastguard Worker for (ResourceConfigValue* this_value : related_values) {
141*d57664e9SAndroid Build Coastguard Worker if (!best_value || this_value->config.isBetterThan(best_value->config, &target_density)) {
142*d57664e9SAndroid Build Coastguard Worker best_value = this_value;
143*d57664e9SAndroid Build Coastguard Worker }
144*d57664e9SAndroid Build Coastguard Worker }
145*d57664e9SAndroid Build Coastguard Worker CHECK(best_value != nullptr);
146*d57664e9SAndroid Build Coastguard Worker best_values.insert(best_value);
147*d57664e9SAndroid Build Coastguard Worker }
148*d57664e9SAndroid Build Coastguard Worker
149*d57664e9SAndroid Build Coastguard Worker // Claim all the values that aren't the best so that they will be removed from the base.
150*d57664e9SAndroid Build Coastguard Worker for (ResourceConfigValue* this_value : related_values) {
151*d57664e9SAndroid Build Coastguard Worker if (best_values.find(this_value) == best_values.end()) {
152*d57664e9SAndroid Build Coastguard Worker (*config_claimed_map)[this_value] = true;
153*d57664e9SAndroid Build Coastguard Worker }
154*d57664e9SAndroid Build Coastguard Worker }
155*d57664e9SAndroid Build Coastguard Worker }
156*d57664e9SAndroid Build Coastguard Worker }
VerifySplitConstraints(IAaptContext * context)157*d57664e9SAndroid Build Coastguard Worker bool TableSplitter::VerifySplitConstraints(IAaptContext* context) {
158*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
159*d57664e9SAndroid Build Coastguard Worker bool error = false;
160*d57664e9SAndroid Build Coastguard Worker for (size_t i = 0; i < split_constraints_.size(); i++) {
161*d57664e9SAndroid Build Coastguard Worker if (split_constraints_[i].configs.size() == 0) {
162*d57664e9SAndroid Build Coastguard Worker // For now, treat this as a warning. We may consider aborting processing.
163*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Warn(android::DiagMessage() << "no configurations for constraint '"
164*d57664e9SAndroid Build Coastguard Worker << split_constraints_[i].name << "'");
165*d57664e9SAndroid Build Coastguard Worker }
166*d57664e9SAndroid Build Coastguard Worker for (size_t j = i + 1; j < split_constraints_.size(); j++) {
167*d57664e9SAndroid Build Coastguard Worker for (const ConfigDescription& config : split_constraints_[i].configs) {
168*d57664e9SAndroid Build Coastguard Worker if (split_constraints_[j].configs.find(config) != split_constraints_[j].configs.end()) {
169*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(
170*d57664e9SAndroid Build Coastguard Worker android::DiagMessage() << "config '" << config << "' appears in multiple splits, "
171*d57664e9SAndroid Build Coastguard Worker << "target split ambiguous");
172*d57664e9SAndroid Build Coastguard Worker error = true;
173*d57664e9SAndroid Build Coastguard Worker }
174*d57664e9SAndroid Build Coastguard Worker }
175*d57664e9SAndroid Build Coastguard Worker }
176*d57664e9SAndroid Build Coastguard Worker }
177*d57664e9SAndroid Build Coastguard Worker return !error;
178*d57664e9SAndroid Build Coastguard Worker }
179*d57664e9SAndroid Build Coastguard Worker
SplitTable(ResourceTable * original_table)180*d57664e9SAndroid Build Coastguard Worker void TableSplitter::SplitTable(ResourceTable* original_table) {
181*d57664e9SAndroid Build Coastguard Worker const size_t split_count = split_constraints_.size();
182*d57664e9SAndroid Build Coastguard Worker for (auto& pkg : original_table->packages) {
183*d57664e9SAndroid Build Coastguard Worker // Initialize all packages for splits.
184*d57664e9SAndroid Build Coastguard Worker for (size_t idx = 0; idx < split_count; idx++) {
185*d57664e9SAndroid Build Coastguard Worker ResourceTable* split_table = splits_[idx].get();
186*d57664e9SAndroid Build Coastguard Worker split_table->FindOrCreatePackage(pkg->name);
187*d57664e9SAndroid Build Coastguard Worker }
188*d57664e9SAndroid Build Coastguard Worker
189*d57664e9SAndroid Build Coastguard Worker for (auto& type : pkg->types) {
190*d57664e9SAndroid Build Coastguard Worker if (type->named_type.type == ResourceType::kMipmap) {
191*d57664e9SAndroid Build Coastguard Worker // Always keep mipmaps.
192*d57664e9SAndroid Build Coastguard Worker continue;
193*d57664e9SAndroid Build Coastguard Worker }
194*d57664e9SAndroid Build Coastguard Worker
195*d57664e9SAndroid Build Coastguard Worker for (auto& entry : type->entries) {
196*d57664e9SAndroid Build Coastguard Worker if (options_.config_filter) {
197*d57664e9SAndroid Build Coastguard Worker // First eliminate any resource that we definitely don't want.
198*d57664e9SAndroid Build Coastguard Worker for (std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
199*d57664e9SAndroid Build Coastguard Worker if (!options_.config_filter->Match(config_value->config)) {
200*d57664e9SAndroid Build Coastguard Worker // null out the entry. We will clean up and remove nulls at the end for performance
201*d57664e9SAndroid Build Coastguard Worker // reasons.
202*d57664e9SAndroid Build Coastguard Worker config_value.reset();
203*d57664e9SAndroid Build Coastguard Worker }
204*d57664e9SAndroid Build Coastguard Worker }
205*d57664e9SAndroid Build Coastguard Worker }
206*d57664e9SAndroid Build Coastguard Worker
207*d57664e9SAndroid Build Coastguard Worker // Organize the values into two separate buckets. Those that are density-dependent and those
208*d57664e9SAndroid Build Coastguard Worker // that are density-independent. One density technically matches all density, it's just that
209*d57664e9SAndroid Build Coastguard Worker // some densities match better. So we need to be aware of the full set of densities to make
210*d57664e9SAndroid Build Coastguard Worker // this decision.
211*d57664e9SAndroid Build Coastguard Worker ConfigDensityGroups density_groups;
212*d57664e9SAndroid Build Coastguard Worker ConfigClaimedMap config_claimed_map;
213*d57664e9SAndroid Build Coastguard Worker for (const std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
214*d57664e9SAndroid Build Coastguard Worker if (config_value) {
215*d57664e9SAndroid Build Coastguard Worker config_claimed_map[config_value.get()] = false;
216*d57664e9SAndroid Build Coastguard Worker
217*d57664e9SAndroid Build Coastguard Worker if (config_value->config.density != 0) {
218*d57664e9SAndroid Build Coastguard Worker // Create a bucket for this density-dependent config.
219*d57664e9SAndroid Build Coastguard Worker density_groups[CopyWithoutDensity(config_value->config)]
220*d57664e9SAndroid Build Coastguard Worker .push_back(config_value.get());
221*d57664e9SAndroid Build Coastguard Worker }
222*d57664e9SAndroid Build Coastguard Worker }
223*d57664e9SAndroid Build Coastguard Worker }
224*d57664e9SAndroid Build Coastguard Worker
225*d57664e9SAndroid Build Coastguard Worker // First we check all the splits. If it doesn't match one of the splits, we leave it in the
226*d57664e9SAndroid Build Coastguard Worker // base.
227*d57664e9SAndroid Build Coastguard Worker for (size_t idx = 0; idx < split_count; idx++) {
228*d57664e9SAndroid Build Coastguard Worker const SplitConstraints& split_constraint = split_constraints_[idx];
229*d57664e9SAndroid Build Coastguard Worker ResourceTable* split_table = splits_[idx].get();
230*d57664e9SAndroid Build Coastguard Worker CloningValueTransformer cloner(&split_table->string_pool);
231*d57664e9SAndroid Build Coastguard Worker
232*d57664e9SAndroid Build Coastguard Worker // Select the values we want from this entry for this split.
233*d57664e9SAndroid Build Coastguard Worker SplitValueSelector selector(split_constraint);
234*d57664e9SAndroid Build Coastguard Worker std::vector<ResourceConfigValue*> selected_values =
235*d57664e9SAndroid Build Coastguard Worker selector.SelectValues(density_groups, &config_claimed_map);
236*d57664e9SAndroid Build Coastguard Worker
237*d57664e9SAndroid Build Coastguard Worker // No need to do any work if we selected nothing.
238*d57664e9SAndroid Build Coastguard Worker if (!selected_values.empty()) {
239*d57664e9SAndroid Build Coastguard Worker // Create the same resource structure in the split. We do this lazily because we might
240*d57664e9SAndroid Build Coastguard Worker // not have actual values for each type/entry.
241*d57664e9SAndroid Build Coastguard Worker ResourceTablePackage* split_pkg = split_table->FindPackage(pkg->name);
242*d57664e9SAndroid Build Coastguard Worker ResourceTableType* split_type = split_pkg->FindOrCreateType(type->named_type);
243*d57664e9SAndroid Build Coastguard Worker split_type->visibility_level = type->visibility_level;
244*d57664e9SAndroid Build Coastguard Worker
245*d57664e9SAndroid Build Coastguard Worker ResourceEntry* split_entry = split_type->FindOrCreateEntry(entry->name);
246*d57664e9SAndroid Build Coastguard Worker if (!split_entry->id) {
247*d57664e9SAndroid Build Coastguard Worker split_entry->id = entry->id;
248*d57664e9SAndroid Build Coastguard Worker split_entry->visibility = entry->visibility;
249*d57664e9SAndroid Build Coastguard Worker split_entry->overlayable_item = entry->overlayable_item;
250*d57664e9SAndroid Build Coastguard Worker }
251*d57664e9SAndroid Build Coastguard Worker
252*d57664e9SAndroid Build Coastguard Worker // Copy the selected values into the new Split Entry.
253*d57664e9SAndroid Build Coastguard Worker for (ResourceConfigValue* config_value : selected_values) {
254*d57664e9SAndroid Build Coastguard Worker ResourceConfigValue* new_config_value =
255*d57664e9SAndroid Build Coastguard Worker split_entry->FindOrCreateValue(config_value->config, config_value->product);
256*d57664e9SAndroid Build Coastguard Worker new_config_value->value = config_value->value->Transform(cloner);
257*d57664e9SAndroid Build Coastguard Worker }
258*d57664e9SAndroid Build Coastguard Worker }
259*d57664e9SAndroid Build Coastguard Worker }
260*d57664e9SAndroid Build Coastguard Worker
261*d57664e9SAndroid Build Coastguard Worker if (!options_.preferred_densities.empty()) {
262*d57664e9SAndroid Build Coastguard Worker MarkNonPreferredDensitiesAsClaimed(options_.preferred_densities,
263*d57664e9SAndroid Build Coastguard Worker density_groups,
264*d57664e9SAndroid Build Coastguard Worker &config_claimed_map);
265*d57664e9SAndroid Build Coastguard Worker }
266*d57664e9SAndroid Build Coastguard Worker
267*d57664e9SAndroid Build Coastguard Worker // All splits are handled, now check to see what wasn't claimed and remove whatever exists
268*d57664e9SAndroid Build Coastguard Worker // in other splits.
269*d57664e9SAndroid Build Coastguard Worker for (std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
270*d57664e9SAndroid Build Coastguard Worker if (config_value && config_claimed_map[config_value.get()]) {
271*d57664e9SAndroid Build Coastguard Worker // Claimed, remove from base.
272*d57664e9SAndroid Build Coastguard Worker config_value.reset();
273*d57664e9SAndroid Build Coastguard Worker }
274*d57664e9SAndroid Build Coastguard Worker }
275*d57664e9SAndroid Build Coastguard Worker
276*d57664e9SAndroid Build Coastguard Worker // Now erase all nullptrs.
277*d57664e9SAndroid Build Coastguard Worker entry->values.erase(
278*d57664e9SAndroid Build Coastguard Worker std::remove(entry->values.begin(), entry->values.end(), nullptr),
279*d57664e9SAndroid Build Coastguard Worker entry->values.end());
280*d57664e9SAndroid Build Coastguard Worker }
281*d57664e9SAndroid Build Coastguard Worker }
282*d57664e9SAndroid Build Coastguard Worker }
283*d57664e9SAndroid Build Coastguard Worker }
284*d57664e9SAndroid Build Coastguard Worker
285*d57664e9SAndroid Build Coastguard Worker } // namespace aapt
286