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 33namespace aapt { 34 35using namespace android; 36 37namespace { 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 */ 43class ReferenceIdToNameVisitor : public ValueVisitor { 44private: 45 const std::map<ResourceId, ResourceName>* mMapping; 46 47public: 48 using ValueVisitor::visit; 49 50 ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceName>* mapping) : 51 mMapping(mapping) { 52 assert(mMapping); 53 } 54 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 71BinaryResourceParser::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 76bool 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 */ 105bool 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 159bool 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 260bool 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 282bool 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 380std::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 443std::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 464std::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 489std::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 529std::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 539std::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