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