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