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