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 
33 namespace aapt {
34 
35 using 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  */
41 struct ReferenceIdToNameVisitor : ValueVisitor {
ReferenceIdToNameVisitoraapt::ReferenceIdToNameVisitor42     ReferenceIdToNameVisitor(const std::shared_ptr<IResolver>& resolver,
43                              std::map<ResourceId, ResourceName>* cache) :
44             mResolver(resolver), mCache(cache) {
45     }
46 
visitaapt::ReferenceIdToNameVisitor47     void visit(Reference& reference, ValueVisitorArgs&) override {
48         idToName(reference);
49     }
50 
visitaapt::ReferenceIdToNameVisitor51     void visit(Attribute& attr, ValueVisitorArgs&) override {
52         for (auto& entry : attr.symbols) {
53             idToName(entry.symbol);
54         }
55     }
56 
visitaapt::ReferenceIdToNameVisitor57     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 
visitaapt::ReferenceIdToNameVisitor68     void visit(Styleable& styleable, ValueVisitorArgs&) override {
69         for (auto& attr : styleable.entries) {
70             idToName(attr);
71         }
72     }
73 
visitaapt::ReferenceIdToNameVisitor74     void visit(Array& array, ValueVisitorArgs&) override {
75         for (auto& item : array.items) {
76             item->accept(*this, {});
77         }
78     }
79 
visitaapt::ReferenceIdToNameVisitor80     void visit(Plural& plural, ValueVisitorArgs&) override {
81         for (auto& item : plural.values) {
82             if (item) {
83                 item->accept(*this, {});
84             }
85         }
86     }
87 
88 private:
idToNameaapt::ReferenceIdToNameVisitor89     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 
BinaryResourceParser(const std::shared_ptr<ResourceTable> & table,const std::shared_ptr<IResolver> & resolver,const Source & source,const std::u16string & defaultPackage,const void * data,size_t len)116 BinaryResourceParser::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 
parse()126 bool 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 
getSymbol(const void * data,ResourceNameRef * outSymbol)154 bool 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 
parseSymbolTable(const ResChunk_header * chunk)197 bool 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 
parseTable(const ResChunk_header * chunk)247 bool 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 
parsePackage(const ResChunk_header * chunk)327 bool 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 
parsePublic(const ResChunk_header * chunk)458 bool 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 
parseTypeSpec(const ResChunk_header * chunk)514 bool 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 
parseType(const ResChunk_header * chunk)541 bool 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 
parseValue(const ResourceNameRef & name,const ConfigDescription & config,const Res_value * value,uint16_t flags)661 std::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 
parseMapEntry(const ResourceNameRef & name,const ConfigDescription & config,const ResTable_map_entry * map)734 std::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 
parseStyle(const ResourceNameRef & name,const ConfigDescription & config,const ResTable_map_entry * map)754 std::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 
parseAttr(const ResourceNameRef & name,const ConfigDescription & config,const ResTable_map_entry * map)793 std::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 
parseArray(const ResourceNameRef & name,const ConfigDescription & config,const ResTable_map_entry * map)836 std::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 
parseStyleable(const ResourceNameRef & name,const ConfigDescription & config,const ResTable_map_entry * map)846 std::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 
parsePlural(const ResourceNameRef & name,const ConfigDescription & config,const ResTable_map_entry * map)866 std::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