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