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