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 30namespace aapt { 31 32static bool lessThanType(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) { 33 return lhs->type < rhs; 34} 35 36template <typename T> 37static 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 42ResourceTablePackage* 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 52ResourceTablePackage* 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 61ResourceTablePackage* 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 74ResourceTablePackage* 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 87ResourceTableType* 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 96ResourceTableType* 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 105ResourceEntry* 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 115ResourceEntry* 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 125ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config) { 126 return findValue(config, StringPiece()); 127} 128 129struct ConfigKey { 130 const ConfigDescription* config; 131 const StringPiece& product; 132}; 133 134bool 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 142ResourceConfigValue* 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 155ResourceConfigValue* 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 170std::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/** 193 * The default handler for collisions. A return value of -1 means keep the 194 * existing value, 0 means fail, and +1 means take the incoming value. 195 */ 196int ResourceTable::resolveValueCollision(Value* existing, Value* incoming) { 197 Attribute* existingAttr = valueCast<Attribute>(existing); 198 Attribute* incomingAttr = valueCast<Attribute>(incoming); 199 200 if (!incomingAttr) { 201 if (incoming->isWeak()) { 202 // We're trying to add a weak resource but a resource 203 // already exists. Keep the existing. 204 return -1; 205 } else if (existing->isWeak()) { 206 // Override the weak resource with the new strong resource. 207 return 1; 208 } 209 // The existing and incoming values are strong, this is an error 210 // if the values are not both attributes. 211 return 0; 212 } 213 214 if (!existingAttr) { 215 if (existing->isWeak()) { 216 // The existing value is not an attribute and it is weak, 217 // so take the incoming attribute value. 218 return 1; 219 } 220 // The existing value is not an attribute and it is strong, 221 // so the incoming attribute value is an error. 222 return 0; 223 } 224 225 assert(incomingAttr && existingAttr); 226 227 // 228 // Attribute specific handling. At this point we know both 229 // values are attributes. Since we can declare and define 230 // attributes all-over, we do special handling to see 231 // which definition sticks. 232 // 233 if (existingAttr->typeMask == incomingAttr->typeMask) { 234 // The two attributes are both DECLs, but they are plain attributes 235 // with the same formats. 236 // Keep the strongest one. 237 return existingAttr->isWeak() ? 1 : -1; 238 } 239 240 if (existingAttr->isWeak() && existingAttr->typeMask == android::ResTable_map::TYPE_ANY) { 241 // Any incoming attribute is better than this. 242 return 1; 243 } 244 245 if (incomingAttr->isWeak() && incomingAttr->typeMask == android::ResTable_map::TYPE_ANY) { 246 // The incoming attribute may be a USE instead of a DECL. 247 // Keep the existing attribute. 248 return -1; 249 } 250 return 0; 251} 252 253static constexpr const char16_t* kValidNameChars = u"._-"; 254static constexpr const char16_t* kValidNameMangledChars = u"._-$"; 255 256bool ResourceTable::addResource(const ResourceNameRef& name, 257 const ConfigDescription& config, 258 const StringPiece& product, 259 std::unique_ptr<Value> value, 260 IDiagnostics* diag) { 261 return addResourceImpl(name, {}, config, product, std::move(value), kValidNameChars, 262 resolveValueCollision, diag); 263} 264 265bool ResourceTable::addResource(const ResourceNameRef& name, 266 const ResourceId resId, 267 const ConfigDescription& config, 268 const StringPiece& product, 269 std::unique_ptr<Value> value, 270 IDiagnostics* diag) { 271 return addResourceImpl(name, resId, config, product, std::move(value), kValidNameChars, 272 resolveValueCollision, diag); 273} 274 275bool ResourceTable::addFileReference(const ResourceNameRef& name, 276 const ConfigDescription& config, 277 const Source& source, 278 const StringPiece16& path, 279 IDiagnostics* diag) { 280 return addFileReferenceImpl(name, config, source, path, nullptr, kValidNameChars, diag); 281} 282 283bool ResourceTable::addFileReferenceAllowMangled(const ResourceNameRef& name, 284 const ConfigDescription& config, 285 const Source& source, 286 const StringPiece16& path, 287 io::IFile* file, 288 IDiagnostics* diag) { 289 return addFileReferenceImpl(name, config, source, path, file, kValidNameMangledChars, diag); 290} 291 292bool ResourceTable::addFileReferenceImpl(const ResourceNameRef& name, 293 const ConfigDescription& config, 294 const Source& source, 295 const StringPiece16& path, 296 io::IFile* file, 297 const char16_t* validChars, 298 IDiagnostics* diag) { 299 std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>( 300 stringPool.makeRef(path)); 301 fileRef->setSource(source); 302 fileRef->file = file; 303 return addResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef), 304 kValidNameChars, resolveValueCollision, diag); 305} 306 307bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name, 308 const ConfigDescription& config, 309 const StringPiece& product, 310 std::unique_ptr<Value> value, 311 IDiagnostics* diag) { 312 return addResourceImpl(name, ResourceId{}, config, product, std::move(value), 313 kValidNameMangledChars, resolveValueCollision, diag); 314} 315 316bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name, 317 const ResourceId id, 318 const ConfigDescription& config, 319 const StringPiece& product, 320 std::unique_ptr<Value> value, 321 IDiagnostics* diag) { 322 return addResourceImpl(name, id, config, product, std::move(value), kValidNameMangledChars, 323 resolveValueCollision, diag); 324} 325 326bool ResourceTable::addResourceImpl(const ResourceNameRef& name, 327 const ResourceId resId, 328 const ConfigDescription& config, 329 const StringPiece& product, 330 std::unique_ptr<Value> value, 331 const char16_t* validChars, 332 std::function<int(Value*,Value*)> conflictResolver, 333 IDiagnostics* diag) { 334 assert(value && "value can't be nullptr"); 335 assert(diag && "diagnostics can't be nullptr"); 336 337 auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars); 338 if (badCharIter != name.entry.end()) { 339 diag->error(DiagMessage(value->getSource()) 340 << "resource '" 341 << name 342 << "' has invalid entry name '" 343 << name.entry 344 << "'. Invalid character '" 345 << StringPiece16(badCharIter, 1) 346 << "'"); 347 return false; 348 } 349 350 ResourceTablePackage* package = findOrCreatePackage(name.package); 351 if (resId.isValid() && package->id && package->id.value() != resId.packageId()) { 352 diag->error(DiagMessage(value->getSource()) 353 << "trying to add resource '" 354 << name 355 << "' with ID " 356 << resId 357 << " but package '" 358 << package->name 359 << "' already has ID " 360 << std::hex << (int) package->id.value() << std::dec); 361 return false; 362 } 363 364 ResourceTableType* type = package->findOrCreateType(name.type); 365 if (resId.isValid() && type->id && type->id.value() != resId.typeId()) { 366 diag->error(DiagMessage(value->getSource()) 367 << "trying to add resource '" 368 << name 369 << "' with ID " 370 << resId 371 << " but type '" 372 << type->type 373 << "' already has ID " 374 << std::hex << (int) type->id.value() << std::dec); 375 return false; 376 } 377 378 ResourceEntry* entry = type->findOrCreateEntry(name.entry); 379 if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) { 380 diag->error(DiagMessage(value->getSource()) 381 << "trying to add resource '" 382 << name 383 << "' with ID " 384 << resId 385 << " but resource already has ID " 386 << ResourceId(package->id.value(), type->id.value(), entry->id.value())); 387 return false; 388 } 389 390 ResourceConfigValue* configValue = entry->findOrCreateValue(config, product); 391 if (!configValue->value) { 392 // Resource does not exist, add it now. 393 configValue->value = std::move(value); 394 395 } else { 396 int collisionResult = conflictResolver(configValue->value.get(), value.get()); 397 if (collisionResult > 0) { 398 // Take the incoming value. 399 configValue->value = std::move(value); 400 } else if (collisionResult == 0) { 401 diag->error(DiagMessage(value->getSource()) 402 << "duplicate value for resource '" << name << "' " 403 << "with config '" << config << "'"); 404 diag->error(DiagMessage(configValue->value->getSource()) 405 << "resource previously defined here"); 406 return false; 407 } 408 } 409 410 if (resId.isValid()) { 411 package->id = resId.packageId(); 412 type->id = resId.typeId(); 413 entry->id = resId.entryId(); 414 } 415 return true; 416} 417 418bool ResourceTable::setSymbolState(const ResourceNameRef& name, const ResourceId resId, 419 const Symbol& symbol, IDiagnostics* diag) { 420 return setSymbolStateImpl(name, resId, symbol, kValidNameChars, diag); 421} 422 423bool ResourceTable::setSymbolStateAllowMangled(const ResourceNameRef& name, 424 const ResourceId resId, 425 const Symbol& symbol, IDiagnostics* diag) { 426 return setSymbolStateImpl(name, resId, symbol, kValidNameMangledChars, diag); 427} 428 429bool ResourceTable::setSymbolStateImpl(const ResourceNameRef& name, const ResourceId resId, 430 const Symbol& symbol, const char16_t* validChars, 431 IDiagnostics* diag) { 432 assert(diag && "diagnostics can't be nullptr"); 433 434 auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars); 435 if (badCharIter != name.entry.end()) { 436 diag->error(DiagMessage(symbol.source) 437 << "resource '" 438 << name 439 << "' has invalid entry name '" 440 << name.entry 441 << "'. Invalid character '" 442 << StringPiece16(badCharIter, 1) 443 << "'"); 444 return false; 445 } 446 447 ResourceTablePackage* package = findOrCreatePackage(name.package); 448 if (resId.isValid() && package->id && package->id.value() != resId.packageId()) { 449 diag->error(DiagMessage(symbol.source) 450 << "trying to add resource '" 451 << name 452 << "' with ID " 453 << resId 454 << " but package '" 455 << package->name 456 << "' already has ID " 457 << std::hex << (int) package->id.value() << std::dec); 458 return false; 459 } 460 461 ResourceTableType* type = package->findOrCreateType(name.type); 462 if (resId.isValid() && type->id && type->id.value() != resId.typeId()) { 463 diag->error(DiagMessage(symbol.source) 464 << "trying to add resource '" 465 << name 466 << "' with ID " 467 << resId 468 << " but type '" 469 << type->type 470 << "' already has ID " 471 << std::hex << (int) type->id.value() << std::dec); 472 return false; 473 } 474 475 ResourceEntry* entry = type->findOrCreateEntry(name.entry); 476 if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) { 477 diag->error(DiagMessage(symbol.source) 478 << "trying to add resource '" 479 << name 480 << "' with ID " 481 << resId 482 << " but resource already has ID " 483 << ResourceId(package->id.value(), type->id.value(), entry->id.value())); 484 return false; 485 } 486 487 if (resId.isValid()) { 488 package->id = resId.packageId(); 489 type->id = resId.typeId(); 490 entry->id = resId.entryId(); 491 } 492 493 // Only mark the type state as public, it doesn't care about being private. 494 if (symbol.state == SymbolState::kPublic) { 495 type->symbolStatus.state = SymbolState::kPublic; 496 } 497 498 if (symbol.state == SymbolState::kUndefined && 499 entry->symbolStatus.state != SymbolState::kUndefined) { 500 // We can't undefine a symbol (remove its visibility). Ignore. 501 return true; 502 } 503 504 if (symbol.state == SymbolState::kPrivate && 505 entry->symbolStatus.state == SymbolState::kPublic) { 506 // We can't downgrade public to private. Ignore. 507 return true; 508 } 509 510 entry->symbolStatus = std::move(symbol); 511 return true; 512} 513 514Maybe<ResourceTable::SearchResult> 515ResourceTable::findResource(const ResourceNameRef& name) { 516 ResourceTablePackage* package = findPackage(name.package); 517 if (!package) { 518 return {}; 519 } 520 521 ResourceTableType* type = package->findType(name.type); 522 if (!type) { 523 return {}; 524 } 525 526 ResourceEntry* entry = type->findEntry(name.entry); 527 if (!entry) { 528 return {}; 529 } 530 return SearchResult{ package, type, entry }; 531} 532 533} // namespace aapt 534