ResourceTable.cpp revision d5083f6f6b9bc76bbe64052bcec639eee752a321
1/* 2 * Copyright (C) 2015 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 "ResourceTable.h" 18#include "ConfigDescription.h" 19#include "NameMangler.h" 20#include "ResourceValues.h" 21#include "ValueVisitor.h" 22#include "util/Util.h" 23 24#include <android-base/logging.h> 25#include <androidfw/ResourceTypes.h> 26#include <algorithm> 27#include <memory> 28#include <string> 29#include <tuple> 30 31using android::StringPiece; 32 33namespace aapt { 34 35static bool less_than_type(const std::unique_ptr<ResourceTableType>& lhs, 36 ResourceType rhs) { 37 return lhs->type < rhs; 38} 39 40template <typename T> 41static bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, 42 const StringPiece& rhs) { 43 return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0; 44} 45 46ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) { 47 const auto last = packages.end(); 48 auto iter = 49 std::lower_bound(packages.begin(), last, name, 50 less_than_struct_with_name<ResourceTablePackage>); 51 if (iter != last && name == (*iter)->name) { 52 return iter->get(); 53 } 54 return nullptr; 55} 56 57ResourceTablePackage* ResourceTable::FindPackageById(uint8_t id) { 58 for (auto& package : packages) { 59 if (package->id && package->id.value() == id) { 60 return package.get(); 61 } 62 } 63 return nullptr; 64} 65 66ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name, 67 Maybe<uint8_t> id) { 68 ResourceTablePackage* package = FindOrCreatePackage(name); 69 if (id && !package->id) { 70 package->id = id; 71 return package; 72 } 73 74 if (id && package->id && package->id.value() != id.value()) { 75 return nullptr; 76 } 77 return package; 78} 79 80ResourceTablePackage* ResourceTable::FindOrCreatePackage( 81 const StringPiece& name) { 82 const auto last = packages.end(); 83 auto iter = 84 std::lower_bound(packages.begin(), last, name, 85 less_than_struct_with_name<ResourceTablePackage>); 86 if (iter != last && name == (*iter)->name) { 87 return iter->get(); 88 } 89 90 std::unique_ptr<ResourceTablePackage> new_package = 91 util::make_unique<ResourceTablePackage>(); 92 new_package->name = name.to_string(); 93 return packages.emplace(iter, std::move(new_package))->get(); 94} 95 96ResourceTableType* ResourceTablePackage::FindType(ResourceType type) { 97 const auto last = types.end(); 98 auto iter = std::lower_bound(types.begin(), last, type, less_than_type); 99 if (iter != last && (*iter)->type == type) { 100 return iter->get(); 101 } 102 return nullptr; 103} 104 105ResourceTableType* ResourceTablePackage::FindOrCreateType(ResourceType type) { 106 const auto last = types.end(); 107 auto iter = std::lower_bound(types.begin(), last, type, less_than_type); 108 if (iter != last && (*iter)->type == type) { 109 return iter->get(); 110 } 111 return types.emplace(iter, new ResourceTableType(type))->get(); 112} 113 114ResourceEntry* ResourceTableType::FindEntry(const StringPiece& name) { 115 const auto last = entries.end(); 116 auto iter = std::lower_bound(entries.begin(), last, name, 117 less_than_struct_with_name<ResourceEntry>); 118 if (iter != last && name == (*iter)->name) { 119 return iter->get(); 120 } 121 return nullptr; 122} 123 124ResourceEntry* ResourceTableType::FindOrCreateEntry(const StringPiece& name) { 125 auto last = entries.end(); 126 auto iter = std::lower_bound(entries.begin(), last, name, 127 less_than_struct_with_name<ResourceEntry>); 128 if (iter != last && name == (*iter)->name) { 129 return iter->get(); 130 } 131 return entries.emplace(iter, new ResourceEntry(name))->get(); 132} 133 134ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config) { 135 return FindValue(config, StringPiece()); 136} 137 138struct ConfigKey { 139 const ConfigDescription* config; 140 const StringPiece& product; 141}; 142 143bool ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue>& lhs, 144 const ConfigKey& rhs) { 145 int cmp = lhs->config.compare(*rhs.config); 146 if (cmp == 0) { 147 cmp = StringPiece(lhs->product).compare(rhs.product); 148 } 149 return cmp < 0; 150} 151 152ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config, 153 const StringPiece& product) { 154 auto iter = std::lower_bound(values.begin(), values.end(), 155 ConfigKey{&config, product}, ltConfigKeyRef); 156 if (iter != values.end()) { 157 ResourceConfigValue* value = iter->get(); 158 if (value->config == config && StringPiece(value->product) == product) { 159 return value; 160 } 161 } 162 return nullptr; 163} 164 165ResourceConfigValue* ResourceEntry::FindOrCreateValue( 166 const ConfigDescription& config, const StringPiece& product) { 167 auto iter = std::lower_bound(values.begin(), values.end(), 168 ConfigKey{&config, product}, ltConfigKeyRef); 169 if (iter != values.end()) { 170 ResourceConfigValue* value = iter->get(); 171 if (value->config == config && StringPiece(value->product) == product) { 172 return value; 173 } 174 } 175 ResourceConfigValue* newValue = 176 values 177 .insert(iter, util::make_unique<ResourceConfigValue>(config, product)) 178 ->get(); 179 return newValue; 180} 181 182std::vector<ResourceConfigValue*> ResourceEntry::findAllValues( 183 const ConfigDescription& config) { 184 std::vector<ResourceConfigValue*> results; 185 186 auto iter = values.begin(); 187 for (; iter != values.end(); ++iter) { 188 ResourceConfigValue* value = iter->get(); 189 if (value->config == config) { 190 results.push_back(value); 191 ++iter; 192 break; 193 } 194 } 195 196 for (; iter != values.end(); ++iter) { 197 ResourceConfigValue* value = iter->get(); 198 if (value->config == config) { 199 results.push_back(value); 200 } 201 } 202 return results; 203} 204 205std::vector<ResourceConfigValue*> ResourceEntry::FindValuesIf( 206 const std::function<bool(ResourceConfigValue*)>& f) { 207 std::vector<ResourceConfigValue*> results; 208 for (auto& configValue : values) { 209 if (f(configValue.get())) { 210 results.push_back(configValue.get()); 211 } 212 } 213 return results; 214} 215 216/** 217 * The default handler for collisions. 218 * 219 * Typically, a weak value will be overridden by a strong value. An existing 220 * weak 221 * value will not be overridden by an incoming weak value. 222 * 223 * There are some exceptions: 224 * 225 * Attributes: There are two types of Attribute values: USE and DECL. 226 * 227 * USE is anywhere an Attribute is declared without a format, and in a place 228 * that would 229 * be legal to declare if the Attribute already existed. This is typically in a 230 * <declare-styleable> tag. Attributes defined in a <declare-styleable> are also 231 * weak. 232 * 233 * DECL is an absolute declaration of an Attribute and specifies an explicit 234 * format. 235 * 236 * A DECL will override a USE without error. Two DECLs must match in their 237 * format for there to be 238 * no error. 239 */ 240ResourceTable::CollisionResult ResourceTable::ResolveValueCollision( 241 Value* existing, Value* incoming) { 242 Attribute* existing_attr = ValueCast<Attribute>(existing); 243 Attribute* incoming_attr = ValueCast<Attribute>(incoming); 244 if (!incoming_attr) { 245 if (incoming->IsWeak()) { 246 // We're trying to add a weak resource but a resource 247 // already exists. Keep the existing. 248 return CollisionResult::kKeepOriginal; 249 } else if (existing->IsWeak()) { 250 // Override the weak resource with the new strong resource. 251 return CollisionResult::kTakeNew; 252 } 253 // The existing and incoming values are strong, this is an error 254 // if the values are not both attributes. 255 return CollisionResult::kConflict; 256 } 257 258 if (!existing_attr) { 259 if (existing->IsWeak()) { 260 // The existing value is not an attribute and it is weak, 261 // so take the incoming attribute value. 262 return CollisionResult::kTakeNew; 263 } 264 // The existing value is not an attribute and it is strong, 265 // so the incoming attribute value is an error. 266 return CollisionResult::kConflict; 267 } 268 269 CHECK(incoming_attr != nullptr && existing_attr != nullptr); 270 271 // 272 // Attribute specific handling. At this point we know both 273 // values are attributes. Since we can declare and define 274 // attributes all-over, we do special handling to see 275 // which definition sticks. 276 // 277 if (existing_attr->type_mask == incoming_attr->type_mask) { 278 // The two attributes are both DECLs, but they are plain attributes 279 // with the same formats. 280 // Keep the strongest one. 281 return existing_attr->IsWeak() ? CollisionResult::kTakeNew 282 : CollisionResult::kKeepOriginal; 283 } 284 285 if (existing_attr->IsWeak() && 286 existing_attr->type_mask == android::ResTable_map::TYPE_ANY) { 287 // Any incoming attribute is better than this. 288 return CollisionResult::kTakeNew; 289 } 290 291 if (incoming_attr->IsWeak() && 292 incoming_attr->type_mask == android::ResTable_map::TYPE_ANY) { 293 // The incoming attribute may be a USE instead of a DECL. 294 // Keep the existing attribute. 295 return CollisionResult::kKeepOriginal; 296 } 297 return CollisionResult::kConflict; 298} 299 300static constexpr const char* kValidNameChars = "._-"; 301static constexpr const char* kValidNameMangledChars = "._-$"; 302 303bool ResourceTable::AddResource(const ResourceNameRef& name, 304 const ConfigDescription& config, 305 const StringPiece& product, 306 std::unique_ptr<Value> value, 307 IDiagnostics* diag) { 308 return AddResourceImpl(name, {}, config, product, std::move(value), 309 kValidNameChars, ResolveValueCollision, diag); 310} 311 312bool ResourceTable::AddResource(const ResourceNameRef& name, 313 const ResourceId& res_id, 314 const ConfigDescription& config, 315 const StringPiece& product, 316 std::unique_ptr<Value> value, 317 IDiagnostics* diag) { 318 return AddResourceImpl(name, res_id, config, product, std::move(value), 319 kValidNameChars, ResolveValueCollision, diag); 320} 321 322bool ResourceTable::AddFileReference(const ResourceNameRef& name, 323 const ConfigDescription& config, 324 const Source& source, 325 const StringPiece& path, 326 IDiagnostics* diag) { 327 return AddFileReferenceImpl(name, config, source, path, nullptr, 328 kValidNameChars, diag); 329} 330 331bool ResourceTable::AddFileReferenceAllowMangled( 332 const ResourceNameRef& name, const ConfigDescription& config, 333 const Source& source, const StringPiece& path, io::IFile* file, 334 IDiagnostics* diag) { 335 return AddFileReferenceImpl(name, config, source, path, file, 336 kValidNameMangledChars, diag); 337} 338 339bool ResourceTable::AddFileReferenceImpl( 340 const ResourceNameRef& name, const ConfigDescription& config, 341 const Source& source, const StringPiece& path, io::IFile* file, 342 const char* valid_chars, IDiagnostics* diag) { 343 std::unique_ptr<FileReference> fileRef = 344 util::make_unique<FileReference>(string_pool.MakeRef(path)); 345 fileRef->SetSource(source); 346 fileRef->file = file; 347 return AddResourceImpl(name, ResourceId{}, config, StringPiece{}, 348 std::move(fileRef), valid_chars, ResolveValueCollision, 349 diag); 350} 351 352bool ResourceTable::AddResourceAllowMangled(const ResourceNameRef& name, 353 const ConfigDescription& config, 354 const StringPiece& product, 355 std::unique_ptr<Value> value, 356 IDiagnostics* diag) { 357 return AddResourceImpl(name, ResourceId{}, config, product, std::move(value), 358 kValidNameMangledChars, ResolveValueCollision, diag); 359} 360 361bool ResourceTable::AddResourceAllowMangled(const ResourceNameRef& name, 362 const ResourceId& id, 363 const ConfigDescription& config, 364 const StringPiece& product, 365 std::unique_ptr<Value> value, 366 IDiagnostics* diag) { 367 return AddResourceImpl(name, id, config, product, std::move(value), 368 kValidNameMangledChars, ResolveValueCollision, diag); 369} 370 371bool ResourceTable::AddResourceImpl( 372 const ResourceNameRef& name, const ResourceId& res_id, 373 const ConfigDescription& config, const StringPiece& product, 374 std::unique_ptr<Value> value, const char* valid_chars, 375 const CollisionResolverFunc& conflictResolver, IDiagnostics* diag) { 376 CHECK(value != nullptr); 377 CHECK(diag != nullptr); 378 379 auto bad_char_iter = 380 util::FindNonAlphaNumericAndNotInSet(name.entry, valid_chars); 381 if (bad_char_iter != name.entry.end()) { 382 diag->Error(DiagMessage(value->GetSource()) 383 << "resource '" << name << "' has invalid entry name '" 384 << name.entry << "'. Invalid character '" 385 << StringPiece(bad_char_iter, 1) << "'"); 386 return false; 387 } 388 389 ResourceTablePackage* package = FindOrCreatePackage(name.package); 390 if (res_id.is_valid() && package->id && 391 package->id.value() != res_id.package_id()) { 392 diag->Error(DiagMessage(value->GetSource()) 393 << "trying to add resource '" << name << "' with ID " << res_id 394 << " but package '" << package->name << "' already has ID " 395 << std::hex << (int)package->id.value() << std::dec); 396 return false; 397 } 398 399 ResourceTableType* type = package->FindOrCreateType(name.type); 400 if (res_id.is_valid() && type->id && type->id.value() != res_id.type_id()) { 401 diag->Error(DiagMessage(value->GetSource()) 402 << "trying to add resource '" << name << "' with ID " << res_id 403 << " but type '" << type->type << "' already has ID " 404 << std::hex << (int)type->id.value() << std::dec); 405 return false; 406 } 407 408 ResourceEntry* entry = type->FindOrCreateEntry(name.entry); 409 if (res_id.is_valid() && entry->id && 410 entry->id.value() != res_id.entry_id()) { 411 diag->Error(DiagMessage(value->GetSource()) 412 << "trying to add resource '" << name << "' with ID " << res_id 413 << " but resource already has ID " 414 << ResourceId(package->id.value(), type->id.value(), 415 entry->id.value())); 416 return false; 417 } 418 419 ResourceConfigValue* config_value = entry->FindOrCreateValue(config, product); 420 if (!config_value->value) { 421 // Resource does not exist, add it now. 422 config_value->value = std::move(value); 423 424 } else { 425 switch (conflictResolver(config_value->value.get(), value.get())) { 426 case CollisionResult::kTakeNew: 427 // Take the incoming value. 428 config_value->value = std::move(value); 429 break; 430 431 case CollisionResult::kConflict: 432 diag->Error(DiagMessage(value->GetSource()) 433 << "duplicate value for resource '" << name << "' " 434 << "with config '" << config << "'"); 435 diag->Error(DiagMessage(config_value->value->GetSource()) 436 << "resource previously defined here"); 437 return false; 438 439 case CollisionResult::kKeepOriginal: 440 break; 441 } 442 } 443 444 if (res_id.is_valid()) { 445 package->id = res_id.package_id(); 446 type->id = res_id.type_id(); 447 entry->id = res_id.entry_id(); 448 } 449 return true; 450} 451 452bool ResourceTable::SetSymbolState(const ResourceNameRef& name, 453 const ResourceId& res_id, 454 const Symbol& symbol, IDiagnostics* diag) { 455 return SetSymbolStateImpl(name, res_id, symbol, kValidNameChars, diag); 456} 457 458bool ResourceTable::SetSymbolStateAllowMangled(const ResourceNameRef& name, 459 const ResourceId& res_id, 460 const Symbol& symbol, 461 IDiagnostics* diag) { 462 return SetSymbolStateImpl(name, res_id, symbol, kValidNameMangledChars, diag); 463} 464 465bool ResourceTable::SetSymbolStateImpl(const ResourceNameRef& name, 466 const ResourceId& res_id, 467 const Symbol& symbol, 468 const char* valid_chars, 469 IDiagnostics* diag) { 470 CHECK(diag != nullptr); 471 472 auto bad_char_iter = 473 util::FindNonAlphaNumericAndNotInSet(name.entry, valid_chars); 474 if (bad_char_iter != name.entry.end()) { 475 diag->Error(DiagMessage(symbol.source) 476 << "resource '" << name << "' has invalid entry name '" 477 << name.entry << "'. Invalid character '" 478 << StringPiece(bad_char_iter, 1) << "'"); 479 return false; 480 } 481 482 ResourceTablePackage* package = FindOrCreatePackage(name.package); 483 if (res_id.is_valid() && package->id && 484 package->id.value() != res_id.package_id()) { 485 diag->Error(DiagMessage(symbol.source) 486 << "trying to add resource '" << name << "' with ID " << res_id 487 << " but package '" << package->name << "' already has ID " 488 << std::hex << (int)package->id.value() << std::dec); 489 return false; 490 } 491 492 ResourceTableType* type = package->FindOrCreateType(name.type); 493 if (res_id.is_valid() && type->id && type->id.value() != res_id.type_id()) { 494 diag->Error(DiagMessage(symbol.source) 495 << "trying to add resource '" << name << "' with ID " << res_id 496 << " but type '" << type->type << "' already has ID " 497 << std::hex << (int)type->id.value() << std::dec); 498 return false; 499 } 500 501 ResourceEntry* entry = type->FindOrCreateEntry(name.entry); 502 if (res_id.is_valid() && entry->id && 503 entry->id.value() != res_id.entry_id()) { 504 diag->Error(DiagMessage(symbol.source) 505 << "trying to add resource '" << name << "' with ID " << res_id 506 << " but resource already has ID " 507 << ResourceId(package->id.value(), type->id.value(), 508 entry->id.value())); 509 return false; 510 } 511 512 if (res_id.is_valid()) { 513 package->id = res_id.package_id(); 514 type->id = res_id.type_id(); 515 entry->id = res_id.entry_id(); 516 } 517 518 // Only mark the type state as public, it doesn't care about being private. 519 if (symbol.state == SymbolState::kPublic) { 520 type->symbol_status.state = SymbolState::kPublic; 521 } 522 523 if (symbol.state == SymbolState::kUndefined && 524 entry->symbol_status.state != SymbolState::kUndefined) { 525 // We can't undefine a symbol (remove its visibility). Ignore. 526 return true; 527 } 528 529 if (symbol.state == SymbolState::kPrivate && 530 entry->symbol_status.state == SymbolState::kPublic) { 531 // We can't downgrade public to private. Ignore. 532 return true; 533 } 534 535 entry->symbol_status = std::move(symbol); 536 return true; 537} 538 539Maybe<ResourceTable::SearchResult> ResourceTable::FindResource( 540 const ResourceNameRef& name) { 541 ResourceTablePackage* package = FindPackage(name.package); 542 if (!package) { 543 return {}; 544 } 545 546 ResourceTableType* type = package->FindType(name.type); 547 if (!type) { 548 return {}; 549 } 550 551 ResourceEntry* entry = type->FindEntry(name.entry); 552 if (!entry) { 553 return {}; 554 } 555 return SearchResult{package, type, entry}; 556} 557 558} // namespace aapt 559