blob: 4f1947ab8364f3d31d2637b348bcacd23e60189c [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,
Adam Lesinski6cc479b2015-06-12 15:45:48 -0700119 const std::u16string& defaultPackage,
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800120 const void* data,
121 size_t len) :
Adam Lesinski6cc479b2015-06-12 15:45:48 -0700122 mTable(table), mResolver(resolver), mSource(source), mDefaultPackage(defaultPackage),
123 mData(data), mDataLen(len) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800124}
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
Adam Lesinskica2fc352015-04-03 12:08:26 -0700159 if (reinterpret_cast<uintptr_t>(data) < reinterpret_cast<uintptr_t>(mData)) {
160 return false;
161 }
162
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800163 // We only support 32 bit offsets right now.
Adam Lesinskica2fc352015-04-03 12:08:26 -0700164 const uintptr_t offset = reinterpret_cast<uintptr_t>(data) -
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800165 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 }
Adam Lesinski6cc479b2015-06-12 15:45:48 -0700182 if (outSymbol->package.empty()) {
183 outSymbol->package = mTable->getPackage();
184 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800185 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
Adam Lesinskica2fc352015-04-03 12:08:26 -0700236 if (mSymbolPool.setTo(parser.getChunk(), parser.getChunk()->size) != NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800237 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:
Adam Lesinskica2fc352015-04-03 12:08:26 -0700261 if (mValuePool.getError() == NO_INIT) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800262 if (mValuePool.setTo(parser.getChunk(), parser.getChunk()->size) !=
Adam Lesinskica2fc352015-04-03 12:08:26 -0700263 NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800264 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()),
Adam Lesinskica2fc352015-04-03 12:08:26 -0700290 getChunkDataLen(*parser.getChunk())) != NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800291 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) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700328 if (mValuePool.getError() != NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800329 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]));
Adam Lesinski6cc479b2015-06-12 15:45:48 -0700358 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 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800374
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:
Adam Lesinskica2fc352015-04-03 12:08:26 -0700380 if (mTypePool.getError() == NO_INIT) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800381 if (mTypePool.setTo(parser.getChunk(), parser.getChunk()->size) !=
Adam Lesinskica2fc352015-04-03 12:08:26 -0700382 NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800383 Logger::error(mSource)
384 << "failed to parse type string pool with code "
385 << mTypePool.getError()
386 << "."
387 << std::endl;
388 return false;
389 }
Adam Lesinskica2fc352015-04-03 12:08:26 -0700390 } else if (mKeyPool.getError() == NO_INIT) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800391 if (mKeyPool.setTo(parser.getChunk(), parser.getChunk()->size) !=
Adam Lesinskica2fc352015-04-03 12:08:26 -0700392 NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800393 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
Adam Lesinski6ff19662015-04-30 17:40:46 -0700419 case RES_TABLE_PUBLIC_TYPE:
420 if (!parsePublic(parser.getChunk())) {
421 return false;
422 }
423 break;
424
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800425 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
Adam Lesinski769de982015-04-10 19:43:55 -0700447 ReferenceIdToNameVisitor visitor(mResolver, &mIdIndex);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800448 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
Adam Lesinski6ff19662015-04-30 17:40:46 -0700458bool 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
Adam Lesinski330edcd2015-05-04 17:40:56 -0700498 if (!mTable->markPublicAllowMangled(name, resId, source)) {
Adam Lesinski6ff19662015-04-30 17:40:46 -0700499 return false;
500 }
501
Adam Lesinski330edcd2015-05-04 17:40:56 -0700502 // 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
Adam Lesinski6ff19662015-04-30 17:40:46 -0700509 entry++;
510 }
511 return true;
512}
513
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800514bool BinaryResourceParser::parseTypeSpec(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
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) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700542 if (mTypePool.getError() != NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800543 Logger::error(mSource)
544 << "no type string pool available for ResTable_typeSpec."
545 << std::endl;
546 return false;
547 }
548
Adam Lesinskica2fc352015-04-03 12:08:26 -0700549 if (mKeyPool.getError() != NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800550 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
Adam Lesinski330edcd2015-05-04 17:40:56 -0700641 if (!mTable->addResourceAllowMangled(name, config, source, std::move(resourceValue))) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800642 return false;
643 }
644
645 if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0) {
Adam Lesinski330edcd2015-05-04 17:40:56 -0700646 if (!mTable->markPublicAllowMangled(name, resId, mSource.line(0))) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800647 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) {
Adam Lesinski330edcd2015-05-04 17:40:56 -0700665 if (name.type == ResourceType::kId) {
666 return util::make_unique<Id>();
667 }
668
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800669 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 {
Adam Lesinskid5c4f872015-04-21 13:56:10 -0700687 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
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800694 // 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 = {};
Adam Lesinskidfa5e072015-05-12 21:42:59 -0700720 nullType.dataType = Res_value::TYPE_REFERENCE;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800721 return util::make_unique<BinaryPrimitive>(nullType);
722 }
723
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800724 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
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800730 // 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) {
Adam Lesinskibdaa0922015-05-08 20:16:23 -0700757 std::unique_ptr<Style> style = util::make_unique<Style>();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800758 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
Adam Lesinski330edcd2015-05-04 17:40:56 -0700814 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));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800829 }
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) {
Adam Lesinski769de982015-04-10 19:43:55 -0700851 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 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800862 }
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