• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "ResourceTable.h"
18 #include "ResourceUtils.h"
19 #include "ResourceValues.h"
20 #include "Source.h"
21 #include "ValueVisitor.h"
22 #include "unflatten/BinaryResourceParser.h"
23 #include "unflatten/ResChunkPullParser.h"
24 #include "util/Util.h"
25 
26 #include <androidfw/ResourceTypes.h>
27 #include <androidfw/TypeWrappers.h>
28 #include <android-base/macros.h>
29 #include <algorithm>
30 #include <map>
31 #include <string>
32 
33 namespace aapt {
34 
35 using namespace android;
36 
37 namespace {
38 
39 /*
40  * Visitor that converts a reference's resource ID to a resource name,
41  * given a mapping from resource ID to resource name.
42  */
43 class ReferenceIdToNameVisitor : public ValueVisitor {
44 private:
45     const std::map<ResourceId, ResourceName>* mMapping;
46 
47 public:
48     using ValueVisitor::visit;
49 
ReferenceIdToNameVisitor(const std::map<ResourceId,ResourceName> * mapping)50     ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceName>* mapping) :
51             mMapping(mapping) {
52         assert(mMapping);
53     }
54 
visit(Reference * reference)55     void visit(Reference* reference) override {
56         if (!reference->id || !reference->id.value().isValid()) {
57             return;
58         }
59 
60         ResourceId id = reference->id.value();
61         auto cacheIter = mMapping->find(id);
62         if (cacheIter != mMapping->end()) {
63             reference->name = cacheIter->second;
64             reference->id = {};
65         }
66     }
67 };
68 
69 } // namespace
70 
BinaryResourceParser(IAaptContext * context,ResourceTable * table,const Source & source,const void * data,size_t len)71 BinaryResourceParser::BinaryResourceParser(IAaptContext* context, ResourceTable* table,
72                                            const Source& source, const void* data, size_t len) :
73         mContext(context), mTable(table), mSource(source), mData(data), mDataLen(len) {
74 }
75 
parse()76 bool BinaryResourceParser::parse() {
77     ResChunkPullParser parser(mData, mDataLen);
78 
79     bool error = false;
80     while(ResChunkPullParser::isGoodEvent(parser.next())) {
81         if (parser.getChunk()->type != android::RES_TABLE_TYPE) {
82             mContext->getDiagnostics()->warn(DiagMessage(mSource)
83                                              << "unknown chunk of type '"
84                                              << (int) parser.getChunk()->type << "'");
85             continue;
86         }
87 
88         if (!parseTable(parser.getChunk())) {
89             error = true;
90         }
91     }
92 
93     if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
94         mContext->getDiagnostics()->error(DiagMessage(mSource)
95                                           << "corrupt resource table: "
96                                           << parser.getLastError());
97         return false;
98     }
99     return !error;
100 }
101 
102 /**
103  * Parses the resource table, which contains all the packages, types, and entries.
104  */
parseTable(const ResChunk_header * chunk)105 bool BinaryResourceParser::parseTable(const ResChunk_header* chunk) {
106     const ResTable_header* tableHeader = convertTo<ResTable_header>(chunk);
107     if (!tableHeader) {
108         mContext->getDiagnostics()->error(DiagMessage(mSource) << "corrupt ResTable_header chunk");
109         return false;
110     }
111 
112     ResChunkPullParser parser(getChunkData(&tableHeader->header),
113                               getChunkDataLen(&tableHeader->header));
114     while (ResChunkPullParser::isGoodEvent(parser.next())) {
115         switch (util::deviceToHost16(parser.getChunk()->type)) {
116         case android::RES_STRING_POOL_TYPE:
117             if (mValuePool.getError() == NO_INIT) {
118                 status_t err = mValuePool.setTo(parser.getChunk(),
119                                                 util::deviceToHost32(parser.getChunk()->size));
120                 if (err != NO_ERROR) {
121                     mContext->getDiagnostics()->error(DiagMessage(mSource)
122                                                       << "corrupt string pool in ResTable: "
123                                                       << mValuePool.getError());
124                     return false;
125                 }
126 
127                 // Reserve some space for the strings we are going to add.
128                 mTable->stringPool.hintWillAdd(mValuePool.size(), mValuePool.styleCount());
129             } else {
130                 mContext->getDiagnostics()->warn(DiagMessage(mSource)
131                                                  << "unexpected string pool in ResTable");
132             }
133             break;
134 
135         case android::RES_TABLE_PACKAGE_TYPE:
136             if (!parsePackage(parser.getChunk())) {
137                 return false;
138             }
139             break;
140 
141         default:
142             mContext->getDiagnostics()
143                     ->warn(DiagMessage(mSource)
144                            << "unexpected chunk type "
145                            << (int) util::deviceToHost16(parser.getChunk()->type));
146             break;
147         }
148     }
149 
150     if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
151         mContext->getDiagnostics()->error(DiagMessage(mSource)
152                                           << "corrupt resource table: " << parser.getLastError());
153         return false;
154     }
155     return true;
156 }
157 
158 
parsePackage(const ResChunk_header * chunk)159 bool BinaryResourceParser::parsePackage(const ResChunk_header* chunk) {
160     const ResTable_package* packageHeader = convertTo<ResTable_package>(chunk);
161     if (!packageHeader) {
162         mContext->getDiagnostics()->error(DiagMessage(mSource)
163                                           << "corrupt ResTable_package chunk");
164         return false;
165     }
166 
167     uint32_t packageId = util::deviceToHost32(packageHeader->id);
168     if (packageId > std::numeric_limits<uint8_t>::max()) {
169         mContext->getDiagnostics()->error(DiagMessage(mSource)
170                                           << "package ID is too big (" << packageId << ")");
171         return false;
172     }
173 
174     // Extract the package name.
175     size_t len = strnlen16((const char16_t*) packageHeader->name, arraysize(packageHeader->name));
176     std::u16string packageName;
177     packageName.resize(len);
178     for (size_t i = 0; i < len; i++) {
179         packageName[i] = util::deviceToHost16(packageHeader->name[i]);
180     }
181 
182     ResourceTablePackage* package = mTable->createPackage(packageName, (uint8_t) packageId);
183     if (!package) {
184         mContext->getDiagnostics()->error(DiagMessage(mSource)
185                                           << "incompatible package '" << packageName
186                                           << "' with ID " << packageId);
187         return false;
188     }
189 
190     // There can be multiple packages in a table, so
191     // clear the type and key pool in case they were set from a previous package.
192     mTypePool.uninit();
193     mKeyPool.uninit();
194 
195     ResChunkPullParser parser(getChunkData(&packageHeader->header),
196                               getChunkDataLen(&packageHeader->header));
197     while (ResChunkPullParser::isGoodEvent(parser.next())) {
198         switch (util::deviceToHost16(parser.getChunk()->type)) {
199         case android::RES_STRING_POOL_TYPE:
200             if (mTypePool.getError() == NO_INIT) {
201                 status_t err = mTypePool.setTo(parser.getChunk(),
202                                                util::deviceToHost32(parser.getChunk()->size));
203                 if (err != NO_ERROR) {
204                     mContext->getDiagnostics()->error(DiagMessage(mSource)
205                                                       << "corrupt type string pool in "
206                                                       << "ResTable_package: "
207                                                       << mTypePool.getError());
208                     return false;
209                 }
210             } else if (mKeyPool.getError() == NO_INIT) {
211                 status_t err = mKeyPool.setTo(parser.getChunk(),
212                                               util::deviceToHost32(parser.getChunk()->size));
213                 if (err != NO_ERROR) {
214                     mContext->getDiagnostics()->error(DiagMessage(mSource)
215                                                       << "corrupt key string pool in "
216                                                       << "ResTable_package: "
217                                                       << mKeyPool.getError());
218                     return false;
219                 }
220             } else {
221                 mContext->getDiagnostics()->warn(DiagMessage(mSource) << "unexpected string pool");
222             }
223             break;
224 
225         case android::RES_TABLE_TYPE_SPEC_TYPE:
226             if (!parseTypeSpec(parser.getChunk())) {
227                 return false;
228             }
229             break;
230 
231         case android::RES_TABLE_TYPE_TYPE:
232             if (!parseType(package, parser.getChunk())) {
233                 return false;
234             }
235             break;
236 
237         default:
238             mContext->getDiagnostics()
239                     ->warn(DiagMessage(mSource)
240                            << "unexpected chunk type "
241                            << (int) util::deviceToHost16(parser.getChunk()->type));
242             break;
243         }
244     }
245 
246     if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
247         mContext->getDiagnostics()->error(DiagMessage(mSource)
248                                           << "corrupt ResTable_package: "
249                                           << parser.getLastError());
250         return false;
251     }
252 
253     // Now go through the table and change local resource ID references to
254     // symbolic references.
255     ReferenceIdToNameVisitor visitor(&mIdIndex);
256     visitAllValuesInTable(mTable, &visitor);
257     return true;
258 }
259 
parseTypeSpec(const ResChunk_header * chunk)260 bool BinaryResourceParser::parseTypeSpec(const ResChunk_header* chunk) {
261     if (mTypePool.getError() != NO_ERROR) {
262         mContext->getDiagnostics()->error(DiagMessage(mSource)
263                                           << "missing type string pool");
264         return false;
265     }
266 
267     const ResTable_typeSpec* typeSpec = convertTo<ResTable_typeSpec>(chunk);
268     if (!typeSpec) {
269         mContext->getDiagnostics()->error(DiagMessage(mSource)
270                                           << "corrupt ResTable_typeSpec chunk");
271         return false;
272     }
273 
274     if (typeSpec->id == 0) {
275         mContext->getDiagnostics()->error(DiagMessage(mSource)
276                                           << "ResTable_typeSpec has invalid id: " << typeSpec->id);
277         return false;
278     }
279     return true;
280 }
281 
parseType(const ResourceTablePackage * package,const ResChunk_header * chunk)282 bool BinaryResourceParser::parseType(const ResourceTablePackage* package,
283                                      const ResChunk_header* chunk) {
284     if (mTypePool.getError() != NO_ERROR) {
285         mContext->getDiagnostics()->error(DiagMessage(mSource)
286                                           << "missing type string pool");
287         return false;
288     }
289 
290     if (mKeyPool.getError() != NO_ERROR) {
291         mContext->getDiagnostics()->error(DiagMessage(mSource)
292                                           << "missing key string pool");
293         return false;
294     }
295 
296     const ResTable_type* type = convertTo<ResTable_type>(chunk);
297     if (!type) {
298         mContext->getDiagnostics()->error(DiagMessage(mSource)
299                                           << "corrupt ResTable_type chunk");
300         return false;
301     }
302 
303     if (type->id == 0) {
304         mContext->getDiagnostics()->error(DiagMessage(mSource)
305                                           << "ResTable_type has invalid id: " << (int) type->id);
306         return false;
307     }
308 
309     ConfigDescription config;
310     config.copyFromDtoH(type->config);
311 
312     StringPiece16 typeStr16 = util::getString(mTypePool, type->id - 1);
313 
314     const ResourceType* parsedType = parseResourceType(typeStr16);
315     if (!parsedType) {
316         mContext->getDiagnostics()->error(DiagMessage(mSource)
317                                           << "invalid type name '" << typeStr16
318                                           << "' for type with ID " << (int) type->id);
319         return false;
320     }
321 
322     TypeVariant tv(type);
323     for (auto it = tv.beginEntries(); it != tv.endEntries(); ++it) {
324         const ResTable_entry* entry = *it;
325         if (!entry) {
326             continue;
327         }
328 
329         const ResourceName name(package->name, *parsedType,
330                                 util::getString(mKeyPool,
331                                                 util::deviceToHost32(entry->key.index)).toString());
332 
333         const ResourceId resId(package->id.value(), type->id, static_cast<uint16_t>(it.index()));
334 
335         std::unique_ptr<Value> resourceValue;
336         if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
337             const ResTable_map_entry* mapEntry = static_cast<const ResTable_map_entry*>(entry);
338 
339             // TODO(adamlesinski): Check that the entry count is valid.
340             resourceValue = parseMapEntry(name, config, mapEntry);
341         } else {
342             const Res_value* value = (const Res_value*)(
343                     (const uint8_t*) entry + util::deviceToHost32(entry->size));
344             resourceValue = parseValue(name, config, value, entry->flags);
345         }
346 
347         if (!resourceValue) {
348             mContext->getDiagnostics()->error(DiagMessage(mSource)
349                                               << "failed to parse value for resource " << name
350                                               << " (" << resId << ") with configuration '"
351                                               << config << "'");
352             return false;
353         }
354 
355         if (!mTable->addResourceAllowMangled(name, config, {}, std::move(resourceValue),
356                                              mContext->getDiagnostics())) {
357             return false;
358         }
359 
360         if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0) {
361             Symbol symbol;
362             symbol.state = SymbolState::kPublic;
363             symbol.source = mSource.withLine(0);
364             if (!mTable->setSymbolStateAllowMangled(name, resId, symbol,
365                                                     mContext->getDiagnostics())) {
366                 return false;
367             }
368         }
369 
370         // Add this resource name->id mapping to the index so
371         // that we can resolve all ID references to name references.
372         auto cacheIter = mIdIndex.find(resId);
373         if (cacheIter == mIdIndex.end()) {
374             mIdIndex.insert({ resId, name });
375         }
376     }
377     return true;
378 }
379 
parseValue(const ResourceNameRef & name,const ConfigDescription & config,const Res_value * value,uint16_t flags)380 std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& name,
381                                                        const ConfigDescription& config,
382                                                        const Res_value* value,
383                                                        uint16_t flags) {
384     if (name.type == ResourceType::kId) {
385         return util::make_unique<Id>();
386     }
387 
388     const uint32_t data = util::deviceToHost32(value->data);
389 
390     if (value->dataType == Res_value::TYPE_STRING) {
391         StringPiece16 str = util::getString(mValuePool, data);
392 
393         const ResStringPool_span* spans = mValuePool.styleAt(data);
394 
395         // Check if the string has a valid style associated with it.
396         if (spans != nullptr && spans->name.index != ResStringPool_span::END) {
397             StyleString styleStr = { str.toString() };
398             while (spans->name.index != ResStringPool_span::END) {
399                 styleStr.spans.push_back(Span{
400                         util::getString(mValuePool, spans->name.index).toString(),
401                         spans->firstChar,
402                         spans->lastChar
403                 });
404                 spans++;
405             }
406             return util::make_unique<StyledString>(mTable->stringPool.makeRef(
407                     styleStr, StringPool::Context{1, config}));
408         } else {
409             if (name.type != ResourceType::kString &&
410                     util::stringStartsWith<char16_t>(str, u"res/")) {
411                 // This must be a FileReference.
412                 return util::make_unique<FileReference>(mTable->stringPool.makeRef(
413                             str, StringPool::Context{ 0, config }));
414             }
415 
416             // There are no styles associated with this string, so treat it as
417             // a simple string.
418             return util::make_unique<String>(mTable->stringPool.makeRef(
419                     str, StringPool::Context{1, config}));
420         }
421     }
422 
423     if (value->dataType == Res_value::TYPE_REFERENCE ||
424             value->dataType == Res_value::TYPE_ATTRIBUTE) {
425         const Reference::Type type = (value->dataType == Res_value::TYPE_REFERENCE) ?
426                 Reference::Type::kResource : Reference::Type::kAttribute;
427 
428         if (data == 0) {
429             // A reference of 0, must be the magic @null reference.
430             Res_value nullType = {};
431             nullType.dataType = Res_value::TYPE_REFERENCE;
432             return util::make_unique<BinaryPrimitive>(nullType);
433         }
434 
435         // This is a normal reference.
436         return util::make_unique<Reference>(data, type);
437     }
438 
439     // Treat this as a raw binary primitive.
440     return util::make_unique<BinaryPrimitive>(*value);
441 }
442 
parseMapEntry(const ResourceNameRef & name,const ConfigDescription & config,const ResTable_map_entry * map)443 std::unique_ptr<Value> BinaryResourceParser::parseMapEntry(const ResourceNameRef& name,
444                                                            const ConfigDescription& config,
445                                                            const ResTable_map_entry* map) {
446     switch (name.type) {
447         case ResourceType::kStyle:
448             return parseStyle(name, config, map);
449         case ResourceType::kAttrPrivate:
450             // fallthrough
451         case ResourceType::kAttr:
452             return parseAttr(name, config, map);
453         case ResourceType::kArray:
454             return parseArray(name, config, map);
455         case ResourceType::kPlurals:
456             return parsePlural(name, config, map);
457         default:
458             assert(false && "unknown map type");
459             break;
460     }
461     return {};
462 }
463 
parseStyle(const ResourceNameRef & name,const ConfigDescription & config,const ResTable_map_entry * map)464 std::unique_ptr<Style> BinaryResourceParser::parseStyle(const ResourceNameRef& name,
465                                                         const ConfigDescription& config,
466                                                         const ResTable_map_entry* map) {
467     std::unique_ptr<Style> style = util::make_unique<Style>();
468     if (util::deviceToHost32(map->parent.ident) != 0) {
469         // The parent is a regular reference to a resource.
470         style->parent = Reference(util::deviceToHost32(map->parent.ident));
471     }
472 
473     for (const ResTable_map& mapEntry : map) {
474         if (Res_INTERNALID(util::deviceToHost32(mapEntry.name.ident))) {
475             continue;
476         }
477 
478         Style::Entry styleEntry;
479         styleEntry.key = Reference(util::deviceToHost32(mapEntry.name.ident));
480         styleEntry.value = parseValue(name, config, &mapEntry.value, 0);
481         if (!styleEntry.value) {
482             return {};
483         }
484         style->entries.push_back(std::move(styleEntry));
485     }
486     return style;
487 }
488 
parseAttr(const ResourceNameRef & name,const ConfigDescription & config,const ResTable_map_entry * map)489 std::unique_ptr<Attribute> BinaryResourceParser::parseAttr(const ResourceNameRef& name,
490                                                            const ConfigDescription& config,
491                                                            const ResTable_map_entry* map) {
492     const bool isWeak = (util::deviceToHost16(map->flags) & ResTable_entry::FLAG_WEAK) != 0;
493     std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
494 
495     // First we must discover what type of attribute this is. Find the type mask.
496     auto typeMaskIter = std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool {
497         return util::deviceToHost32(entry.name.ident) == ResTable_map::ATTR_TYPE;
498     });
499 
500     if (typeMaskIter != end(map)) {
501         attr->typeMask = util::deviceToHost32(typeMaskIter->value.data);
502     }
503 
504     for (const ResTable_map& mapEntry : map) {
505         if (Res_INTERNALID(util::deviceToHost32(mapEntry.name.ident))) {
506             switch (util::deviceToHost32(mapEntry.name.ident)) {
507             case ResTable_map::ATTR_MIN:
508                 attr->minInt = static_cast<int32_t>(mapEntry.value.data);
509                 break;
510             case ResTable_map::ATTR_MAX:
511                 attr->maxInt = static_cast<int32_t>(mapEntry.value.data);
512                 break;
513             }
514             continue;
515         }
516 
517         if (attr->typeMask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
518             Attribute::Symbol symbol;
519             symbol.value = util::deviceToHost32(mapEntry.value.data);
520             symbol.symbol = Reference(util::deviceToHost32(mapEntry.name.ident));
521             attr->symbols.push_back(std::move(symbol));
522         }
523     }
524 
525     // TODO(adamlesinski): Find i80n, attributes.
526     return attr;
527 }
528 
parseArray(const ResourceNameRef & name,const ConfigDescription & config,const ResTable_map_entry * map)529 std::unique_ptr<Array> BinaryResourceParser::parseArray(const ResourceNameRef& name,
530                                                         const ConfigDescription& config,
531                                                         const ResTable_map_entry* map) {
532     std::unique_ptr<Array> array = util::make_unique<Array>();
533     for (const ResTable_map& mapEntry : map) {
534         array->items.push_back(parseValue(name, config, &mapEntry.value, 0));
535     }
536     return array;
537 }
538 
parsePlural(const ResourceNameRef & name,const ConfigDescription & config,const ResTable_map_entry * map)539 std::unique_ptr<Plural> BinaryResourceParser::parsePlural(const ResourceNameRef& name,
540                                                           const ConfigDescription& config,
541                                                           const ResTable_map_entry* map) {
542     std::unique_ptr<Plural> plural = util::make_unique<Plural>();
543     for (const ResTable_map& mapEntry : map) {
544         std::unique_ptr<Item> item = parseValue(name, config, &mapEntry.value, 0);
545         if (!item) {
546             return {};
547         }
548 
549         switch (util::deviceToHost32(mapEntry.name.ident)) {
550             case ResTable_map::ATTR_ZERO:
551                 plural->values[Plural::Zero] = std::move(item);
552                 break;
553             case ResTable_map::ATTR_ONE:
554                 plural->values[Plural::One] = std::move(item);
555                 break;
556             case ResTable_map::ATTR_TWO:
557                 plural->values[Plural::Two] = std::move(item);
558                 break;
559             case ResTable_map::ATTR_FEW:
560                 plural->values[Plural::Few] = std::move(item);
561                 break;
562             case ResTable_map::ATTR_MANY:
563                 plural->values[Plural::Many] = std::move(item);
564                 break;
565             case ResTable_map::ATTR_OTHER:
566                 plural->values[Plural::Other] = std::move(item);
567                 break;
568         }
569     }
570     return plural;
571 }
572 
573 } // namespace aapt
574