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