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 "BinaryResourceParser.h" 18#include "Logger.h" 19#include "ResChunkPullParser.h" 20#include "Resolver.h" 21#include "ResourceParser.h" 22#include "ResourceTable.h" 23#include "ResourceTypeExtensions.h" 24#include "ResourceValues.h" 25#include "Source.h" 26#include "Util.h" 27 28#include <androidfw/ResourceTypes.h> 29#include <androidfw/TypeWrappers.h> 30#include <map> 31#include <string> 32 33namespace aapt { 34 35using namespace android; 36 37/* 38 * Visitor that converts a reference's resource ID to a resource name, 39 * given a mapping from resource ID to resource name. 40 */ 41struct ReferenceIdToNameVisitor : ValueVisitor { 42 ReferenceIdToNameVisitor(const std::shared_ptr<IResolver>& resolver, 43 std::map<ResourceId, ResourceName>* cache) : 44 mResolver(resolver), mCache(cache) { 45 } 46 47 void visit(Reference& reference, ValueVisitorArgs&) override { 48 idToName(reference); 49 } 50 51 void visit(Attribute& attr, ValueVisitorArgs&) override { 52 for (auto& entry : attr.symbols) { 53 idToName(entry.symbol); 54 } 55 } 56 57 void visit(Style& style, ValueVisitorArgs&) override { 58 if (style.parent.id.isValid()) { 59 idToName(style.parent); 60 } 61 62 for (auto& entry : style.entries) { 63 idToName(entry.key); 64 entry.value->accept(*this, {}); 65 } 66 } 67 68 void visit(Styleable& styleable, ValueVisitorArgs&) override { 69 for (auto& attr : styleable.entries) { 70 idToName(attr); 71 } 72 } 73 74 void visit(Array& array, ValueVisitorArgs&) override { 75 for (auto& item : array.items) { 76 item->accept(*this, {}); 77 } 78 } 79 80 void visit(Plural& plural, ValueVisitorArgs&) override { 81 for (auto& item : plural.values) { 82 if (item) { 83 item->accept(*this, {}); 84 } 85 } 86 } 87 88private: 89 void idToName(Reference& reference) { 90 if (!reference.id.isValid()) { 91 return; 92 } 93 94 auto cacheIter = mCache->find(reference.id); 95 if (cacheIter != mCache->end()) { 96 reference.name = cacheIter->second; 97 reference.id = 0; 98 } else { 99 Maybe<ResourceName> result = mResolver->findName(reference.id); 100 if (result) { 101 reference.name = result.value(); 102 103 // Add to cache. 104 mCache->insert({reference.id, reference.name}); 105 106 reference.id = 0; 107 } 108 } 109 } 110 111 std::shared_ptr<IResolver> mResolver; 112 std::map<ResourceId, ResourceName>* mCache; 113}; 114 115 116BinaryResourceParser::BinaryResourceParser(const std::shared_ptr<ResourceTable>& table, 117 const std::shared_ptr<IResolver>& resolver, 118 const Source& source, 119 const std::u16string& defaultPackage, 120 const void* data, 121 size_t len) : 122 mTable(table), mResolver(resolver), mSource(source), mDefaultPackage(defaultPackage), 123 mData(data), mDataLen(len) { 124} 125 126bool BinaryResourceParser::parse() { 127 ResChunkPullParser parser(mData, mDataLen); 128 129 bool error = false; 130 while(ResChunkPullParser::isGoodEvent(parser.next())) { 131 if (parser.getChunk()->type != android::RES_TABLE_TYPE) { 132 Logger::warn(mSource) 133 << "unknown chunk of type '" 134 << parser.getChunk()->type 135 << "'." 136 << std::endl; 137 continue; 138 } 139 140 error |= !parseTable(parser.getChunk()); 141 } 142 143 if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) { 144 Logger::error(mSource) 145 << "bad document: " 146 << parser.getLastError() 147 << "." 148 << std::endl; 149 return false; 150 } 151 return !error; 152} 153 154bool BinaryResourceParser::getSymbol(const void* data, ResourceNameRef* outSymbol) { 155 if (!mSymbolEntries || mSymbolEntryCount == 0) { 156 return false; 157 } 158 159 if (reinterpret_cast<uintptr_t>(data) < reinterpret_cast<uintptr_t>(mData)) { 160 return false; 161 } 162 163 // We only support 32 bit offsets right now. 164 const uintptr_t offset = reinterpret_cast<uintptr_t>(data) - 165 reinterpret_cast<uintptr_t>(mData); 166 if (offset > std::numeric_limits<uint32_t>::max()) { 167 return false; 168 } 169 170 for (size_t i = 0; i < mSymbolEntryCount; i++) { 171 if (mSymbolEntries[i].offset == offset) { 172 // This offset is a symbol! 173 const StringPiece16 str = util::getString(mSymbolPool, 174 mSymbolEntries[i].stringIndex); 175 StringPiece16 typeStr; 176 ResourceParser::extractResourceName(str, &outSymbol->package, &typeStr, 177 &outSymbol->entry); 178 const ResourceType* type = parseResourceType(typeStr); 179 if (!type) { 180 return false; 181 } 182 if (outSymbol->package.empty()) { 183 outSymbol->package = mTable->getPackage(); 184 } 185 outSymbol->type = *type; 186 187 // Since we scan the symbol table in order, we can start looking for the 188 // next symbol from this point. 189 mSymbolEntryCount -= i + 1; 190 mSymbolEntries += i + 1; 191 return true; 192 } 193 } 194 return false; 195} 196 197bool BinaryResourceParser::parseSymbolTable(const ResChunk_header* chunk) { 198 const SymbolTable_header* symbolTableHeader = convertTo<SymbolTable_header>(chunk); 199 if (!symbolTableHeader) { 200 Logger::error(mSource) 201 << "could not parse chunk as SymbolTable_header." 202 << std::endl; 203 return false; 204 } 205 206 const size_t entrySizeBytes = symbolTableHeader->count * sizeof(SymbolTable_entry); 207 if (entrySizeBytes > getChunkDataLen(symbolTableHeader->header)) { 208 Logger::error(mSource) 209 << "entries extend beyond chunk." 210 << std::endl; 211 return false; 212 } 213 214 mSymbolEntries = reinterpret_cast<const SymbolTable_entry*>( 215 getChunkData(symbolTableHeader->header)); 216 mSymbolEntryCount = symbolTableHeader->count; 217 218 ResChunkPullParser parser(getChunkData(symbolTableHeader->header) + entrySizeBytes, 219 getChunkDataLen(symbolTableHeader->header) - entrySizeBytes); 220 if (!ResChunkPullParser::isGoodEvent(parser.next())) { 221 Logger::error(mSource) 222 << "failed to parse chunk: " 223 << parser.getLastError() 224 << "." 225 << std::endl; 226 return false; 227 } 228 229 if (parser.getChunk()->type != android::RES_STRING_POOL_TYPE) { 230 Logger::error(mSource) 231 << "expected Symbol string pool." 232 << std::endl; 233 return false; 234 } 235 236 if (mSymbolPool.setTo(parser.getChunk(), parser.getChunk()->size) != NO_ERROR) { 237 Logger::error(mSource) 238 << "failed to parse symbol string pool with code: " 239 << mSymbolPool.getError() 240 << "." 241 << std::endl; 242 return false; 243 } 244 return true; 245} 246 247bool BinaryResourceParser::parseTable(const ResChunk_header* chunk) { 248 const ResTable_header* tableHeader = convertTo<ResTable_header>(chunk); 249 if (!tableHeader) { 250 Logger::error(mSource) 251 << "could not parse chunk as ResTable_header." 252 << std::endl; 253 return false; 254 } 255 256 ResChunkPullParser parser(getChunkData(tableHeader->header), 257 getChunkDataLen(tableHeader->header)); 258 while (ResChunkPullParser::isGoodEvent(parser.next())) { 259 switch (parser.getChunk()->type) { 260 case android::RES_STRING_POOL_TYPE: 261 if (mValuePool.getError() == NO_INIT) { 262 if (mValuePool.setTo(parser.getChunk(), parser.getChunk()->size) != 263 NO_ERROR) { 264 Logger::error(mSource) 265 << "failed to parse value string pool with code: " 266 << mValuePool.getError() 267 << "." 268 << std::endl; 269 return false; 270 } 271 272 // Reserve some space for the strings we are going to add. 273 mTable->getValueStringPool().hintWillAdd( 274 mValuePool.size(), mValuePool.styleCount()); 275 } else { 276 Logger::warn(mSource) 277 << "unexpected string pool." 278 << std::endl; 279 } 280 break; 281 282 case RES_TABLE_SYMBOL_TABLE_TYPE: 283 if (!parseSymbolTable(parser.getChunk())) { 284 return false; 285 } 286 break; 287 288 case RES_TABLE_SOURCE_POOL_TYPE: { 289 if (mSourcePool.setTo(getChunkData(*parser.getChunk()), 290 getChunkDataLen(*parser.getChunk())) != NO_ERROR) { 291 Logger::error(mSource) 292 << "failed to parse source pool with code: " 293 << mSourcePool.getError() 294 << "." 295 << std::endl; 296 return false; 297 } 298 break; 299 } 300 301 case android::RES_TABLE_PACKAGE_TYPE: 302 if (!parsePackage(parser.getChunk())) { 303 return false; 304 } 305 break; 306 307 default: 308 Logger::warn(mSource) 309 << "unexpected chunk of type " 310 << parser.getChunk()->type 311 << "." 312 << std::endl; 313 break; 314 } 315 } 316 317 if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) { 318 Logger::error(mSource) 319 << "bad resource table: " << parser.getLastError() 320 << "." 321 << std::endl; 322 return false; 323 } 324 return true; 325} 326 327bool BinaryResourceParser::parsePackage(const ResChunk_header* chunk) { 328 if (mValuePool.getError() != NO_ERROR) { 329 Logger::error(mSource) 330 << "no value string pool for ResTable." 331 << std::endl; 332 return false; 333 } 334 335 const ResTable_package* packageHeader = convertTo<ResTable_package>(chunk); 336 if (!packageHeader) { 337 Logger::error(mSource) 338 << "could not parse chunk as ResTable_header." 339 << std::endl; 340 return false; 341 } 342 343 if (mTable->getPackageId() == ResourceTable::kUnsetPackageId) { 344 // This is the first time the table has it's package ID set. 345 mTable->setPackageId(packageHeader->id); 346 } else if (mTable->getPackageId() != packageHeader->id) { 347 Logger::error(mSource) 348 << "ResTable_package has package ID " 349 << std::hex << packageHeader->id << std::dec 350 << " but ResourceTable has package ID " 351 << std::hex << mTable->getPackageId() << std::dec 352 << std::endl; 353 return false; 354 } 355 356 size_t len = strnlen16(reinterpret_cast<const char16_t*>(packageHeader->name), 357 sizeof(packageHeader->name) / sizeof(packageHeader->name[0])); 358 if (mTable->getPackage().empty() && len == 0) { 359 mTable->setPackage(mDefaultPackage); 360 } else if (len > 0) { 361 StringPiece16 thisPackage(reinterpret_cast<const char16_t*>(packageHeader->name), len); 362 if (mTable->getPackage().empty()) { 363 mTable->setPackage(thisPackage); 364 } else if (thisPackage != mTable->getPackage()) { 365 Logger::error(mSource) 366 << "incompatible packages: " 367 << mTable->getPackage() 368 << " vs. " 369 << thisPackage 370 << std::endl; 371 return false; 372 } 373 } 374 375 ResChunkPullParser parser(getChunkData(packageHeader->header), 376 getChunkDataLen(packageHeader->header)); 377 while (ResChunkPullParser::isGoodEvent(parser.next())) { 378 switch (parser.getChunk()->type) { 379 case android::RES_STRING_POOL_TYPE: 380 if (mTypePool.getError() == NO_INIT) { 381 if (mTypePool.setTo(parser.getChunk(), parser.getChunk()->size) != 382 NO_ERROR) { 383 Logger::error(mSource) 384 << "failed to parse type string pool with code " 385 << mTypePool.getError() 386 << "." 387 << std::endl; 388 return false; 389 } 390 } else if (mKeyPool.getError() == NO_INIT) { 391 if (mKeyPool.setTo(parser.getChunk(), parser.getChunk()->size) != 392 NO_ERROR) { 393 Logger::error(mSource) 394 << "failed to parse key string pool with code " 395 << mKeyPool.getError() 396 << "." 397 << std::endl; 398 return false; 399 } 400 } else { 401 Logger::warn(mSource) 402 << "unexpected string pool." 403 << std::endl; 404 } 405 break; 406 407 case android::RES_TABLE_TYPE_SPEC_TYPE: 408 if (!parseTypeSpec(parser.getChunk())) { 409 return false; 410 } 411 break; 412 413 case android::RES_TABLE_TYPE_TYPE: 414 if (!parseType(parser.getChunk())) { 415 return false; 416 } 417 break; 418 419 case RES_TABLE_PUBLIC_TYPE: 420 if (!parsePublic(parser.getChunk())) { 421 return false; 422 } 423 break; 424 425 default: 426 Logger::warn(mSource) 427 << "unexpected chunk of type " 428 << parser.getChunk()->type 429 << "." 430 << std::endl; 431 break; 432 } 433 } 434 435 if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) { 436 Logger::error(mSource) 437 << "bad package: " 438 << parser.getLastError() 439 << "." 440 << std::endl; 441 return false; 442 } 443 444 // Now go through the table and change resource ID references to 445 // symbolic references. 446 447 ReferenceIdToNameVisitor visitor(mResolver, &mIdIndex); 448 for (auto& type : *mTable) { 449 for (auto& entry : type->entries) { 450 for (auto& configValue : entry->values) { 451 configValue.value->accept(visitor, {}); 452 } 453 } 454 } 455 return true; 456} 457 458bool BinaryResourceParser::parsePublic(const ResChunk_header* chunk) { 459 const Public_header* header = convertTo<Public_header>(chunk); 460 461 if (header->typeId == 0) { 462 Logger::error(mSource) 463 << "invalid type ID " << header->typeId << std::endl; 464 return false; 465 } 466 467 const ResourceType* parsedType = parseResourceType(util::getString(mTypePool, 468 header->typeId - 1)); 469 if (!parsedType) { 470 Logger::error(mSource) 471 << "invalid type " << util::getString(mTypePool, header->typeId - 1) << std::endl; 472 return false; 473 } 474 475 const uintptr_t chunkEnd = reinterpret_cast<uintptr_t>(chunk) + chunk->size; 476 const Public_entry* entry = reinterpret_cast<const Public_entry*>( 477 getChunkData(header->header)); 478 for (uint32_t i = 0; i < header->count; i++) { 479 if (reinterpret_cast<uintptr_t>(entry) + sizeof(*entry) > chunkEnd) { 480 Logger::error(mSource) 481 << "Public_entry extends beyond chunk." 482 << std::endl; 483 return false; 484 } 485 486 const ResourceId resId = { mTable->getPackageId(), header->typeId, entry->entryId }; 487 const ResourceName name = { 488 mTable->getPackage(), 489 *parsedType, 490 util::getString(mKeyPool, entry->key.index).toString() }; 491 492 SourceLine source; 493 if (mSourcePool.getError() == NO_ERROR) { 494 source.path = util::utf16ToUtf8(util::getString(mSourcePool, entry->source.index)); 495 source.line = entry->sourceLine; 496 } 497 498 if (!mTable->markPublicAllowMangled(name, resId, source)) { 499 return false; 500 } 501 502 // Add this resource name->id mapping to the index so 503 // that we can resolve all ID references to name references. 504 auto cacheIter = mIdIndex.find(resId); 505 if (cacheIter == mIdIndex.end()) { 506 mIdIndex.insert({ resId, name }); 507 } 508 509 entry++; 510 } 511 return true; 512} 513 514bool BinaryResourceParser::parseTypeSpec(const ResChunk_header* chunk) { 515 if (mTypePool.getError() != NO_ERROR) { 516 Logger::error(mSource) 517 << "no type string pool available for ResTable_typeSpec." 518 << std::endl; 519 return false; 520 } 521 522 const ResTable_typeSpec* typeSpec = convertTo<ResTable_typeSpec>(chunk); 523 if (!typeSpec) { 524 Logger::error(mSource) 525 << "could not parse chunk as ResTable_typeSpec." 526 << std::endl; 527 return false; 528 } 529 530 if (typeSpec->id == 0) { 531 Logger::error(mSource) 532 << "ResTable_typeSpec has invalid id: " 533 << typeSpec->id 534 << "." 535 << std::endl; 536 return false; 537 } 538 return true; 539} 540 541bool BinaryResourceParser::parseType(const ResChunk_header* chunk) { 542 if (mTypePool.getError() != NO_ERROR) { 543 Logger::error(mSource) 544 << "no type string pool available for ResTable_typeSpec." 545 << std::endl; 546 return false; 547 } 548 549 if (mKeyPool.getError() != NO_ERROR) { 550 Logger::error(mSource) 551 << "no key string pool available for ResTable_type." 552 << std::endl; 553 return false; 554 } 555 556 const ResTable_type* type = convertTo<ResTable_type>(chunk); 557 if (!type) { 558 Logger::error(mSource) 559 << "could not parse chunk as ResTable_type." 560 << std::endl; 561 return false; 562 } 563 564 if (type->id == 0) { 565 Logger::error(mSource) 566 << "ResTable_type has invalid id: " 567 << type->id 568 << "." 569 << std::endl; 570 return false; 571 } 572 573 const ConfigDescription config(type->config); 574 const StringPiece16 typeName = util::getString(mTypePool, type->id - 1); 575 576 const ResourceType* parsedType = parseResourceType(typeName); 577 if (!parsedType) { 578 Logger::error(mSource) 579 << "invalid type name '" 580 << typeName 581 << "' for type with ID " 582 << uint32_t(type->id) 583 << "." << std::endl; 584 return false; 585 } 586 587 android::TypeVariant tv(type); 588 for (auto it = tv.beginEntries(); it != tv.endEntries(); ++it) { 589 if (!*it) { 590 continue; 591 } 592 593 const ResTable_entry* entry = *it; 594 const ResourceName name = { 595 mTable->getPackage(), 596 *parsedType, 597 util::getString(mKeyPool, entry->key.index).toString() 598 }; 599 600 const ResourceId resId = { mTable->getPackageId(), type->id, it.index() }; 601 602 std::unique_ptr<Value> resourceValue; 603 const ResTable_entry_source* sourceBlock = nullptr; 604 if (entry->flags & ResTable_entry::FLAG_COMPLEX) { 605 const ResTable_map_entry* mapEntry = static_cast<const ResTable_map_entry*>(entry); 606 if (mapEntry->size - sizeof(*mapEntry) == sizeof(*sourceBlock)) { 607 const uint8_t* data = reinterpret_cast<const uint8_t*>(mapEntry); 608 data += mapEntry->size - sizeof(*sourceBlock); 609 sourceBlock = reinterpret_cast<const ResTable_entry_source*>(data); 610 } 611 612 // TODO(adamlesinski): Check that the entry count is valid. 613 resourceValue = parseMapEntry(name, config, mapEntry); 614 } else { 615 if (entry->size - sizeof(*entry) == sizeof(*sourceBlock)) { 616 const uint8_t* data = reinterpret_cast<const uint8_t*>(entry); 617 data += entry->size - sizeof(*sourceBlock); 618 sourceBlock = reinterpret_cast<const ResTable_entry_source*>(data); 619 } 620 621 const Res_value* value = reinterpret_cast<const Res_value*>( 622 reinterpret_cast<const uint8_t*>(entry) + entry->size); 623 resourceValue = parseValue(name, config, value, entry->flags); 624 } 625 626 if (!resourceValue) { 627 // TODO(adamlesinski): For now this is ok, but it really shouldn't be. 628 continue; 629 } 630 631 SourceLine source = mSource.line(0); 632 if (sourceBlock) { 633 size_t len; 634 const char* str = mSourcePool.string8At(sourceBlock->pathIndex, &len); 635 if (str) { 636 source.path.assign(str, len); 637 } 638 source.line = sourceBlock->line; 639 } 640 641 if (!mTable->addResourceAllowMangled(name, config, source, std::move(resourceValue))) { 642 return false; 643 } 644 645 if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0) { 646 if (!mTable->markPublicAllowMangled(name, resId, mSource.line(0))) { 647 return false; 648 } 649 } 650 651 // Add this resource name->id mapping to the index so 652 // that we can resolve all ID references to name references. 653 auto cacheIter = mIdIndex.find(resId); 654 if (cacheIter == mIdIndex.end()) { 655 mIdIndex.insert({ resId, name }); 656 } 657 } 658 return true; 659} 660 661std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& name, 662 const ConfigDescription& config, 663 const Res_value* value, 664 uint16_t flags) { 665 if (name.type == ResourceType::kId) { 666 return util::make_unique<Id>(); 667 } 668 669 if (value->dataType == Res_value::TYPE_STRING) { 670 StringPiece16 str = util::getString(mValuePool, value->data); 671 672 const ResStringPool_span* spans = mValuePool.styleAt(value->data); 673 if (spans != nullptr) { 674 StyleString styleStr = { str.toString() }; 675 while (spans->name.index != ResStringPool_span::END) { 676 styleStr.spans.push_back(Span{ 677 util::getString(mValuePool, spans->name.index).toString(), 678 spans->firstChar, 679 spans->lastChar 680 }); 681 spans++; 682 } 683 return util::make_unique<StyledString>( 684 mTable->getValueStringPool().makeRef( 685 styleStr, StringPool::Context{1, config})); 686 } else { 687 if (name.type != ResourceType::kString && 688 util::stringStartsWith<char16_t>(str, u"res/")) { 689 // This must be a FileReference. 690 return util::make_unique<FileReference>(mTable->getValueStringPool().makeRef( 691 str, StringPool::Context{ 0, config })); 692 } 693 694 // There are no styles associated with this string, so treat it as 695 // a simple string. 696 return util::make_unique<String>( 697 mTable->getValueStringPool().makeRef( 698 str, StringPool::Context{1, config})); 699 } 700 } 701 702 if (value->dataType == Res_value::TYPE_REFERENCE || 703 value->dataType == Res_value::TYPE_ATTRIBUTE) { 704 const Reference::Type type = (value->dataType == Res_value::TYPE_REFERENCE) ? 705 Reference::Type::kResource : Reference::Type::kAttribute; 706 707 if (value->data != 0) { 708 // This is a normal reference. 709 return util::make_unique<Reference>(value->data, type); 710 } 711 712 // This reference has an invalid ID. Check if it is an unresolved symbol. 713 ResourceNameRef symbol; 714 if (getSymbol(&value->data, &symbol)) { 715 return util::make_unique<Reference>(symbol, type); 716 } 717 718 // This is not an unresolved symbol, so it must be the magic @null reference. 719 Res_value nullType = {}; 720 nullType.dataType = Res_value::TYPE_REFERENCE; 721 return util::make_unique<BinaryPrimitive>(nullType); 722 } 723 724 if (value->dataType == ExtendedTypes::TYPE_RAW_STRING) { 725 return util::make_unique<RawString>( 726 mTable->getValueStringPool().makeRef(util::getString(mValuePool, value->data), 727 StringPool::Context{ 1, config })); 728 } 729 730 // Treat this as a raw binary primitive. 731 return util::make_unique<BinaryPrimitive>(*value); 732} 733 734std::unique_ptr<Value> BinaryResourceParser::parseMapEntry(const ResourceNameRef& name, 735 const ConfigDescription& config, 736 const ResTable_map_entry* map) { 737 switch (name.type) { 738 case ResourceType::kStyle: 739 return parseStyle(name, config, map); 740 case ResourceType::kAttr: 741 return parseAttr(name, config, map); 742 case ResourceType::kArray: 743 return parseArray(name, config, map); 744 case ResourceType::kStyleable: 745 return parseStyleable(name, config, map); 746 case ResourceType::kPlurals: 747 return parsePlural(name, config, map); 748 default: 749 break; 750 } 751 return {}; 752} 753 754std::unique_ptr<Style> BinaryResourceParser::parseStyle(const ResourceNameRef& name, 755 const ConfigDescription& config, 756 const ResTable_map_entry* map) { 757 std::unique_ptr<Style> style = util::make_unique<Style>(); 758 if (map->parent.ident == 0) { 759 // The parent is either not set or it is an unresolved symbol. 760 // Check to see if it is a symbol. 761 ResourceNameRef symbol; 762 if (getSymbol(&map->parent.ident, &symbol)) { 763 style->parent.name = symbol.toResourceName(); 764 } 765 } else { 766 // The parent is a regular reference to a resource. 767 style->parent.id = map->parent.ident; 768 } 769 770 for (const ResTable_map& mapEntry : map) { 771 style->entries.emplace_back(); 772 Style::Entry& styleEntry = style->entries.back(); 773 774 if (mapEntry.name.ident == 0) { 775 // The map entry's key (attribute) is not set. This must be 776 // a symbol reference, so resolve it. 777 ResourceNameRef symbol; 778 bool result = getSymbol(&mapEntry.name.ident, &symbol); 779 assert(result); 780 styleEntry.key.name = symbol.toResourceName(); 781 } else { 782 // The map entry's key (attribute) is a regular reference. 783 styleEntry.key.id = mapEntry.name.ident; 784 } 785 786 // Parse the attribute's value. 787 styleEntry.value = parseValue(name, config, &mapEntry.value, 0); 788 assert(styleEntry.value); 789 } 790 return style; 791} 792 793std::unique_ptr<Attribute> BinaryResourceParser::parseAttr(const ResourceNameRef& name, 794 const ConfigDescription& config, 795 const ResTable_map_entry* map) { 796 const bool isWeak = (map->flags & ResTable_entry::FLAG_WEAK) != 0; 797 std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak); 798 799 // First we must discover what type of attribute this is. Find the type mask. 800 auto typeMaskIter = std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool { 801 return entry.name.ident == ResTable_map::ATTR_TYPE; 802 }); 803 804 if (typeMaskIter != end(map)) { 805 attr->typeMask = typeMaskIter->value.data; 806 } 807 808 if (attr->typeMask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) { 809 for (const ResTable_map& mapEntry : map) { 810 if (Res_INTERNALID(mapEntry.name.ident)) { 811 continue; 812 } 813 814 Attribute::Symbol symbol; 815 symbol.value = mapEntry.value.data; 816 if (mapEntry.name.ident == 0) { 817 // The map entry's key (id) is not set. This must be 818 // a symbol reference, so resolve it. 819 ResourceNameRef symbolName; 820 bool result = getSymbol(&mapEntry.name.ident, &symbolName); 821 assert(result); 822 symbol.symbol.name = symbolName.toResourceName(); 823 } else { 824 // The map entry's key (id) is a regular reference. 825 symbol.symbol.id = mapEntry.name.ident; 826 } 827 828 attr->symbols.push_back(std::move(symbol)); 829 } 830 } 831 832 // TODO(adamlesinski): Find min, max, i80n, etc attributes. 833 return attr; 834} 835 836std::unique_ptr<Array> BinaryResourceParser::parseArray(const ResourceNameRef& name, 837 const ConfigDescription& config, 838 const ResTable_map_entry* map) { 839 std::unique_ptr<Array> array = util::make_unique<Array>(); 840 for (const ResTable_map& mapEntry : map) { 841 array->items.push_back(parseValue(name, config, &mapEntry.value, 0)); 842 } 843 return array; 844} 845 846std::unique_ptr<Styleable> BinaryResourceParser::parseStyleable(const ResourceNameRef& name, 847 const ConfigDescription& config, 848 const ResTable_map_entry* map) { 849 std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>(); 850 for (const ResTable_map& mapEntry : map) { 851 if (mapEntry.name.ident == 0) { 852 // The map entry's key (attribute) is not set. This must be 853 // a symbol reference, so resolve it. 854 ResourceNameRef symbol; 855 bool result = getSymbol(&mapEntry.name.ident, &symbol); 856 assert(result); 857 styleable->entries.emplace_back(symbol); 858 } else { 859 // The map entry's key (attribute) is a regular reference. 860 styleable->entries.emplace_back(mapEntry.name.ident); 861 } 862 } 863 return styleable; 864} 865 866std::unique_ptr<Plural> BinaryResourceParser::parsePlural(const ResourceNameRef& name, 867 const ConfigDescription& config, 868 const ResTable_map_entry* map) { 869 std::unique_ptr<Plural> plural = util::make_unique<Plural>(); 870 for (const ResTable_map& mapEntry : map) { 871 std::unique_ptr<Item> item = parseValue(name, config, &mapEntry.value, 0); 872 873 switch (mapEntry.name.ident) { 874 case android::ResTable_map::ATTR_ZERO: 875 plural->values[Plural::Zero] = std::move(item); 876 break; 877 case android::ResTable_map::ATTR_ONE: 878 plural->values[Plural::One] = std::move(item); 879 break; 880 case android::ResTable_map::ATTR_TWO: 881 plural->values[Plural::Two] = std::move(item); 882 break; 883 case android::ResTable_map::ATTR_FEW: 884 plural->values[Plural::Few] = std::move(item); 885 break; 886 case android::ResTable_map::ATTR_MANY: 887 plural->values[Plural::Many] = std::move(item); 888 break; 889 case android::ResTable_map::ATTR_OTHER: 890 plural->values[Plural::Other] = std::move(item); 891 break; 892 } 893 } 894 return plural; 895} 896 897} // namespace aapt 898