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