1 // 2 // Copyright 2006 The Android Open Source Project 3 // 4 // Build resource files from raw assets. 5 // 6 7 #include "ResourceTable.h" 8 9 #include "AaptUtil.h" 10 #include "XMLNode.h" 11 #include "ResourceFilter.h" 12 #include "ResourceIdCache.h" 13 #include "SdkConstants.h" 14 15 #include <algorithm> 16 #include <androidfw/ResourceTypes.h> 17 #include <utils/ByteOrder.h> 18 #include <utils/TypeHelpers.h> 19 #include <stdarg.h> 20 21 // SSIZE: mingw does not have signed size_t == ssize_t. 22 // STATUST: mingw does seem to redefine UNKNOWN_ERROR from our enum value, so a cast is necessary. 23 #if !defined(_WIN32) 24 # define SSIZE(x) x 25 # define STATUST(x) x 26 #else 27 # define SSIZE(x) (signed size_t)x 28 # define STATUST(x) (status_t)x 29 #endif 30 31 // Set to true for noisy debug output. 32 static const bool kIsDebug = false; 33 34 #if PRINT_STRING_METRICS 35 static const bool kPrintStringMetrics = true; 36 #else 37 static const bool kPrintStringMetrics = false; 38 #endif 39 40 static const char* kAttrPrivateType = "^attr-private"; 41 42 status_t compileXmlFile(const Bundle* bundle, 43 const sp<AaptAssets>& assets, 44 const String16& resourceName, 45 const sp<AaptFile>& target, 46 ResourceTable* table, 47 int options) 48 { 49 sp<XMLNode> root = XMLNode::parse(target); 50 if (root == NULL) { 51 return UNKNOWN_ERROR; 52 } 53 54 return compileXmlFile(bundle, assets, resourceName, root, target, table, options); 55 } 56 57 status_t compileXmlFile(const Bundle* bundle, 58 const sp<AaptAssets>& assets, 59 const String16& resourceName, 60 const sp<AaptFile>& target, 61 const sp<AaptFile>& outTarget, 62 ResourceTable* table, 63 int options) 64 { 65 sp<XMLNode> root = XMLNode::parse(target); 66 if (root == NULL) { 67 return UNKNOWN_ERROR; 68 } 69 70 return compileXmlFile(bundle, assets, resourceName, root, outTarget, table, options); 71 } 72 73 status_t compileXmlFile(const Bundle* bundle, 74 const sp<AaptAssets>& assets, 75 const String16& resourceName, 76 const sp<XMLNode>& root, 77 const sp<AaptFile>& target, 78 ResourceTable* table, 79 int options) 80 { 81 if ((options&XML_COMPILE_STRIP_WHITESPACE) != 0) { 82 root->removeWhitespace(true, NULL); 83 } else if ((options&XML_COMPILE_COMPACT_WHITESPACE) != 0) { 84 root->removeWhitespace(false, NULL); 85 } 86 87 if ((options&XML_COMPILE_UTF8) != 0) { 88 root->setUTF8(true); 89 } 90 91 bool hasErrors = false; 92 93 if ((options&XML_COMPILE_ASSIGN_ATTRIBUTE_IDS) != 0) { 94 status_t err = root->assignResourceIds(assets, table); 95 if (err != NO_ERROR) { 96 hasErrors = true; 97 } 98 } 99 100 status_t err = root->parseValues(assets, table); 101 if (err != NO_ERROR) { 102 hasErrors = true; 103 } 104 105 if (hasErrors) { 106 return UNKNOWN_ERROR; 107 } 108 109 if (table->modifyForCompat(bundle, resourceName, target, root) != NO_ERROR) { 110 return UNKNOWN_ERROR; 111 } 112 113 if (kIsDebug) { 114 printf("Input XML Resource:\n"); 115 root->print(); 116 } 117 err = root->flatten(target, 118 (options&XML_COMPILE_STRIP_COMMENTS) != 0, 119 (options&XML_COMPILE_STRIP_RAW_VALUES) != 0); 120 if (err != NO_ERROR) { 121 return err; 122 } 123 124 if (kIsDebug) { 125 printf("Output XML Resource:\n"); 126 ResXMLTree tree; 127 tree.setTo(target->getData(), target->getSize()); 128 printXMLBlock(&tree); 129 } 130 131 target->setCompressionMethod(ZipEntry::kCompressDeflated); 132 133 return err; 134 } 135 136 struct flag_entry 137 { 138 const char16_t* name; 139 size_t nameLen; 140 uint32_t value; 141 const char* description; 142 }; 143 144 static const char16_t referenceArray[] = 145 { 'r', 'e', 'f', 'e', 'r', 'e', 'n', 'c', 'e' }; 146 static const char16_t stringArray[] = 147 { 's', 't', 'r', 'i', 'n', 'g' }; 148 static const char16_t integerArray[] = 149 { 'i', 'n', 't', 'e', 'g', 'e', 'r' }; 150 static const char16_t booleanArray[] = 151 { 'b', 'o', 'o', 'l', 'e', 'a', 'n' }; 152 static const char16_t colorArray[] = 153 { 'c', 'o', 'l', 'o', 'r' }; 154 static const char16_t floatArray[] = 155 { 'f', 'l', 'o', 'a', 't' }; 156 static const char16_t dimensionArray[] = 157 { 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n' }; 158 static const char16_t fractionArray[] = 159 { 'f', 'r', 'a', 'c', 't', 'i', 'o', 'n' }; 160 static const char16_t enumArray[] = 161 { 'e', 'n', 'u', 'm' }; 162 static const char16_t flagsArray[] = 163 { 'f', 'l', 'a', 'g', 's' }; 164 165 static const flag_entry gFormatFlags[] = { 166 { referenceArray, sizeof(referenceArray)/2, ResTable_map::TYPE_REFERENCE, 167 "a reference to another resource, in the form \"<code>@[+][<i>package</i>:]<i>type</i>:<i>name</i></code>\"\n" 168 "or to a theme attribute in the form \"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>\"."}, 169 { stringArray, sizeof(stringArray)/2, ResTable_map::TYPE_STRING, 170 "a string value, using '\\\\;' to escape characters such as '\\\\n' or '\\\\uxxxx' for a unicode character." }, 171 { integerArray, sizeof(integerArray)/2, ResTable_map::TYPE_INTEGER, 172 "an integer value, such as \"<code>100</code>\"." }, 173 { booleanArray, sizeof(booleanArray)/2, ResTable_map::TYPE_BOOLEAN, 174 "a boolean value, either \"<code>true</code>\" or \"<code>false</code>\"." }, 175 { colorArray, sizeof(colorArray)/2, ResTable_map::TYPE_COLOR, 176 "a color value, in the form of \"<code>#<i>rgb</i></code>\", \"<code>#<i>argb</i></code>\",\n" 177 "\"<code>#<i>rrggbb</i></code>\", or \"<code>#<i>aarrggbb</i></code>\"." }, 178 { floatArray, sizeof(floatArray)/2, ResTable_map::TYPE_FLOAT, 179 "a floating point value, such as \"<code>1.2</code>\"."}, 180 { dimensionArray, sizeof(dimensionArray)/2, ResTable_map::TYPE_DIMENSION, 181 "a dimension value, which is a floating point number appended with a unit such as \"<code>14.5sp</code>\".\n" 182 "Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),\n" 183 "in (inches), mm (millimeters)." }, 184 { fractionArray, sizeof(fractionArray)/2, ResTable_map::TYPE_FRACTION, 185 "a fractional value, which is a floating point number appended with either % or %p, such as \"<code>14.5%</code>\".\n" 186 "The % suffix always means a percentage of the base size; the optional %p suffix provides a size relative to\n" 187 "some parent container." }, 188 { enumArray, sizeof(enumArray)/2, ResTable_map::TYPE_ENUM, NULL }, 189 { flagsArray, sizeof(flagsArray)/2, ResTable_map::TYPE_FLAGS, NULL }, 190 { NULL, 0, 0, NULL } 191 }; 192 193 static const char16_t suggestedArray[] = { 's', 'u', 'g', 'g', 'e', 's', 't', 'e', 'd' }; 194 195 static const flag_entry l10nRequiredFlags[] = { 196 { suggestedArray, sizeof(suggestedArray)/2, ResTable_map::L10N_SUGGESTED, NULL }, 197 { NULL, 0, 0, NULL } 198 }; 199 200 static const char16_t nulStr[] = { 0 }; 201 202 static uint32_t parse_flags(const char16_t* str, size_t len, 203 const flag_entry* flags, bool* outError = NULL) 204 { 205 while (len > 0 && isspace(*str)) { 206 str++; 207 len--; 208 } 209 while (len > 0 && isspace(str[len-1])) { 210 len--; 211 } 212 213 const char16_t* const end = str + len; 214 uint32_t value = 0; 215 216 while (str < end) { 217 const char16_t* div = str; 218 while (div < end && *div != '|') { 219 div++; 220 } 221 222 const flag_entry* cur = flags; 223 while (cur->name) { 224 if (strzcmp16(cur->name, cur->nameLen, str, div-str) == 0) { 225 value |= cur->value; 226 break; 227 } 228 cur++; 229 } 230 231 if (!cur->name) { 232 if (outError) *outError = true; 233 return 0; 234 } 235 236 str = div < end ? div+1 : div; 237 } 238 239 if (outError) *outError = false; 240 return value; 241 } 242 243 static String16 mayOrMust(int type, int flags) 244 { 245 if ((type&(~flags)) == 0) { 246 return String16("<p>Must"); 247 } 248 249 return String16("<p>May"); 250 } 251 252 static void appendTypeInfo(ResourceTable* outTable, const String16& pkg, 253 const String16& typeName, const String16& ident, int type, 254 const flag_entry* flags) 255 { 256 bool hadType = false; 257 while (flags->name) { 258 if ((type&flags->value) != 0 && flags->description != NULL) { 259 String16 fullMsg(mayOrMust(type, flags->value)); 260 fullMsg.append(String16(" be ")); 261 fullMsg.append(String16(flags->description)); 262 outTable->appendTypeComment(pkg, typeName, ident, fullMsg); 263 hadType = true; 264 } 265 flags++; 266 } 267 if (hadType && (type&ResTable_map::TYPE_REFERENCE) == 0) { 268 outTable->appendTypeComment(pkg, typeName, ident, 269 String16("<p>This may also be a reference to a resource (in the form\n" 270 "\"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>\") or\n" 271 "theme attribute (in the form\n" 272 "\"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>\")\n" 273 "containing a value of this type.")); 274 } 275 } 276 277 struct PendingAttribute 278 { 279 const String16 myPackage; 280 const SourcePos sourcePos; 281 const bool appendComment; 282 int32_t type; 283 String16 ident; 284 String16 comment; 285 bool hasErrors; 286 bool added; 287 288 PendingAttribute(String16 _package, const sp<AaptFile>& in, 289 ResXMLTree& block, bool _appendComment) 290 : myPackage(_package) 291 , sourcePos(in->getPrintableSource(), block.getLineNumber()) 292 , appendComment(_appendComment) 293 , type(ResTable_map::TYPE_ANY) 294 , hasErrors(false) 295 , added(false) 296 { 297 } 298 299 status_t createIfNeeded(ResourceTable* outTable) 300 { 301 if (added || hasErrors) { 302 return NO_ERROR; 303 } 304 added = true; 305 306 String16 attr16("attr"); 307 308 if (outTable->hasBagOrEntry(myPackage, attr16, ident)) { 309 sourcePos.error("Attribute \"%s\" has already been defined\n", 310 String8(ident).string()); 311 hasErrors = true; 312 return UNKNOWN_ERROR; 313 } 314 315 char numberStr[16]; 316 sprintf(numberStr, "%d", type); 317 status_t err = outTable->addBag(sourcePos, myPackage, 318 attr16, ident, String16(""), 319 String16("^type"), 320 String16(numberStr), NULL, NULL); 321 if (err != NO_ERROR) { 322 hasErrors = true; 323 return err; 324 } 325 outTable->appendComment(myPackage, attr16, ident, comment, appendComment); 326 //printf("Attribute %s comment: %s\n", String8(ident).string(), 327 // String8(comment).string()); 328 return err; 329 } 330 }; 331 332 static status_t compileAttribute(const sp<AaptFile>& in, 333 ResXMLTree& block, 334 const String16& myPackage, 335 ResourceTable* outTable, 336 String16* outIdent = NULL, 337 bool inStyleable = false) 338 { 339 PendingAttribute attr(myPackage, in, block, inStyleable); 340 341 const String16 attr16("attr"); 342 const String16 id16("id"); 343 344 // Attribute type constants. 345 const String16 enum16("enum"); 346 const String16 flag16("flag"); 347 348 ResXMLTree::event_code_t code; 349 size_t len; 350 status_t err; 351 352 ssize_t identIdx = block.indexOfAttribute(NULL, "name"); 353 if (identIdx >= 0) { 354 attr.ident = String16(block.getAttributeStringValue(identIdx, &len)); 355 if (outIdent) { 356 *outIdent = attr.ident; 357 } 358 } else { 359 attr.sourcePos.error("A 'name' attribute is required for <attr>\n"); 360 attr.hasErrors = true; 361 } 362 363 attr.comment = String16( 364 block.getComment(&len) ? block.getComment(&len) : nulStr); 365 366 ssize_t typeIdx = block.indexOfAttribute(NULL, "format"); 367 if (typeIdx >= 0) { 368 String16 typeStr = String16(block.getAttributeStringValue(typeIdx, &len)); 369 attr.type = parse_flags(typeStr.string(), typeStr.size(), gFormatFlags); 370 if (attr.type == 0) { 371 attr.sourcePos.error("Tag <attr> 'format' attribute value \"%s\" not valid\n", 372 String8(typeStr).string()); 373 attr.hasErrors = true; 374 } 375 attr.createIfNeeded(outTable); 376 } else if (!inStyleable) { 377 // Attribute definitions outside of styleables always define the 378 // attribute as a generic value. 379 attr.createIfNeeded(outTable); 380 } 381 382 //printf("Attribute %s: type=0x%08x\n", String8(attr.ident).string(), attr.type); 383 384 ssize_t minIdx = block.indexOfAttribute(NULL, "min"); 385 if (minIdx >= 0) { 386 String16 val = String16(block.getAttributeStringValue(minIdx, &len)); 387 if (!ResTable::stringToInt(val.string(), val.size(), NULL)) { 388 attr.sourcePos.error("Tag <attr> 'min' attribute must be a number, not \"%s\"\n", 389 String8(val).string()); 390 attr.hasErrors = true; 391 } 392 attr.createIfNeeded(outTable); 393 if (!attr.hasErrors) { 394 err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident, 395 String16(""), String16("^min"), String16(val), NULL, NULL); 396 if (err != NO_ERROR) { 397 attr.hasErrors = true; 398 } 399 } 400 } 401 402 ssize_t maxIdx = block.indexOfAttribute(NULL, "max"); 403 if (maxIdx >= 0) { 404 String16 val = String16(block.getAttributeStringValue(maxIdx, &len)); 405 if (!ResTable::stringToInt(val.string(), val.size(), NULL)) { 406 attr.sourcePos.error("Tag <attr> 'max' attribute must be a number, not \"%s\"\n", 407 String8(val).string()); 408 attr.hasErrors = true; 409 } 410 attr.createIfNeeded(outTable); 411 if (!attr.hasErrors) { 412 err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident, 413 String16(""), String16("^max"), String16(val), NULL, NULL); 414 attr.hasErrors = true; 415 } 416 } 417 418 if ((minIdx >= 0 || maxIdx >= 0) && (attr.type&ResTable_map::TYPE_INTEGER) == 0) { 419 attr.sourcePos.error("Tag <attr> must have format=integer attribute if using max or min\n"); 420 attr.hasErrors = true; 421 } 422 423 ssize_t l10nIdx = block.indexOfAttribute(NULL, "localization"); 424 if (l10nIdx >= 0) { 425 const char16_t* str = block.getAttributeStringValue(l10nIdx, &len); 426 bool error; 427 uint32_t l10n_required = parse_flags(str, len, l10nRequiredFlags, &error); 428 if (error) { 429 attr.sourcePos.error("Tag <attr> 'localization' attribute value \"%s\" not valid\n", 430 String8(str).string()); 431 attr.hasErrors = true; 432 } 433 attr.createIfNeeded(outTable); 434 if (!attr.hasErrors) { 435 char buf[11]; 436 sprintf(buf, "%d", l10n_required); 437 err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident, 438 String16(""), String16("^l10n"), String16(buf), NULL, NULL); 439 if (err != NO_ERROR) { 440 attr.hasErrors = true; 441 } 442 } 443 } 444 445 String16 enumOrFlagsComment; 446 447 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 448 if (code == ResXMLTree::START_TAG) { 449 uint32_t localType = 0; 450 if (strcmp16(block.getElementName(&len), enum16.string()) == 0) { 451 localType = ResTable_map::TYPE_ENUM; 452 } else if (strcmp16(block.getElementName(&len), flag16.string()) == 0) { 453 localType = ResTable_map::TYPE_FLAGS; 454 } else { 455 SourcePos(in->getPrintableSource(), block.getLineNumber()) 456 .error("Tag <%s> can not appear inside <attr>, only <enum> or <flag>\n", 457 String8(block.getElementName(&len)).string()); 458 return UNKNOWN_ERROR; 459 } 460 461 attr.createIfNeeded(outTable); 462 463 if (attr.type == ResTable_map::TYPE_ANY) { 464 // No type was explicitly stated, so supplying enum tags 465 // implicitly creates an enum or flag. 466 attr.type = 0; 467 } 468 469 if ((attr.type&(ResTable_map::TYPE_ENUM|ResTable_map::TYPE_FLAGS)) == 0) { 470 // Wasn't originally specified as an enum, so update its type. 471 attr.type |= localType; 472 if (!attr.hasErrors) { 473 char numberStr[16]; 474 sprintf(numberStr, "%d", attr.type); 475 err = outTable->addBag(SourcePos(in->getPrintableSource(), block.getLineNumber()), 476 myPackage, attr16, attr.ident, String16(""), 477 String16("^type"), String16(numberStr), NULL, NULL, true); 478 if (err != NO_ERROR) { 479 attr.hasErrors = true; 480 } 481 } 482 } else if ((uint32_t)(attr.type&(ResTable_map::TYPE_ENUM|ResTable_map::TYPE_FLAGS)) != localType) { 483 if (localType == ResTable_map::TYPE_ENUM) { 484 SourcePos(in->getPrintableSource(), block.getLineNumber()) 485 .error("<enum> attribute can not be used inside a flags format\n"); 486 attr.hasErrors = true; 487 } else { 488 SourcePos(in->getPrintableSource(), block.getLineNumber()) 489 .error("<flag> attribute can not be used inside a enum format\n"); 490 attr.hasErrors = true; 491 } 492 } 493 494 String16 itemIdent; 495 ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "name"); 496 if (itemIdentIdx >= 0) { 497 itemIdent = String16(block.getAttributeStringValue(itemIdentIdx, &len)); 498 } else { 499 SourcePos(in->getPrintableSource(), block.getLineNumber()) 500 .error("A 'name' attribute is required for <enum> or <flag>\n"); 501 attr.hasErrors = true; 502 } 503 504 String16 value; 505 ssize_t valueIdx = block.indexOfAttribute(NULL, "value"); 506 if (valueIdx >= 0) { 507 value = String16(block.getAttributeStringValue(valueIdx, &len)); 508 } else { 509 SourcePos(in->getPrintableSource(), block.getLineNumber()) 510 .error("A 'value' attribute is required for <enum> or <flag>\n"); 511 attr.hasErrors = true; 512 } 513 if (!attr.hasErrors && !ResTable::stringToInt(value.string(), value.size(), NULL)) { 514 SourcePos(in->getPrintableSource(), block.getLineNumber()) 515 .error("Tag <enum> or <flag> 'value' attribute must be a number," 516 " not \"%s\"\n", 517 String8(value).string()); 518 attr.hasErrors = true; 519 } 520 521 if (!attr.hasErrors) { 522 if (enumOrFlagsComment.size() == 0) { 523 enumOrFlagsComment.append(mayOrMust(attr.type, 524 ResTable_map::TYPE_ENUM|ResTable_map::TYPE_FLAGS)); 525 enumOrFlagsComment.append((attr.type&ResTable_map::TYPE_ENUM) 526 ? String16(" be one of the following constant values.") 527 : String16(" be one or more (separated by '|') of the following constant values.")); 528 enumOrFlagsComment.append(String16("</p>\n<table>\n" 529 "<colgroup align=\"left\" />\n" 530 "<colgroup align=\"left\" />\n" 531 "<colgroup align=\"left\" />\n" 532 "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>")); 533 } 534 535 enumOrFlagsComment.append(String16("\n<tr><td><code>")); 536 enumOrFlagsComment.append(itemIdent); 537 enumOrFlagsComment.append(String16("</code></td><td>")); 538 enumOrFlagsComment.append(value); 539 enumOrFlagsComment.append(String16("</td><td>")); 540 if (block.getComment(&len)) { 541 enumOrFlagsComment.append(String16(block.getComment(&len))); 542 } 543 enumOrFlagsComment.append(String16("</td></tr>")); 544 545 err = outTable->addBag(SourcePos(in->getPrintableSource(), block.getLineNumber()), 546 myPackage, 547 attr16, attr.ident, String16(""), 548 itemIdent, value, NULL, NULL, false, true); 549 if (err != NO_ERROR) { 550 attr.hasErrors = true; 551 } 552 } 553 } else if (code == ResXMLTree::END_TAG) { 554 if (strcmp16(block.getElementName(&len), attr16.string()) == 0) { 555 break; 556 } 557 if ((attr.type&ResTable_map::TYPE_ENUM) != 0) { 558 if (strcmp16(block.getElementName(&len), enum16.string()) != 0) { 559 SourcePos(in->getPrintableSource(), block.getLineNumber()) 560 .error("Found tag </%s> where </enum> is expected\n", 561 String8(block.getElementName(&len)).string()); 562 return UNKNOWN_ERROR; 563 } 564 } else { 565 if (strcmp16(block.getElementName(&len), flag16.string()) != 0) { 566 SourcePos(in->getPrintableSource(), block.getLineNumber()) 567 .error("Found tag </%s> where </flag> is expected\n", 568 String8(block.getElementName(&len)).string()); 569 return UNKNOWN_ERROR; 570 } 571 } 572 } 573 } 574 575 if (!attr.hasErrors && attr.added) { 576 appendTypeInfo(outTable, myPackage, attr16, attr.ident, attr.type, gFormatFlags); 577 } 578 579 if (!attr.hasErrors && enumOrFlagsComment.size() > 0) { 580 enumOrFlagsComment.append(String16("\n</table>")); 581 outTable->appendTypeComment(myPackage, attr16, attr.ident, enumOrFlagsComment); 582 } 583 584 585 return NO_ERROR; 586 } 587 588 bool localeIsDefined(const ResTable_config& config) 589 { 590 return config.locale == 0; 591 } 592 593 status_t parseAndAddBag(Bundle* bundle, 594 const sp<AaptFile>& in, 595 ResXMLTree* block, 596 const ResTable_config& config, 597 const String16& myPackage, 598 const String16& curType, 599 const String16& ident, 600 const String16& parentIdent, 601 const String16& itemIdent, 602 int32_t curFormat, 603 bool isFormatted, 604 const String16& /* product */, 605 PseudolocalizationMethod pseudolocalize, 606 const bool overwrite, 607 ResourceTable* outTable) 608 { 609 status_t err; 610 const String16 item16("item"); 611 612 String16 str; 613 Vector<StringPool::entry_style_span> spans; 614 err = parseStyledString(bundle, in->getPrintableSource().string(), 615 block, item16, &str, &spans, isFormatted, 616 pseudolocalize); 617 if (err != NO_ERROR) { 618 return err; 619 } 620 621 if (kIsDebug) { 622 printf("Adding resource bag entry l=%c%c c=%c%c orien=%d d=%d " 623 " pid=%s, bag=%s, id=%s: %s\n", 624 config.language[0], config.language[1], 625 config.country[0], config.country[1], 626 config.orientation, config.density, 627 String8(parentIdent).string(), 628 String8(ident).string(), 629 String8(itemIdent).string(), 630 String8(str).string()); 631 } 632 633 err = outTable->addBag(SourcePos(in->getPrintableSource(), block->getLineNumber()), 634 myPackage, curType, ident, parentIdent, itemIdent, str, 635 &spans, &config, overwrite, false, curFormat); 636 return err; 637 } 638 639 /* 640 * Returns true if needle is one of the elements in the comma-separated list 641 * haystack, false otherwise. 642 */ 643 bool isInProductList(const String16& needle, const String16& haystack) { 644 const char16_t *needle2 = needle.string(); 645 const char16_t *haystack2 = haystack.string(); 646 size_t needlesize = needle.size(); 647 648 while (*haystack2 != '\0') { 649 if (strncmp16(haystack2, needle2, needlesize) == 0) { 650 if (haystack2[needlesize] == '\0' || haystack2[needlesize] == ',') { 651 return true; 652 } 653 } 654 655 while (*haystack2 != '\0' && *haystack2 != ',') { 656 haystack2++; 657 } 658 if (*haystack2 == ',') { 659 haystack2++; 660 } 661 } 662 663 return false; 664 } 665 666 /* 667 * A simple container that holds a resource type and name. It is ordered first by type then 668 * by name. 669 */ 670 struct type_ident_pair_t { 671 String16 type; 672 String16 ident; 673 674 type_ident_pair_t() { }; 675 type_ident_pair_t(const String16& t, const String16& i) : type(t), ident(i) { } 676 type_ident_pair_t(const type_ident_pair_t& o) : type(o.type), ident(o.ident) { } 677 inline bool operator < (const type_ident_pair_t& o) const { 678 int cmp = compare_type(type, o.type); 679 if (cmp < 0) { 680 return true; 681 } else if (cmp > 0) { 682 return false; 683 } else { 684 return strictly_order_type(ident, o.ident); 685 } 686 } 687 }; 688 689 690 status_t parseAndAddEntry(Bundle* bundle, 691 const sp<AaptFile>& in, 692 ResXMLTree* block, 693 const ResTable_config& config, 694 const String16& myPackage, 695 const String16& curType, 696 const String16& ident, 697 const String16& curTag, 698 bool curIsStyled, 699 int32_t curFormat, 700 bool isFormatted, 701 const String16& product, 702 PseudolocalizationMethod pseudolocalize, 703 const bool overwrite, 704 KeyedVector<type_ident_pair_t, bool>* skippedResourceNames, 705 ResourceTable* outTable) 706 { 707 status_t err; 708 709 String16 str; 710 Vector<StringPool::entry_style_span> spans; 711 err = parseStyledString(bundle, in->getPrintableSource().string(), block, 712 curTag, &str, curIsStyled ? &spans : NULL, 713 isFormatted, pseudolocalize); 714 715 if (err < NO_ERROR) { 716 return err; 717 } 718 719 /* 720 * If a product type was specified on the command line 721 * and also in the string, and the two are not the same, 722 * return without adding the string. 723 */ 724 725 const char *bundleProduct = bundle->getProduct(); 726 if (bundleProduct == NULL) { 727 bundleProduct = ""; 728 } 729 730 if (product.size() != 0) { 731 /* 732 * If the command-line-specified product is empty, only "default" 733 * matches. Other variants are skipped. This is so generation 734 * of the R.java file when the product is not known is predictable. 735 */ 736 737 if (bundleProduct[0] == '\0') { 738 if (strcmp16(String16("default").string(), product.string()) != 0) { 739 /* 740 * This string has a product other than 'default'. Do not add it, 741 * but record it so that if we do not see the same string with 742 * product 'default' or no product, then report an error. 743 */ 744 skippedResourceNames->replaceValueFor( 745 type_ident_pair_t(curType, ident), true); 746 return NO_ERROR; 747 } 748 } else { 749 /* 750 * The command-line product is not empty. 751 * If the product for this string is on the command-line list, 752 * it matches. "default" also matches, but only if nothing 753 * else has matched already. 754 */ 755 756 if (isInProductList(product, String16(bundleProduct))) { 757 ; 758 } else if (strcmp16(String16("default").string(), product.string()) == 0 && 759 !outTable->hasBagOrEntry(myPackage, curType, ident, config)) { 760 ; 761 } else { 762 return NO_ERROR; 763 } 764 } 765 } 766 767 if (kIsDebug) { 768 printf("Adding resource entry l=%c%c c=%c%c orien=%d d=%d id=%s: %s\n", 769 config.language[0], config.language[1], 770 config.country[0], config.country[1], 771 config.orientation, config.density, 772 String8(ident).string(), String8(str).string()); 773 } 774 775 err = outTable->addEntry(SourcePos(in->getPrintableSource(), block->getLineNumber()), 776 myPackage, curType, ident, str, &spans, &config, 777 false, curFormat, overwrite); 778 779 return err; 780 } 781 782 status_t compileResourceFile(Bundle* bundle, 783 const sp<AaptAssets>& assets, 784 const sp<AaptFile>& in, 785 const ResTable_config& defParams, 786 const bool overwrite, 787 ResourceTable* outTable) 788 { 789 ResXMLTree block; 790 status_t err = parseXMLResource(in, &block, false, true); 791 if (err != NO_ERROR) { 792 return err; 793 } 794 795 // Top-level tag. 796 const String16 resources16("resources"); 797 798 // Identifier declaration tags. 799 const String16 declare_styleable16("declare-styleable"); 800 const String16 attr16("attr"); 801 802 // Data creation organizational tags. 803 const String16 string16("string"); 804 const String16 drawable16("drawable"); 805 const String16 color16("color"); 806 const String16 bool16("bool"); 807 const String16 integer16("integer"); 808 const String16 dimen16("dimen"); 809 const String16 fraction16("fraction"); 810 const String16 style16("style"); 811 const String16 plurals16("plurals"); 812 const String16 array16("array"); 813 const String16 string_array16("string-array"); 814 const String16 integer_array16("integer-array"); 815 const String16 public16("public"); 816 const String16 public_padding16("public-padding"); 817 const String16 private_symbols16("private-symbols"); 818 const String16 java_symbol16("java-symbol"); 819 const String16 add_resource16("add-resource"); 820 const String16 skip16("skip"); 821 const String16 eat_comment16("eat-comment"); 822 823 // Data creation tags. 824 const String16 bag16("bag"); 825 const String16 item16("item"); 826 827 // Attribute type constants. 828 const String16 enum16("enum"); 829 830 // plural values 831 const String16 other16("other"); 832 const String16 quantityOther16("^other"); 833 const String16 zero16("zero"); 834 const String16 quantityZero16("^zero"); 835 const String16 one16("one"); 836 const String16 quantityOne16("^one"); 837 const String16 two16("two"); 838 const String16 quantityTwo16("^two"); 839 const String16 few16("few"); 840 const String16 quantityFew16("^few"); 841 const String16 many16("many"); 842 const String16 quantityMany16("^many"); 843 844 // useful attribute names and special values 845 const String16 name16("name"); 846 const String16 translatable16("translatable"); 847 const String16 formatted16("formatted"); 848 const String16 false16("false"); 849 850 const String16 myPackage(assets->getPackage()); 851 852 bool hasErrors = false; 853 854 bool fileIsTranslatable = true; 855 if (strstr(in->getPrintableSource().string(), "donottranslate") != NULL) { 856 fileIsTranslatable = false; 857 } 858 859 DefaultKeyedVector<String16, uint32_t> nextPublicId(0); 860 861 // Stores the resource names that were skipped. Typically this happens when 862 // AAPT is invoked without a product specified and a resource has no 863 // 'default' product attribute. 864 KeyedVector<type_ident_pair_t, bool> skippedResourceNames; 865 866 ResXMLTree::event_code_t code; 867 do { 868 code = block.next(); 869 } while (code == ResXMLTree::START_NAMESPACE); 870 871 size_t len; 872 if (code != ResXMLTree::START_TAG) { 873 SourcePos(in->getPrintableSource(), block.getLineNumber()).error( 874 "No start tag found\n"); 875 return UNKNOWN_ERROR; 876 } 877 if (strcmp16(block.getElementName(&len), resources16.string()) != 0) { 878 SourcePos(in->getPrintableSource(), block.getLineNumber()).error( 879 "Invalid start tag %s\n", String8(block.getElementName(&len)).string()); 880 return UNKNOWN_ERROR; 881 } 882 883 ResTable_config curParams(defParams); 884 885 ResTable_config pseudoParams(curParams); 886 pseudoParams.language[0] = 'e'; 887 pseudoParams.language[1] = 'n'; 888 pseudoParams.country[0] = 'X'; 889 pseudoParams.country[1] = 'A'; 890 891 ResTable_config pseudoBidiParams(curParams); 892 pseudoBidiParams.language[0] = 'a'; 893 pseudoBidiParams.language[1] = 'r'; 894 pseudoBidiParams.country[0] = 'X'; 895 pseudoBidiParams.country[1] = 'B'; 896 897 // We should skip resources for pseudolocales if they were 898 // already added automatically. This is a fix for a transition period when 899 // manually pseudolocalized resources may be expected. 900 // TODO: remove this check after next SDK version release. 901 if ((bundle->getPseudolocalize() & PSEUDO_ACCENTED && 902 curParams.locale == pseudoParams.locale) || 903 (bundle->getPseudolocalize() & PSEUDO_BIDI && 904 curParams.locale == pseudoBidiParams.locale)) { 905 SourcePos(in->getPrintableSource(), 0).warning( 906 "Resource file %s is skipped as pseudolocalization" 907 " was done automatically.", 908 in->getPrintableSource().string()); 909 return NO_ERROR; 910 } 911 912 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 913 if (code == ResXMLTree::START_TAG) { 914 const String16* curTag = NULL; 915 String16 curType; 916 String16 curName; 917 int32_t curFormat = ResTable_map::TYPE_ANY; 918 bool curIsBag = false; 919 bool curIsBagReplaceOnOverwrite = false; 920 bool curIsStyled = false; 921 bool curIsPseudolocalizable = false; 922 bool curIsFormatted = fileIsTranslatable; 923 bool localHasErrors = false; 924 925 if (strcmp16(block.getElementName(&len), skip16.string()) == 0) { 926 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 927 && code != ResXMLTree::BAD_DOCUMENT) { 928 if (code == ResXMLTree::END_TAG) { 929 if (strcmp16(block.getElementName(&len), skip16.string()) == 0) { 930 break; 931 } 932 } 933 } 934 continue; 935 936 } else if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) { 937 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 938 && code != ResXMLTree::BAD_DOCUMENT) { 939 if (code == ResXMLTree::END_TAG) { 940 if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) { 941 break; 942 } 943 } 944 } 945 continue; 946 947 } else if (strcmp16(block.getElementName(&len), public16.string()) == 0) { 948 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber()); 949 950 String16 type; 951 ssize_t typeIdx = block.indexOfAttribute(NULL, "type"); 952 if (typeIdx < 0) { 953 srcPos.error("A 'type' attribute is required for <public>\n"); 954 hasErrors = localHasErrors = true; 955 } 956 type = String16(block.getAttributeStringValue(typeIdx, &len)); 957 958 String16 name; 959 ssize_t nameIdx = block.indexOfAttribute(NULL, "name"); 960 if (nameIdx < 0) { 961 srcPos.error("A 'name' attribute is required for <public>\n"); 962 hasErrors = localHasErrors = true; 963 } 964 name = String16(block.getAttributeStringValue(nameIdx, &len)); 965 966 uint32_t ident = 0; 967 ssize_t identIdx = block.indexOfAttribute(NULL, "id"); 968 if (identIdx >= 0) { 969 const char16_t* identStr = block.getAttributeStringValue(identIdx, &len); 970 Res_value identValue; 971 if (!ResTable::stringToInt(identStr, len, &identValue)) { 972 srcPos.error("Given 'id' attribute is not an integer: %s\n", 973 String8(block.getAttributeStringValue(identIdx, &len)).string()); 974 hasErrors = localHasErrors = true; 975 } else { 976 ident = identValue.data; 977 nextPublicId.replaceValueFor(type, ident+1); 978 } 979 } else if (nextPublicId.indexOfKey(type) < 0) { 980 srcPos.error("No 'id' attribute supplied <public>," 981 " and no previous id defined in this file.\n"); 982 hasErrors = localHasErrors = true; 983 } else if (!localHasErrors) { 984 ident = nextPublicId.valueFor(type); 985 nextPublicId.replaceValueFor(type, ident+1); 986 } 987 988 if (!localHasErrors) { 989 err = outTable->addPublic(srcPos, myPackage, type, name, ident); 990 if (err < NO_ERROR) { 991 hasErrors = localHasErrors = true; 992 } 993 } 994 if (!localHasErrors) { 995 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R")); 996 if (symbols != NULL) { 997 symbols = symbols->addNestedSymbol(String8(type), srcPos); 998 } 999 if (symbols != NULL) { 1000 symbols->makeSymbolPublic(String8(name), srcPos); 1001 String16 comment( 1002 block.getComment(&len) ? block.getComment(&len) : nulStr); 1003 symbols->appendComment(String8(name), comment, srcPos); 1004 } else { 1005 srcPos.error("Unable to create symbols!\n"); 1006 hasErrors = localHasErrors = true; 1007 } 1008 } 1009 1010 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 1011 if (code == ResXMLTree::END_TAG) { 1012 if (strcmp16(block.getElementName(&len), public16.string()) == 0) { 1013 break; 1014 } 1015 } 1016 } 1017 continue; 1018 1019 } else if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) { 1020 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber()); 1021 1022 String16 type; 1023 ssize_t typeIdx = block.indexOfAttribute(NULL, "type"); 1024 if (typeIdx < 0) { 1025 srcPos.error("A 'type' attribute is required for <public-padding>\n"); 1026 hasErrors = localHasErrors = true; 1027 } 1028 type = String16(block.getAttributeStringValue(typeIdx, &len)); 1029 1030 String16 name; 1031 ssize_t nameIdx = block.indexOfAttribute(NULL, "name"); 1032 if (nameIdx < 0) { 1033 srcPos.error("A 'name' attribute is required for <public-padding>\n"); 1034 hasErrors = localHasErrors = true; 1035 } 1036 name = String16(block.getAttributeStringValue(nameIdx, &len)); 1037 1038 uint32_t start = 0; 1039 ssize_t startIdx = block.indexOfAttribute(NULL, "start"); 1040 if (startIdx >= 0) { 1041 const char16_t* startStr = block.getAttributeStringValue(startIdx, &len); 1042 Res_value startValue; 1043 if (!ResTable::stringToInt(startStr, len, &startValue)) { 1044 srcPos.error("Given 'start' attribute is not an integer: %s\n", 1045 String8(block.getAttributeStringValue(startIdx, &len)).string()); 1046 hasErrors = localHasErrors = true; 1047 } else { 1048 start = startValue.data; 1049 } 1050 } else if (nextPublicId.indexOfKey(type) < 0) { 1051 srcPos.error("No 'start' attribute supplied <public-padding>," 1052 " and no previous id defined in this file.\n"); 1053 hasErrors = localHasErrors = true; 1054 } else if (!localHasErrors) { 1055 start = nextPublicId.valueFor(type); 1056 } 1057 1058 uint32_t end = 0; 1059 ssize_t endIdx = block.indexOfAttribute(NULL, "end"); 1060 if (endIdx >= 0) { 1061 const char16_t* endStr = block.getAttributeStringValue(endIdx, &len); 1062 Res_value endValue; 1063 if (!ResTable::stringToInt(endStr, len, &endValue)) { 1064 srcPos.error("Given 'end' attribute is not an integer: %s\n", 1065 String8(block.getAttributeStringValue(endIdx, &len)).string()); 1066 hasErrors = localHasErrors = true; 1067 } else { 1068 end = endValue.data; 1069 } 1070 } else { 1071 srcPos.error("No 'end' attribute supplied <public-padding>\n"); 1072 hasErrors = localHasErrors = true; 1073 } 1074 1075 if (end >= start) { 1076 nextPublicId.replaceValueFor(type, end+1); 1077 } else { 1078 srcPos.error("Padding start '%ul' is after end '%ul'\n", 1079 start, end); 1080 hasErrors = localHasErrors = true; 1081 } 1082 1083 String16 comment( 1084 block.getComment(&len) ? block.getComment(&len) : nulStr); 1085 for (uint32_t curIdent=start; curIdent<=end; curIdent++) { 1086 if (localHasErrors) { 1087 break; 1088 } 1089 String16 curName(name); 1090 char buf[64]; 1091 sprintf(buf, "%d", (int)(end-curIdent+1)); 1092 curName.append(String16(buf)); 1093 1094 err = outTable->addEntry(srcPos, myPackage, type, curName, 1095 String16("padding"), NULL, &curParams, false, 1096 ResTable_map::TYPE_STRING, overwrite); 1097 if (err < NO_ERROR) { 1098 hasErrors = localHasErrors = true; 1099 break; 1100 } 1101 err = outTable->addPublic(srcPos, myPackage, type, 1102 curName, curIdent); 1103 if (err < NO_ERROR) { 1104 hasErrors = localHasErrors = true; 1105 break; 1106 } 1107 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R")); 1108 if (symbols != NULL) { 1109 symbols = symbols->addNestedSymbol(String8(type), srcPos); 1110 } 1111 if (symbols != NULL) { 1112 symbols->makeSymbolPublic(String8(curName), srcPos); 1113 symbols->appendComment(String8(curName), comment, srcPos); 1114 } else { 1115 srcPos.error("Unable to create symbols!\n"); 1116 hasErrors = localHasErrors = true; 1117 } 1118 } 1119 1120 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 1121 if (code == ResXMLTree::END_TAG) { 1122 if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) { 1123 break; 1124 } 1125 } 1126 } 1127 continue; 1128 1129 } else if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) { 1130 String16 pkg; 1131 ssize_t pkgIdx = block.indexOfAttribute(NULL, "package"); 1132 if (pkgIdx < 0) { 1133 SourcePos(in->getPrintableSource(), block.getLineNumber()).error( 1134 "A 'package' attribute is required for <private-symbols>\n"); 1135 hasErrors = localHasErrors = true; 1136 } 1137 pkg = String16(block.getAttributeStringValue(pkgIdx, &len)); 1138 if (!localHasErrors) { 1139 assets->setSymbolsPrivatePackage(String8(pkg)); 1140 } 1141 1142 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 1143 if (code == ResXMLTree::END_TAG) { 1144 if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) { 1145 break; 1146 } 1147 } 1148 } 1149 continue; 1150 1151 } else if (strcmp16(block.getElementName(&len), java_symbol16.string()) == 0) { 1152 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber()); 1153 1154 String16 type; 1155 ssize_t typeIdx = block.indexOfAttribute(NULL, "type"); 1156 if (typeIdx < 0) { 1157 srcPos.error("A 'type' attribute is required for <public>\n"); 1158 hasErrors = localHasErrors = true; 1159 } 1160 type = String16(block.getAttributeStringValue(typeIdx, &len)); 1161 1162 String16 name; 1163 ssize_t nameIdx = block.indexOfAttribute(NULL, "name"); 1164 if (nameIdx < 0) { 1165 srcPos.error("A 'name' attribute is required for <public>\n"); 1166 hasErrors = localHasErrors = true; 1167 } 1168 name = String16(block.getAttributeStringValue(nameIdx, &len)); 1169 1170 sp<AaptSymbols> symbols = assets->getJavaSymbolsFor(String8("R")); 1171 if (symbols != NULL) { 1172 symbols = symbols->addNestedSymbol(String8(type), srcPos); 1173 } 1174 if (symbols != NULL) { 1175 symbols->makeSymbolJavaSymbol(String8(name), srcPos); 1176 String16 comment( 1177 block.getComment(&len) ? block.getComment(&len) : nulStr); 1178 symbols->appendComment(String8(name), comment, srcPos); 1179 } else { 1180 srcPos.error("Unable to create symbols!\n"); 1181 hasErrors = localHasErrors = true; 1182 } 1183 1184 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 1185 if (code == ResXMLTree::END_TAG) { 1186 if (strcmp16(block.getElementName(&len), java_symbol16.string()) == 0) { 1187 break; 1188 } 1189 } 1190 } 1191 continue; 1192 1193 1194 } else if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) { 1195 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber()); 1196 1197 String16 typeName; 1198 ssize_t typeIdx = block.indexOfAttribute(NULL, "type"); 1199 if (typeIdx < 0) { 1200 srcPos.error("A 'type' attribute is required for <add-resource>\n"); 1201 hasErrors = localHasErrors = true; 1202 } 1203 typeName = String16(block.getAttributeStringValue(typeIdx, &len)); 1204 1205 String16 name; 1206 ssize_t nameIdx = block.indexOfAttribute(NULL, "name"); 1207 if (nameIdx < 0) { 1208 srcPos.error("A 'name' attribute is required for <add-resource>\n"); 1209 hasErrors = localHasErrors = true; 1210 } 1211 name = String16(block.getAttributeStringValue(nameIdx, &len)); 1212 1213 outTable->canAddEntry(srcPos, myPackage, typeName, name); 1214 1215 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 1216 if (code == ResXMLTree::END_TAG) { 1217 if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) { 1218 break; 1219 } 1220 } 1221 } 1222 continue; 1223 1224 } else if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) { 1225 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber()); 1226 1227 String16 ident; 1228 ssize_t identIdx = block.indexOfAttribute(NULL, "name"); 1229 if (identIdx < 0) { 1230 srcPos.error("A 'name' attribute is required for <declare-styleable>\n"); 1231 hasErrors = localHasErrors = true; 1232 } 1233 ident = String16(block.getAttributeStringValue(identIdx, &len)); 1234 1235 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R")); 1236 if (!localHasErrors) { 1237 if (symbols != NULL) { 1238 symbols = symbols->addNestedSymbol(String8("styleable"), srcPos); 1239 } 1240 sp<AaptSymbols> styleSymbols = symbols; 1241 if (symbols != NULL) { 1242 symbols = symbols->addNestedSymbol(String8(ident), srcPos); 1243 } 1244 if (symbols == NULL) { 1245 srcPos.error("Unable to create symbols!\n"); 1246 return UNKNOWN_ERROR; 1247 } 1248 1249 String16 comment( 1250 block.getComment(&len) ? block.getComment(&len) : nulStr); 1251 styleSymbols->appendComment(String8(ident), comment, srcPos); 1252 } else { 1253 symbols = NULL; 1254 } 1255 1256 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 1257 if (code == ResXMLTree::START_TAG) { 1258 if (strcmp16(block.getElementName(&len), skip16.string()) == 0) { 1259 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 1260 && code != ResXMLTree::BAD_DOCUMENT) { 1261 if (code == ResXMLTree::END_TAG) { 1262 if (strcmp16(block.getElementName(&len), skip16.string()) == 0) { 1263 break; 1264 } 1265 } 1266 } 1267 continue; 1268 } else if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) { 1269 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 1270 && code != ResXMLTree::BAD_DOCUMENT) { 1271 if (code == ResXMLTree::END_TAG) { 1272 if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) { 1273 break; 1274 } 1275 } 1276 } 1277 continue; 1278 } else if (strcmp16(block.getElementName(&len), attr16.string()) != 0) { 1279 SourcePos(in->getPrintableSource(), block.getLineNumber()).error( 1280 "Tag <%s> can not appear inside <declare-styleable>, only <attr>\n", 1281 String8(block.getElementName(&len)).string()); 1282 return UNKNOWN_ERROR; 1283 } 1284 1285 String16 comment( 1286 block.getComment(&len) ? block.getComment(&len) : nulStr); 1287 String16 itemIdent; 1288 err = compileAttribute(in, block, myPackage, outTable, &itemIdent, true); 1289 if (err != NO_ERROR) { 1290 hasErrors = localHasErrors = true; 1291 } 1292 1293 if (symbols != NULL) { 1294 SourcePos srcPos(String8(in->getPrintableSource()), block.getLineNumber()); 1295 symbols->addSymbol(String8(itemIdent), 0, srcPos); 1296 symbols->appendComment(String8(itemIdent), comment, srcPos); 1297 //printf("Attribute %s comment: %s\n", String8(itemIdent).string(), 1298 // String8(comment).string()); 1299 } 1300 } else if (code == ResXMLTree::END_TAG) { 1301 if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) { 1302 break; 1303 } 1304 1305 SourcePos(in->getPrintableSource(), block.getLineNumber()).error( 1306 "Found tag </%s> where </attr> is expected\n", 1307 String8(block.getElementName(&len)).string()); 1308 return UNKNOWN_ERROR; 1309 } 1310 } 1311 continue; 1312 1313 } else if (strcmp16(block.getElementName(&len), attr16.string()) == 0) { 1314 err = compileAttribute(in, block, myPackage, outTable, NULL); 1315 if (err != NO_ERROR) { 1316 hasErrors = true; 1317 } 1318 continue; 1319 1320 } else if (strcmp16(block.getElementName(&len), item16.string()) == 0) { 1321 curTag = &item16; 1322 ssize_t attri = block.indexOfAttribute(NULL, "type"); 1323 if (attri >= 0) { 1324 curType = String16(block.getAttributeStringValue(attri, &len)); 1325 ssize_t nameIdx = block.indexOfAttribute(NULL, "name"); 1326 if (nameIdx >= 0) { 1327 curName = String16(block.getAttributeStringValue(nameIdx, &len)); 1328 } 1329 ssize_t formatIdx = block.indexOfAttribute(NULL, "format"); 1330 if (formatIdx >= 0) { 1331 String16 formatStr = String16(block.getAttributeStringValue( 1332 formatIdx, &len)); 1333 curFormat = parse_flags(formatStr.string(), formatStr.size(), 1334 gFormatFlags); 1335 if (curFormat == 0) { 1336 SourcePos(in->getPrintableSource(), block.getLineNumber()).error( 1337 "Tag <item> 'format' attribute value \"%s\" not valid\n", 1338 String8(formatStr).string()); 1339 hasErrors = localHasErrors = true; 1340 } 1341 } 1342 } else { 1343 SourcePos(in->getPrintableSource(), block.getLineNumber()).error( 1344 "A 'type' attribute is required for <item>\n"); 1345 hasErrors = localHasErrors = true; 1346 } 1347 curIsStyled = true; 1348 } else if (strcmp16(block.getElementName(&len), string16.string()) == 0) { 1349 // Note the existence and locale of every string we process 1350 char rawLocale[RESTABLE_MAX_LOCALE_LEN]; 1351 curParams.getBcp47Locale(rawLocale); 1352 String8 locale(rawLocale); 1353 String16 name; 1354 String16 translatable; 1355 String16 formatted; 1356 1357 size_t n = block.getAttributeCount(); 1358 for (size_t i = 0; i < n; i++) { 1359 size_t length; 1360 const char16_t* attr = block.getAttributeName(i, &length); 1361 if (strcmp16(attr, name16.string()) == 0) { 1362 name.setTo(block.getAttributeStringValue(i, &length)); 1363 } else if (strcmp16(attr, translatable16.string()) == 0) { 1364 translatable.setTo(block.getAttributeStringValue(i, &length)); 1365 } else if (strcmp16(attr, formatted16.string()) == 0) { 1366 formatted.setTo(block.getAttributeStringValue(i, &length)); 1367 } 1368 } 1369 1370 if (name.size() > 0) { 1371 if (locale.size() == 0) { 1372 outTable->addDefaultLocalization(name); 1373 } 1374 if (translatable == false16) { 1375 curIsFormatted = false; 1376 // Untranslatable strings must only exist in the default [empty] locale 1377 if (locale.size() > 0) { 1378 SourcePos(in->getPrintableSource(), block.getLineNumber()).warning( 1379 "string '%s' marked untranslatable but exists in locale '%s'\n", 1380 String8(name).string(), 1381 locale.string()); 1382 // hasErrors = localHasErrors = true; 1383 } else { 1384 // Intentionally empty block: 1385 // 1386 // Don't add untranslatable strings to the localization table; that 1387 // way if we later see localizations of them, they'll be flagged as 1388 // having no default translation. 1389 } 1390 } else { 1391 outTable->addLocalization( 1392 name, 1393 locale, 1394 SourcePos(in->getPrintableSource(), block.getLineNumber())); 1395 } 1396 1397 if (formatted == false16) { 1398 curIsFormatted = false; 1399 } 1400 } 1401 1402 curTag = &string16; 1403 curType = string16; 1404 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING; 1405 curIsStyled = true; 1406 curIsPseudolocalizable = fileIsTranslatable && (translatable != false16); 1407 } else if (strcmp16(block.getElementName(&len), drawable16.string()) == 0) { 1408 curTag = &drawable16; 1409 curType = drawable16; 1410 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR; 1411 } else if (strcmp16(block.getElementName(&len), color16.string()) == 0) { 1412 curTag = &color16; 1413 curType = color16; 1414 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR; 1415 } else if (strcmp16(block.getElementName(&len), bool16.string()) == 0) { 1416 curTag = &bool16; 1417 curType = bool16; 1418 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_BOOLEAN; 1419 } else if (strcmp16(block.getElementName(&len), integer16.string()) == 0) { 1420 curTag = &integer16; 1421 curType = integer16; 1422 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER; 1423 } else if (strcmp16(block.getElementName(&len), dimen16.string()) == 0) { 1424 curTag = &dimen16; 1425 curType = dimen16; 1426 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_DIMENSION; 1427 } else if (strcmp16(block.getElementName(&len), fraction16.string()) == 0) { 1428 curTag = &fraction16; 1429 curType = fraction16; 1430 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_FRACTION; 1431 } else if (strcmp16(block.getElementName(&len), bag16.string()) == 0) { 1432 curTag = &bag16; 1433 curIsBag = true; 1434 ssize_t attri = block.indexOfAttribute(NULL, "type"); 1435 if (attri >= 0) { 1436 curType = String16(block.getAttributeStringValue(attri, &len)); 1437 } else { 1438 SourcePos(in->getPrintableSource(), block.getLineNumber()).error( 1439 "A 'type' attribute is required for <bag>\n"); 1440 hasErrors = localHasErrors = true; 1441 } 1442 } else if (strcmp16(block.getElementName(&len), style16.string()) == 0) { 1443 curTag = &style16; 1444 curType = style16; 1445 curIsBag = true; 1446 } else if (strcmp16(block.getElementName(&len), plurals16.string()) == 0) { 1447 curTag = &plurals16; 1448 curType = plurals16; 1449 curIsBag = true; 1450 curIsPseudolocalizable = fileIsTranslatable; 1451 } else if (strcmp16(block.getElementName(&len), array16.string()) == 0) { 1452 curTag = &array16; 1453 curType = array16; 1454 curIsBag = true; 1455 curIsBagReplaceOnOverwrite = true; 1456 ssize_t formatIdx = block.indexOfAttribute(NULL, "format"); 1457 if (formatIdx >= 0) { 1458 String16 formatStr = String16(block.getAttributeStringValue( 1459 formatIdx, &len)); 1460 curFormat = parse_flags(formatStr.string(), formatStr.size(), 1461 gFormatFlags); 1462 if (curFormat == 0) { 1463 SourcePos(in->getPrintableSource(), block.getLineNumber()).error( 1464 "Tag <array> 'format' attribute value \"%s\" not valid\n", 1465 String8(formatStr).string()); 1466 hasErrors = localHasErrors = true; 1467 } 1468 } 1469 } else if (strcmp16(block.getElementName(&len), string_array16.string()) == 0) { 1470 // Check whether these strings need valid formats. 1471 // (simplified form of what string16 does above) 1472 bool isTranslatable = false; 1473 size_t n = block.getAttributeCount(); 1474 1475 // Pseudolocalizable by default, unless this string array isn't 1476 // translatable. 1477 for (size_t i = 0; i < n; i++) { 1478 size_t length; 1479 const char16_t* attr = block.getAttributeName(i, &length); 1480 if (strcmp16(attr, formatted16.string()) == 0) { 1481 const char16_t* value = block.getAttributeStringValue(i, &length); 1482 if (strcmp16(value, false16.string()) == 0) { 1483 curIsFormatted = false; 1484 } 1485 } else if (strcmp16(attr, translatable16.string()) == 0) { 1486 const char16_t* value = block.getAttributeStringValue(i, &length); 1487 if (strcmp16(value, false16.string()) == 0) { 1488 isTranslatable = false; 1489 } 1490 } 1491 } 1492 1493 curTag = &string_array16; 1494 curType = array16; 1495 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING; 1496 curIsBag = true; 1497 curIsBagReplaceOnOverwrite = true; 1498 curIsPseudolocalizable = isTranslatable && fileIsTranslatable; 1499 } else if (strcmp16(block.getElementName(&len), integer_array16.string()) == 0) { 1500 curTag = &integer_array16; 1501 curType = array16; 1502 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER; 1503 curIsBag = true; 1504 curIsBagReplaceOnOverwrite = true; 1505 } else { 1506 SourcePos(in->getPrintableSource(), block.getLineNumber()).error( 1507 "Found tag %s where item is expected\n", 1508 String8(block.getElementName(&len)).string()); 1509 return UNKNOWN_ERROR; 1510 } 1511 1512 String16 ident; 1513 ssize_t identIdx = block.indexOfAttribute(NULL, "name"); 1514 if (identIdx >= 0) { 1515 ident = String16(block.getAttributeStringValue(identIdx, &len)); 1516 } else { 1517 SourcePos(in->getPrintableSource(), block.getLineNumber()).error( 1518 "A 'name' attribute is required for <%s>\n", 1519 String8(*curTag).string()); 1520 hasErrors = localHasErrors = true; 1521 } 1522 1523 String16 product; 1524 identIdx = block.indexOfAttribute(NULL, "product"); 1525 if (identIdx >= 0) { 1526 product = String16(block.getAttributeStringValue(identIdx, &len)); 1527 } 1528 1529 String16 comment(block.getComment(&len) ? block.getComment(&len) : nulStr); 1530 1531 if (curIsBag) { 1532 // Figure out the parent of this bag... 1533 String16 parentIdent; 1534 ssize_t parentIdentIdx = block.indexOfAttribute(NULL, "parent"); 1535 if (parentIdentIdx >= 0) { 1536 parentIdent = String16(block.getAttributeStringValue(parentIdentIdx, &len)); 1537 } else { 1538 ssize_t sep = ident.findLast('.'); 1539 if (sep >= 0) { 1540 parentIdent.setTo(ident, sep); 1541 } 1542 } 1543 1544 if (!localHasErrors) { 1545 err = outTable->startBag(SourcePos(in->getPrintableSource(), 1546 block.getLineNumber()), myPackage, curType, ident, 1547 parentIdent, &curParams, 1548 overwrite, curIsBagReplaceOnOverwrite); 1549 if (err != NO_ERROR) { 1550 hasErrors = localHasErrors = true; 1551 } 1552 } 1553 1554 ssize_t elmIndex = 0; 1555 char elmIndexStr[14]; 1556 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 1557 && code != ResXMLTree::BAD_DOCUMENT) { 1558 1559 if (code == ResXMLTree::START_TAG) { 1560 if (strcmp16(block.getElementName(&len), item16.string()) != 0) { 1561 SourcePos(in->getPrintableSource(), block.getLineNumber()).error( 1562 "Tag <%s> can not appear inside <%s>, only <item>\n", 1563 String8(block.getElementName(&len)).string(), 1564 String8(*curTag).string()); 1565 return UNKNOWN_ERROR; 1566 } 1567 1568 String16 itemIdent; 1569 if (curType == array16) { 1570 sprintf(elmIndexStr, "^index_%d", (int)elmIndex++); 1571 itemIdent = String16(elmIndexStr); 1572 } else if (curType == plurals16) { 1573 ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "quantity"); 1574 if (itemIdentIdx >= 0) { 1575 String16 quantity16(block.getAttributeStringValue(itemIdentIdx, &len)); 1576 if (quantity16 == other16) { 1577 itemIdent = quantityOther16; 1578 } 1579 else if (quantity16 == zero16) { 1580 itemIdent = quantityZero16; 1581 } 1582 else if (quantity16 == one16) { 1583 itemIdent = quantityOne16; 1584 } 1585 else if (quantity16 == two16) { 1586 itemIdent = quantityTwo16; 1587 } 1588 else if (quantity16 == few16) { 1589 itemIdent = quantityFew16; 1590 } 1591 else if (quantity16 == many16) { 1592 itemIdent = quantityMany16; 1593 } 1594 else { 1595 SourcePos(in->getPrintableSource(), block.getLineNumber()).error( 1596 "Illegal 'quantity' attribute is <item> inside <plurals>\n"); 1597 hasErrors = localHasErrors = true; 1598 } 1599 } else { 1600 SourcePos(in->getPrintableSource(), block.getLineNumber()).error( 1601 "A 'quantity' attribute is required for <item> inside <plurals>\n"); 1602 hasErrors = localHasErrors = true; 1603 } 1604 } else { 1605 ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "name"); 1606 if (itemIdentIdx >= 0) { 1607 itemIdent = String16(block.getAttributeStringValue(itemIdentIdx, &len)); 1608 } else { 1609 SourcePos(in->getPrintableSource(), block.getLineNumber()).error( 1610 "A 'name' attribute is required for <item>\n"); 1611 hasErrors = localHasErrors = true; 1612 } 1613 } 1614 1615 ResXMLParser::ResXMLPosition parserPosition; 1616 block.getPosition(&parserPosition); 1617 1618 err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType, 1619 ident, parentIdent, itemIdent, curFormat, curIsFormatted, 1620 product, NO_PSEUDOLOCALIZATION, overwrite, outTable); 1621 if (err == NO_ERROR) { 1622 if (curIsPseudolocalizable && localeIsDefined(curParams) 1623 && bundle->getPseudolocalize() > 0) { 1624 // pseudolocalize here 1625 if ((PSEUDO_ACCENTED & bundle->getPseudolocalize()) == 1626 PSEUDO_ACCENTED) { 1627 block.setPosition(parserPosition); 1628 err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage, 1629 curType, ident, parentIdent, itemIdent, curFormat, 1630 curIsFormatted, product, PSEUDO_ACCENTED, 1631 overwrite, outTable); 1632 } 1633 if ((PSEUDO_BIDI & bundle->getPseudolocalize()) == 1634 PSEUDO_BIDI) { 1635 block.setPosition(parserPosition); 1636 err = parseAndAddBag(bundle, in, &block, pseudoBidiParams, myPackage, 1637 curType, ident, parentIdent, itemIdent, curFormat, 1638 curIsFormatted, product, PSEUDO_BIDI, 1639 overwrite, outTable); 1640 } 1641 } 1642 } 1643 if (err != NO_ERROR) { 1644 hasErrors = localHasErrors = true; 1645 } 1646 } else if (code == ResXMLTree::END_TAG) { 1647 if (strcmp16(block.getElementName(&len), curTag->string()) != 0) { 1648 SourcePos(in->getPrintableSource(), block.getLineNumber()).error( 1649 "Found tag </%s> where </%s> is expected\n", 1650 String8(block.getElementName(&len)).string(), 1651 String8(*curTag).string()); 1652 return UNKNOWN_ERROR; 1653 } 1654 break; 1655 } 1656 } 1657 } else { 1658 ResXMLParser::ResXMLPosition parserPosition; 1659 block.getPosition(&parserPosition); 1660 1661 err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident, 1662 *curTag, curIsStyled, curFormat, curIsFormatted, 1663 product, NO_PSEUDOLOCALIZATION, overwrite, &skippedResourceNames, outTable); 1664 1665 if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR? 1666 hasErrors = localHasErrors = true; 1667 } 1668 else if (err == NO_ERROR) { 1669 if (curType == string16 && !curParams.language[0] && !curParams.country[0]) { 1670 outTable->addDefaultLocalization(curName); 1671 } 1672 if (curIsPseudolocalizable && localeIsDefined(curParams) 1673 && bundle->getPseudolocalize() > 0) { 1674 // pseudolocalize here 1675 if ((PSEUDO_ACCENTED & bundle->getPseudolocalize()) == 1676 PSEUDO_ACCENTED) { 1677 block.setPosition(parserPosition); 1678 err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType, 1679 ident, *curTag, curIsStyled, curFormat, 1680 curIsFormatted, product, 1681 PSEUDO_ACCENTED, overwrite, &skippedResourceNames, outTable); 1682 } 1683 if ((PSEUDO_BIDI & bundle->getPseudolocalize()) == 1684 PSEUDO_BIDI) { 1685 block.setPosition(parserPosition); 1686 err = parseAndAddEntry(bundle, in, &block, pseudoBidiParams, 1687 myPackage, curType, ident, *curTag, curIsStyled, curFormat, 1688 curIsFormatted, product, 1689 PSEUDO_BIDI, overwrite, &skippedResourceNames, outTable); 1690 } 1691 if (err != NO_ERROR) { 1692 hasErrors = localHasErrors = true; 1693 } 1694 } 1695 } 1696 } 1697 1698 #if 0 1699 if (comment.size() > 0) { 1700 printf("Comment for @%s:%s/%s: %s\n", String8(myPackage).string(), 1701 String8(curType).string(), String8(ident).string(), 1702 String8(comment).string()); 1703 } 1704 #endif 1705 if (!localHasErrors) { 1706 outTable->appendComment(myPackage, curType, ident, comment, false); 1707 } 1708 } 1709 else if (code == ResXMLTree::END_TAG) { 1710 if (strcmp16(block.getElementName(&len), resources16.string()) != 0) { 1711 SourcePos(in->getPrintableSource(), block.getLineNumber()).error( 1712 "Unexpected end tag %s\n", String8(block.getElementName(&len)).string()); 1713 return UNKNOWN_ERROR; 1714 } 1715 } 1716 else if (code == ResXMLTree::START_NAMESPACE || code == ResXMLTree::END_NAMESPACE) { 1717 } 1718 else if (code == ResXMLTree::TEXT) { 1719 if (isWhitespace(block.getText(&len))) { 1720 continue; 1721 } 1722 SourcePos(in->getPrintableSource(), block.getLineNumber()).error( 1723 "Found text \"%s\" where item tag is expected\n", 1724 String8(block.getText(&len)).string()); 1725 return UNKNOWN_ERROR; 1726 } 1727 } 1728 1729 // For every resource defined, there must be exist one variant with a product attribute 1730 // set to 'default' (or no product attribute at all). 1731 // We check to see that for every resource that was ignored because of a mismatched 1732 // product attribute, some product variant of that resource was processed. 1733 for (size_t i = 0; i < skippedResourceNames.size(); i++) { 1734 if (skippedResourceNames[i]) { 1735 const type_ident_pair_t& p = skippedResourceNames.keyAt(i); 1736 if (!outTable->hasBagOrEntry(myPackage, p.type, p.ident)) { 1737 const char* bundleProduct = 1738 (bundle->getProduct() == NULL) ? "" : bundle->getProduct(); 1739 fprintf(stderr, "In resource file %s: %s\n", 1740 in->getPrintableSource().string(), 1741 curParams.toString().string()); 1742 1743 fprintf(stderr, "\t%s '%s' does not match product %s.\n" 1744 "\tYou may have forgotten to include a 'default' product variant" 1745 " of the resource.\n", 1746 String8(p.type).string(), String8(p.ident).string(), 1747 bundleProduct[0] == 0 ? "default" : bundleProduct); 1748 return UNKNOWN_ERROR; 1749 } 1750 } 1751 } 1752 1753 return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR; 1754 } 1755 1756 ResourceTable::ResourceTable(Bundle* bundle, const String16& assetsPackage, ResourceTable::PackageType type) 1757 : mAssetsPackage(assetsPackage) 1758 , mPackageType(type) 1759 , mTypeIdOffset(0) 1760 , mNumLocal(0) 1761 , mBundle(bundle) 1762 { 1763 ssize_t packageId = -1; 1764 switch (mPackageType) { 1765 case App: 1766 case AppFeature: 1767 packageId = 0x7f; 1768 break; 1769 1770 case System: 1771 packageId = 0x01; 1772 break; 1773 1774 case SharedLibrary: 1775 packageId = 0x00; 1776 break; 1777 1778 default: 1779 assert(0); 1780 break; 1781 } 1782 sp<Package> package = new Package(mAssetsPackage, packageId); 1783 mPackages.add(assetsPackage, package); 1784 mOrderedPackages.add(package); 1785 1786 // Every resource table always has one first entry, the bag attributes. 1787 const SourcePos unknown(String8("????"), 0); 1788 getType(mAssetsPackage, String16("attr"), unknown); 1789 } 1790 1791 static uint32_t findLargestTypeIdForPackage(const ResTable& table, const String16& packageName) { 1792 const size_t basePackageCount = table.getBasePackageCount(); 1793 for (size_t i = 0; i < basePackageCount; i++) { 1794 if (packageName == table.getBasePackageName(i)) { 1795 return table.getLastTypeIdForPackage(i); 1796 } 1797 } 1798 return 0; 1799 } 1800 1801 status_t ResourceTable::addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets) 1802 { 1803 status_t err = assets->buildIncludedResources(bundle); 1804 if (err != NO_ERROR) { 1805 return err; 1806 } 1807 1808 mAssets = assets; 1809 mTypeIdOffset = findLargestTypeIdForPackage(assets->getIncludedResources(), mAssetsPackage); 1810 1811 const String8& featureAfter = bundle->getFeatureAfterPackage(); 1812 if (!featureAfter.isEmpty()) { 1813 AssetManager featureAssetManager; 1814 if (!featureAssetManager.addAssetPath(featureAfter, NULL)) { 1815 fprintf(stderr, "ERROR: Feature package '%s' not found.\n", 1816 featureAfter.string()); 1817 return UNKNOWN_ERROR; 1818 } 1819 1820 const ResTable& featureTable = featureAssetManager.getResources(false); 1821 mTypeIdOffset = std::max(mTypeIdOffset, 1822 findLargestTypeIdForPackage(featureTable, mAssetsPackage)); 1823 } 1824 1825 return NO_ERROR; 1826 } 1827 1828 status_t ResourceTable::addPublic(const SourcePos& sourcePos, 1829 const String16& package, 1830 const String16& type, 1831 const String16& name, 1832 const uint32_t ident) 1833 { 1834 uint32_t rid = mAssets->getIncludedResources() 1835 .identifierForName(name.string(), name.size(), 1836 type.string(), type.size(), 1837 package.string(), package.size()); 1838 if (rid != 0) { 1839 sourcePos.error("Error declaring public resource %s/%s for included package %s\n", 1840 String8(type).string(), String8(name).string(), 1841 String8(package).string()); 1842 return UNKNOWN_ERROR; 1843 } 1844 1845 sp<Type> t = getType(package, type, sourcePos); 1846 if (t == NULL) { 1847 return UNKNOWN_ERROR; 1848 } 1849 return t->addPublic(sourcePos, name, ident); 1850 } 1851 1852 status_t ResourceTable::addEntry(const SourcePos& sourcePos, 1853 const String16& package, 1854 const String16& type, 1855 const String16& name, 1856 const String16& value, 1857 const Vector<StringPool::entry_style_span>* style, 1858 const ResTable_config* params, 1859 const bool doSetIndex, 1860 const int32_t format, 1861 const bool overwrite) 1862 { 1863 uint32_t rid = mAssets->getIncludedResources() 1864 .identifierForName(name.string(), name.size(), 1865 type.string(), type.size(), 1866 package.string(), package.size()); 1867 if (rid != 0) { 1868 sourcePos.error("Resource entry %s/%s is already defined in package %s.", 1869 String8(type).string(), String8(name).string(), String8(package).string()); 1870 return UNKNOWN_ERROR; 1871 } 1872 1873 sp<Entry> e = getEntry(package, type, name, sourcePos, overwrite, 1874 params, doSetIndex); 1875 if (e == NULL) { 1876 return UNKNOWN_ERROR; 1877 } 1878 status_t err = e->setItem(sourcePos, value, style, format, overwrite); 1879 if (err == NO_ERROR) { 1880 mNumLocal++; 1881 } 1882 return err; 1883 } 1884 1885 status_t ResourceTable::startBag(const SourcePos& sourcePos, 1886 const String16& package, 1887 const String16& type, 1888 const String16& name, 1889 const String16& bagParent, 1890 const ResTable_config* params, 1891 bool overlay, 1892 bool replace, bool /* isId */) 1893 { 1894 status_t result = NO_ERROR; 1895 1896 // Check for adding entries in other packages... for now we do 1897 // nothing. We need to do the right thing here to support skinning. 1898 uint32_t rid = mAssets->getIncludedResources() 1899 .identifierForName(name.string(), name.size(), 1900 type.string(), type.size(), 1901 package.string(), package.size()); 1902 if (rid != 0) { 1903 sourcePos.error("Resource entry %s/%s is already defined in package %s.", 1904 String8(type).string(), String8(name).string(), String8(package).string()); 1905 return UNKNOWN_ERROR; 1906 } 1907 1908 if (overlay && !mBundle->getAutoAddOverlay() && !hasBagOrEntry(package, type, name)) { 1909 bool canAdd = false; 1910 sp<Package> p = mPackages.valueFor(package); 1911 if (p != NULL) { 1912 sp<Type> t = p->getTypes().valueFor(type); 1913 if (t != NULL) { 1914 if (t->getCanAddEntries().indexOf(name) >= 0) { 1915 canAdd = true; 1916 } 1917 } 1918 } 1919 if (!canAdd) { 1920 sourcePos.error("Resource does not already exist in overlay at '%s'; use <add-resource> to add.\n", 1921 String8(name).string()); 1922 return UNKNOWN_ERROR; 1923 } 1924 } 1925 sp<Entry> e = getEntry(package, type, name, sourcePos, overlay, params); 1926 if (e == NULL) { 1927 return UNKNOWN_ERROR; 1928 } 1929 1930 // If a parent is explicitly specified, set it. 1931 if (bagParent.size() > 0) { 1932 e->setParent(bagParent); 1933 } 1934 1935 if ((result = e->makeItABag(sourcePos)) != NO_ERROR) { 1936 return result; 1937 } 1938 1939 if (overlay && replace) { 1940 return e->emptyBag(sourcePos); 1941 } 1942 return result; 1943 } 1944 1945 status_t ResourceTable::addBag(const SourcePos& sourcePos, 1946 const String16& package, 1947 const String16& type, 1948 const String16& name, 1949 const String16& bagParent, 1950 const String16& bagKey, 1951 const String16& value, 1952 const Vector<StringPool::entry_style_span>* style, 1953 const ResTable_config* params, 1954 bool replace, bool isId, const int32_t format) 1955 { 1956 // Check for adding entries in other packages... for now we do 1957 // nothing. We need to do the right thing here to support skinning. 1958 uint32_t rid = mAssets->getIncludedResources() 1959 .identifierForName(name.string(), name.size(), 1960 type.string(), type.size(), 1961 package.string(), package.size()); 1962 if (rid != 0) { 1963 return NO_ERROR; 1964 } 1965 1966 #if 0 1967 if (name == String16("left")) { 1968 printf("Adding bag left: file=%s, line=%d, type=%s\n", 1969 sourcePos.file.striing(), sourcePos.line, String8(type).string()); 1970 } 1971 #endif 1972 sp<Entry> e = getEntry(package, type, name, sourcePos, replace, params); 1973 if (e == NULL) { 1974 return UNKNOWN_ERROR; 1975 } 1976 1977 // If a parent is explicitly specified, set it. 1978 if (bagParent.size() > 0) { 1979 e->setParent(bagParent); 1980 } 1981 1982 const bool first = e->getBag().indexOfKey(bagKey) < 0; 1983 status_t err = e->addToBag(sourcePos, bagKey, value, style, replace, isId, format); 1984 if (err == NO_ERROR && first) { 1985 mNumLocal++; 1986 } 1987 return err; 1988 } 1989 1990 bool ResourceTable::hasBagOrEntry(const String16& package, 1991 const String16& type, 1992 const String16& name) const 1993 { 1994 // First look for this in the included resources... 1995 uint32_t rid = mAssets->getIncludedResources() 1996 .identifierForName(name.string(), name.size(), 1997 type.string(), type.size(), 1998 package.string(), package.size()); 1999 if (rid != 0) { 2000 return true; 2001 } 2002 2003 sp<Package> p = mPackages.valueFor(package); 2004 if (p != NULL) { 2005 sp<Type> t = p->getTypes().valueFor(type); 2006 if (t != NULL) { 2007 sp<ConfigList> c = t->getConfigs().valueFor(name); 2008 if (c != NULL) return true; 2009 } 2010 } 2011 2012 return false; 2013 } 2014 2015 bool ResourceTable::hasBagOrEntry(const String16& package, 2016 const String16& type, 2017 const String16& name, 2018 const ResTable_config& config) const 2019 { 2020 // First look for this in the included resources... 2021 uint32_t rid = mAssets->getIncludedResources() 2022 .identifierForName(name.string(), name.size(), 2023 type.string(), type.size(), 2024 package.string(), package.size()); 2025 if (rid != 0) { 2026 return true; 2027 } 2028 2029 sp<Package> p = mPackages.valueFor(package); 2030 if (p != NULL) { 2031 sp<Type> t = p->getTypes().valueFor(type); 2032 if (t != NULL) { 2033 sp<ConfigList> c = t->getConfigs().valueFor(name); 2034 if (c != NULL) { 2035 sp<Entry> e = c->getEntries().valueFor(config); 2036 if (e != NULL) { 2037 return true; 2038 } 2039 } 2040 } 2041 } 2042 2043 return false; 2044 } 2045 2046 bool ResourceTable::hasBagOrEntry(const String16& ref, 2047 const String16* defType, 2048 const String16* defPackage) 2049 { 2050 String16 package, type, name; 2051 if (!ResTable::expandResourceRef(ref.string(), ref.size(), &package, &type, &name, 2052 defType, defPackage ? defPackage:&mAssetsPackage, NULL)) { 2053 return false; 2054 } 2055 return hasBagOrEntry(package, type, name); 2056 } 2057 2058 bool ResourceTable::appendComment(const String16& package, 2059 const String16& type, 2060 const String16& name, 2061 const String16& comment, 2062 bool onlyIfEmpty) 2063 { 2064 if (comment.size() <= 0) { 2065 return true; 2066 } 2067 2068 sp<Package> p = mPackages.valueFor(package); 2069 if (p != NULL) { 2070 sp<Type> t = p->getTypes().valueFor(type); 2071 if (t != NULL) { 2072 sp<ConfigList> c = t->getConfigs().valueFor(name); 2073 if (c != NULL) { 2074 c->appendComment(comment, onlyIfEmpty); 2075 return true; 2076 } 2077 } 2078 } 2079 return false; 2080 } 2081 2082 bool ResourceTable::appendTypeComment(const String16& package, 2083 const String16& type, 2084 const String16& name, 2085 const String16& comment) 2086 { 2087 if (comment.size() <= 0) { 2088 return true; 2089 } 2090 2091 sp<Package> p = mPackages.valueFor(package); 2092 if (p != NULL) { 2093 sp<Type> t = p->getTypes().valueFor(type); 2094 if (t != NULL) { 2095 sp<ConfigList> c = t->getConfigs().valueFor(name); 2096 if (c != NULL) { 2097 c->appendTypeComment(comment); 2098 return true; 2099 } 2100 } 2101 } 2102 return false; 2103 } 2104 2105 void ResourceTable::canAddEntry(const SourcePos& pos, 2106 const String16& package, const String16& type, const String16& name) 2107 { 2108 sp<Type> t = getType(package, type, pos); 2109 if (t != NULL) { 2110 t->canAddEntry(name); 2111 } 2112 } 2113 2114 size_t ResourceTable::size() const { 2115 return mPackages.size(); 2116 } 2117 2118 size_t ResourceTable::numLocalResources() const { 2119 return mNumLocal; 2120 } 2121 2122 bool ResourceTable::hasResources() const { 2123 return mNumLocal > 0; 2124 } 2125 2126 sp<AaptFile> ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, 2127 const bool isBase) 2128 { 2129 sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8()); 2130 status_t err = flatten(bundle, filter, data, isBase); 2131 return err == NO_ERROR ? data : NULL; 2132 } 2133 2134 inline uint32_t ResourceTable::getResId(const sp<Package>& p, 2135 const sp<Type>& t, 2136 uint32_t nameId) 2137 { 2138 return makeResId(p->getAssignedId(), t->getIndex(), nameId); 2139 } 2140 2141 uint32_t ResourceTable::getResId(const String16& package, 2142 const String16& type, 2143 const String16& name, 2144 bool onlyPublic) const 2145 { 2146 uint32_t id = ResourceIdCache::lookup(package, type, name, onlyPublic); 2147 if (id != 0) return id; // cache hit 2148 2149 // First look for this in the included resources... 2150 uint32_t specFlags = 0; 2151 uint32_t rid = mAssets->getIncludedResources() 2152 .identifierForName(name.string(), name.size(), 2153 type.string(), type.size(), 2154 package.string(), package.size(), 2155 &specFlags); 2156 if (rid != 0) { 2157 if (onlyPublic) { 2158 if ((specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) { 2159 return 0; 2160 } 2161 } 2162 2163 return ResourceIdCache::store(package, type, name, onlyPublic, rid); 2164 } 2165 2166 sp<Package> p = mPackages.valueFor(package); 2167 if (p == NULL) return 0; 2168 sp<Type> t = p->getTypes().valueFor(type); 2169 if (t == NULL) return 0; 2170 sp<ConfigList> c = t->getConfigs().valueFor(name); 2171 if (c == NULL) { 2172 if (type != String16("attr")) { 2173 return 0; 2174 } 2175 t = p->getTypes().valueFor(String16(kAttrPrivateType)); 2176 if (t == NULL) return 0; 2177 c = t->getConfigs().valueFor(name); 2178 if (c == NULL) return 0; 2179 } 2180 int32_t ei = c->getEntryIndex(); 2181 if (ei < 0) return 0; 2182 2183 return ResourceIdCache::store(package, type, name, onlyPublic, 2184 getResId(p, t, ei)); 2185 } 2186 2187 uint32_t ResourceTable::getResId(const String16& ref, 2188 const String16* defType, 2189 const String16* defPackage, 2190 const char** outErrorMsg, 2191 bool onlyPublic) const 2192 { 2193 String16 package, type, name; 2194 bool refOnlyPublic = true; 2195 if (!ResTable::expandResourceRef( 2196 ref.string(), ref.size(), &package, &type, &name, 2197 defType, defPackage ? defPackage:&mAssetsPackage, 2198 outErrorMsg, &refOnlyPublic)) { 2199 if (kIsDebug) { 2200 printf("Expanding resource: ref=%s\n", String8(ref).string()); 2201 printf("Expanding resource: defType=%s\n", 2202 defType ? String8(*defType).string() : "NULL"); 2203 printf("Expanding resource: defPackage=%s\n", 2204 defPackage ? String8(*defPackage).string() : "NULL"); 2205 printf("Expanding resource: ref=%s\n", String8(ref).string()); 2206 printf("Expanded resource: p=%s, t=%s, n=%s, res=0\n", 2207 String8(package).string(), String8(type).string(), 2208 String8(name).string()); 2209 } 2210 return 0; 2211 } 2212 uint32_t res = getResId(package, type, name, onlyPublic && refOnlyPublic); 2213 if (kIsDebug) { 2214 printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n", 2215 String8(package).string(), String8(type).string(), 2216 String8(name).string(), res); 2217 } 2218 if (res == 0) { 2219 if (outErrorMsg) 2220 *outErrorMsg = "No resource found that matches the given name"; 2221 } 2222 return res; 2223 } 2224 2225 bool ResourceTable::isValidResourceName(const String16& s) 2226 { 2227 const char16_t* p = s.string(); 2228 bool first = true; 2229 while (*p) { 2230 if ((*p >= 'a' && *p <= 'z') 2231 || (*p >= 'A' && *p <= 'Z') 2232 || *p == '_' 2233 || (!first && *p >= '0' && *p <= '9')) { 2234 first = false; 2235 p++; 2236 continue; 2237 } 2238 return false; 2239 } 2240 return true; 2241 } 2242 2243 bool ResourceTable::stringToValue(Res_value* outValue, StringPool* pool, 2244 const String16& str, 2245 bool preserveSpaces, bool coerceType, 2246 uint32_t attrID, 2247 const Vector<StringPool::entry_style_span>* style, 2248 String16* outStr, void* accessorCookie, 2249 uint32_t attrType, const String8* configTypeName, 2250 const ConfigDescription* config) 2251 { 2252 String16 finalStr; 2253 2254 bool res = true; 2255 if (style == NULL || style->size() == 0) { 2256 // Text is not styled so it can be any type... let's figure it out. 2257 res = mAssets->getIncludedResources() 2258 .stringToValue(outValue, &finalStr, str.string(), str.size(), preserveSpaces, 2259 coerceType, attrID, NULL, &mAssetsPackage, this, 2260 accessorCookie, attrType); 2261 } else { 2262 // Styled text can only be a string, and while collecting the style 2263 // information we have already processed that string! 2264 outValue->size = sizeof(Res_value); 2265 outValue->res0 = 0; 2266 outValue->dataType = outValue->TYPE_STRING; 2267 outValue->data = 0; 2268 finalStr = str; 2269 } 2270 2271 if (!res) { 2272 return false; 2273 } 2274 2275 if (outValue->dataType == outValue->TYPE_STRING) { 2276 // Should do better merging styles. 2277 if (pool) { 2278 String8 configStr; 2279 if (config != NULL) { 2280 configStr = config->toString(); 2281 } else { 2282 configStr = "(null)"; 2283 } 2284 if (kIsDebug) { 2285 printf("Adding to pool string style #%zu config %s: %s\n", 2286 style != NULL ? style->size() : 0U, 2287 configStr.string(), String8(finalStr).string()); 2288 } 2289 if (style != NULL && style->size() > 0) { 2290 outValue->data = pool->add(finalStr, *style, configTypeName, config); 2291 } else { 2292 outValue->data = pool->add(finalStr, true, configTypeName, config); 2293 } 2294 } else { 2295 // Caller will fill this in later. 2296 outValue->data = 0; 2297 } 2298 2299 if (outStr) { 2300 *outStr = finalStr; 2301 } 2302 2303 } 2304 2305 return true; 2306 } 2307 2308 uint32_t ResourceTable::getCustomResource( 2309 const String16& package, const String16& type, const String16& name) const 2310 { 2311 //printf("getCustomResource: %s %s %s\n", String8(package).string(), 2312 // String8(type).string(), String8(name).string()); 2313 sp<Package> p = mPackages.valueFor(package); 2314 if (p == NULL) return 0; 2315 sp<Type> t = p->getTypes().valueFor(type); 2316 if (t == NULL) return 0; 2317 sp<ConfigList> c = t->getConfigs().valueFor(name); 2318 if (c == NULL) { 2319 if (type != String16("attr")) { 2320 return 0; 2321 } 2322 t = p->getTypes().valueFor(String16(kAttrPrivateType)); 2323 if (t == NULL) return 0; 2324 c = t->getConfigs().valueFor(name); 2325 if (c == NULL) return 0; 2326 } 2327 int32_t ei = c->getEntryIndex(); 2328 if (ei < 0) return 0; 2329 return getResId(p, t, ei); 2330 } 2331 2332 uint32_t ResourceTable::getCustomResourceWithCreation( 2333 const String16& package, const String16& type, const String16& name, 2334 const bool createIfNotFound) 2335 { 2336 uint32_t resId = getCustomResource(package, type, name); 2337 if (resId != 0 || !createIfNotFound) { 2338 return resId; 2339 } 2340 2341 if (mAssetsPackage != package) { 2342 mCurrentXmlPos.error("creating resource for external package %s: %s/%s.", 2343 String8(package).string(), String8(type).string(), String8(name).string()); 2344 if (package == String16("android")) { 2345 mCurrentXmlPos.printf("did you mean to use @+id instead of @+android:id?"); 2346 } 2347 return 0; 2348 } 2349 2350 String16 value("false"); 2351 status_t status = addEntry(mCurrentXmlPos, package, type, name, value, NULL, NULL, true); 2352 if (status == NO_ERROR) { 2353 resId = getResId(package, type, name); 2354 return resId; 2355 } 2356 return 0; 2357 } 2358 2359 uint32_t ResourceTable::getRemappedPackage(uint32_t origPackage) const 2360 { 2361 return origPackage; 2362 } 2363 2364 bool ResourceTable::getAttributeType(uint32_t attrID, uint32_t* outType) 2365 { 2366 //printf("getAttributeType #%08x\n", attrID); 2367 Res_value value; 2368 if (getItemValue(attrID, ResTable_map::ATTR_TYPE, &value)) { 2369 //printf("getAttributeType #%08x (%s): #%08x\n", attrID, 2370 // String8(getEntry(attrID)->getName()).string(), value.data); 2371 *outType = value.data; 2372 return true; 2373 } 2374 return false; 2375 } 2376 2377 bool ResourceTable::getAttributeMin(uint32_t attrID, uint32_t* outMin) 2378 { 2379 //printf("getAttributeMin #%08x\n", attrID); 2380 Res_value value; 2381 if (getItemValue(attrID, ResTable_map::ATTR_MIN, &value)) { 2382 *outMin = value.data; 2383 return true; 2384 } 2385 return false; 2386 } 2387 2388 bool ResourceTable::getAttributeMax(uint32_t attrID, uint32_t* outMax) 2389 { 2390 //printf("getAttributeMax #%08x\n", attrID); 2391 Res_value value; 2392 if (getItemValue(attrID, ResTable_map::ATTR_MAX, &value)) { 2393 *outMax = value.data; 2394 return true; 2395 } 2396 return false; 2397 } 2398 2399 uint32_t ResourceTable::getAttributeL10N(uint32_t attrID) 2400 { 2401 //printf("getAttributeL10N #%08x\n", attrID); 2402 Res_value value; 2403 if (getItemValue(attrID, ResTable_map::ATTR_L10N, &value)) { 2404 return value.data; 2405 } 2406 return ResTable_map::L10N_NOT_REQUIRED; 2407 } 2408 2409 bool ResourceTable::getLocalizationSetting() 2410 { 2411 return mBundle->getRequireLocalization(); 2412 } 2413 2414 void ResourceTable::reportError(void* accessorCookie, const char* fmt, ...) 2415 { 2416 if (accessorCookie != NULL && fmt != NULL) { 2417 AccessorCookie* ac = (AccessorCookie*)accessorCookie; 2418 int retval=0; 2419 char buf[1024]; 2420 va_list ap; 2421 va_start(ap, fmt); 2422 retval = vsnprintf(buf, sizeof(buf), fmt, ap); 2423 va_end(ap); 2424 ac->sourcePos.error("Error: %s (at '%s' with value '%s').\n", 2425 buf, ac->attr.string(), ac->value.string()); 2426 } 2427 } 2428 2429 bool ResourceTable::getAttributeKeys( 2430 uint32_t attrID, Vector<String16>* outKeys) 2431 { 2432 sp<const Entry> e = getEntry(attrID); 2433 if (e != NULL) { 2434 const size_t N = e->getBag().size(); 2435 for (size_t i=0; i<N; i++) { 2436 const String16& key = e->getBag().keyAt(i); 2437 if (key.size() > 0 && key.string()[0] != '^') { 2438 outKeys->add(key); 2439 } 2440 } 2441 return true; 2442 } 2443 return false; 2444 } 2445 2446 bool ResourceTable::getAttributeEnum( 2447 uint32_t attrID, const char16_t* name, size_t nameLen, 2448 Res_value* outValue) 2449 { 2450 //printf("getAttributeEnum #%08x %s\n", attrID, String8(name, nameLen).string()); 2451 String16 nameStr(name, nameLen); 2452 sp<const Entry> e = getEntry(attrID); 2453 if (e != NULL) { 2454 const size_t N = e->getBag().size(); 2455 for (size_t i=0; i<N; i++) { 2456 //printf("Comparing %s to %s\n", String8(name, nameLen).string(), 2457 // String8(e->getBag().keyAt(i)).string()); 2458 if (e->getBag().keyAt(i) == nameStr) { 2459 return getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, outValue); 2460 } 2461 } 2462 } 2463 return false; 2464 } 2465 2466 bool ResourceTable::getAttributeFlags( 2467 uint32_t attrID, const char16_t* name, size_t nameLen, 2468 Res_value* outValue) 2469 { 2470 outValue->dataType = Res_value::TYPE_INT_HEX; 2471 outValue->data = 0; 2472 2473 //printf("getAttributeFlags #%08x %s\n", attrID, String8(name, nameLen).string()); 2474 String16 nameStr(name, nameLen); 2475 sp<const Entry> e = getEntry(attrID); 2476 if (e != NULL) { 2477 const size_t N = e->getBag().size(); 2478 2479 const char16_t* end = name + nameLen; 2480 const char16_t* pos = name; 2481 while (pos < end) { 2482 const char16_t* start = pos; 2483 while (pos < end && *pos != '|') { 2484 pos++; 2485 } 2486 2487 String16 nameStr(start, pos-start); 2488 size_t i; 2489 for (i=0; i<N; i++) { 2490 //printf("Comparing \"%s\" to \"%s\"\n", String8(nameStr).string(), 2491 // String8(e->getBag().keyAt(i)).string()); 2492 if (e->getBag().keyAt(i) == nameStr) { 2493 Res_value val; 2494 bool got = getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, &val); 2495 if (!got) { 2496 return false; 2497 } 2498 //printf("Got value: 0x%08x\n", val.data); 2499 outValue->data |= val.data; 2500 break; 2501 } 2502 } 2503 2504 if (i >= N) { 2505 // Didn't find this flag identifier. 2506 return false; 2507 } 2508 pos++; 2509 } 2510 2511 return true; 2512 } 2513 return false; 2514 } 2515 2516 status_t ResourceTable::assignResourceIds() 2517 { 2518 const size_t N = mOrderedPackages.size(); 2519 size_t pi; 2520 status_t firstError = NO_ERROR; 2521 2522 // First generate all bag attributes and assign indices. 2523 for (pi=0; pi<N; pi++) { 2524 sp<Package> p = mOrderedPackages.itemAt(pi); 2525 if (p == NULL || p->getTypes().size() == 0) { 2526 // Empty, skip! 2527 continue; 2528 } 2529 2530 if (mPackageType == System) { 2531 p->movePrivateAttrs(); 2532 } 2533 2534 // This has no sense for packages being built as AppFeature (aka with a non-zero offset). 2535 status_t err = p->applyPublicTypeOrder(); 2536 if (err != NO_ERROR && firstError == NO_ERROR) { 2537 firstError = err; 2538 } 2539 2540 // Generate attributes... 2541 const size_t N = p->getOrderedTypes().size(); 2542 size_t ti; 2543 for (ti=0; ti<N; ti++) { 2544 sp<Type> t = p->getOrderedTypes().itemAt(ti); 2545 if (t == NULL) { 2546 continue; 2547 } 2548 const size_t N = t->getOrderedConfigs().size(); 2549 for (size_t ci=0; ci<N; ci++) { 2550 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci); 2551 if (c == NULL) { 2552 continue; 2553 } 2554 const size_t N = c->getEntries().size(); 2555 for (size_t ei=0; ei<N; ei++) { 2556 sp<Entry> e = c->getEntries().valueAt(ei); 2557 if (e == NULL) { 2558 continue; 2559 } 2560 status_t err = e->generateAttributes(this, p->getName()); 2561 if (err != NO_ERROR && firstError == NO_ERROR) { 2562 firstError = err; 2563 } 2564 } 2565 } 2566 } 2567 2568 uint32_t typeIdOffset = 0; 2569 if (mPackageType == AppFeature && p->getName() == mAssetsPackage) { 2570 typeIdOffset = mTypeIdOffset; 2571 } 2572 2573 const SourcePos unknown(String8("????"), 0); 2574 sp<Type> attr = p->getType(String16("attr"), unknown); 2575 2576 // Assign indices... 2577 const size_t typeCount = p->getOrderedTypes().size(); 2578 for (size_t ti = 0; ti < typeCount; ti++) { 2579 sp<Type> t = p->getOrderedTypes().itemAt(ti); 2580 if (t == NULL) { 2581 continue; 2582 } 2583 2584 err = t->applyPublicEntryOrder(); 2585 if (err != NO_ERROR && firstError == NO_ERROR) { 2586 firstError = err; 2587 } 2588 2589 const size_t N = t->getOrderedConfigs().size(); 2590 t->setIndex(ti + 1 + typeIdOffset); 2591 2592 LOG_ALWAYS_FATAL_IF(ti == 0 && attr != t, 2593 "First type is not attr!"); 2594 2595 for (size_t ei=0; ei<N; ei++) { 2596 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ei); 2597 if (c == NULL) { 2598 continue; 2599 } 2600 c->setEntryIndex(ei); 2601 } 2602 } 2603 2604 2605 // Assign resource IDs to keys in bags... 2606 for (size_t ti = 0; ti < typeCount; ti++) { 2607 sp<Type> t = p->getOrderedTypes().itemAt(ti); 2608 if (t == NULL) { 2609 continue; 2610 } 2611 2612 const size_t N = t->getOrderedConfigs().size(); 2613 for (size_t ci=0; ci<N; ci++) { 2614 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci); 2615 if (c == NULL) { 2616 continue; 2617 } 2618 //printf("Ordered config #%d: %p\n", ci, c.get()); 2619 const size_t N = c->getEntries().size(); 2620 for (size_t ei=0; ei<N; ei++) { 2621 sp<Entry> e = c->getEntries().valueAt(ei); 2622 if (e == NULL) { 2623 continue; 2624 } 2625 status_t err = e->assignResourceIds(this, p->getName()); 2626 if (err != NO_ERROR && firstError == NO_ERROR) { 2627 firstError = err; 2628 } 2629 } 2630 } 2631 } 2632 } 2633 return firstError; 2634 } 2635 2636 status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols, 2637 bool skipSymbolsWithoutDefaultLocalization) { 2638 const size_t N = mOrderedPackages.size(); 2639 const String8 defaultLocale; 2640 const String16 stringType("string"); 2641 size_t pi; 2642 2643 for (pi=0; pi<N; pi++) { 2644 sp<Package> p = mOrderedPackages.itemAt(pi); 2645 if (p->getTypes().size() == 0) { 2646 // Empty, skip! 2647 continue; 2648 } 2649 2650 const size_t N = p->getOrderedTypes().size(); 2651 size_t ti; 2652 2653 for (ti=0; ti<N; ti++) { 2654 sp<Type> t = p->getOrderedTypes().itemAt(ti); 2655 if (t == NULL) { 2656 continue; 2657 } 2658 2659 const size_t N = t->getOrderedConfigs().size(); 2660 sp<AaptSymbols> typeSymbols; 2661 if (t->getName() == String16(kAttrPrivateType)) { 2662 typeSymbols = outSymbols->addNestedSymbol(String8("attr"), t->getPos()); 2663 } else { 2664 typeSymbols = outSymbols->addNestedSymbol(String8(t->getName()), t->getPos()); 2665 } 2666 2667 if (typeSymbols == NULL) { 2668 return UNKNOWN_ERROR; 2669 } 2670 2671 for (size_t ci=0; ci<N; ci++) { 2672 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci); 2673 if (c == NULL) { 2674 continue; 2675 } 2676 uint32_t rid = getResId(p, t, ci); 2677 if (rid == 0) { 2678 return UNKNOWN_ERROR; 2679 } 2680 if (Res_GETPACKAGE(rid) + 1 == p->getAssignedId()) { 2681 2682 if (skipSymbolsWithoutDefaultLocalization && 2683 t->getName() == stringType) { 2684 2685 // Don't generate symbols for strings without a default localization. 2686 if (mHasDefaultLocalization.find(c->getName()) 2687 == mHasDefaultLocalization.end()) { 2688 // printf("Skip symbol [%08x] %s\n", rid, 2689 // String8(c->getName()).string()); 2690 continue; 2691 } 2692 } 2693 2694 typeSymbols->addSymbol(String8(c->getName()), rid, c->getPos()); 2695 2696 String16 comment(c->getComment()); 2697 typeSymbols->appendComment(String8(c->getName()), comment, c->getPos()); 2698 //printf("Type symbol [%08x] %s comment: %s\n", rid, 2699 // String8(c->getName()).string(), String8(comment).string()); 2700 comment = c->getTypeComment(); 2701 typeSymbols->appendTypeComment(String8(c->getName()), comment); 2702 } 2703 } 2704 } 2705 } 2706 return NO_ERROR; 2707 } 2708 2709 2710 void 2711 ResourceTable::addLocalization(const String16& name, const String8& locale, const SourcePos& src) 2712 { 2713 mLocalizations[name][locale] = src; 2714 } 2715 2716 void 2717 ResourceTable::addDefaultLocalization(const String16& name) 2718 { 2719 mHasDefaultLocalization.insert(name); 2720 } 2721 2722 2723 /*! 2724 * Flag various sorts of localization problems. '+' indicates checks already implemented; 2725 * '-' indicates checks that will be implemented in the future. 2726 * 2727 * + A localized string for which no default-locale version exists => warning 2728 * + A string for which no version in an explicitly-requested locale exists => warning 2729 * + A localized translation of an translateable="false" string => warning 2730 * - A localized string not provided in every locale used by the table 2731 */ 2732 status_t 2733 ResourceTable::validateLocalizations(void) 2734 { 2735 status_t err = NO_ERROR; 2736 const String8 defaultLocale; 2737 2738 // For all strings... 2739 for (const auto& nameIter : mLocalizations) { 2740 const std::map<String8, SourcePos>& configSrcMap = nameIter.second; 2741 2742 // Look for strings with no default localization 2743 if (configSrcMap.count(defaultLocale) == 0) { 2744 SourcePos().warning("string '%s' has no default translation.", 2745 String8(nameIter.first).string()); 2746 if (mBundle->getVerbose()) { 2747 for (const auto& locale : configSrcMap) { 2748 locale.second.printf("locale %s found", locale.first.string()); 2749 } 2750 } 2751 // !!! TODO: throw an error here in some circumstances 2752 } 2753 2754 // Check that all requested localizations are present for this string 2755 if (mBundle->getConfigurations().size() > 0 && mBundle->getRequireLocalization()) { 2756 const char* allConfigs = mBundle->getConfigurations().string(); 2757 const char* start = allConfigs; 2758 const char* comma; 2759 2760 std::set<String8> missingConfigs; 2761 AaptLocaleValue locale; 2762 do { 2763 String8 config; 2764 comma = strchr(start, ','); 2765 if (comma != NULL) { 2766 config.setTo(start, comma - start); 2767 start = comma + 1; 2768 } else { 2769 config.setTo(start); 2770 } 2771 2772 if (!locale.initFromFilterString(config)) { 2773 continue; 2774 } 2775 2776 // don't bother with the pseudolocale "en_XA" or "ar_XB" 2777 if (config != "en_XA" && config != "ar_XB") { 2778 if (configSrcMap.find(config) == configSrcMap.end()) { 2779 // okay, no specific localization found. it's possible that we are 2780 // requiring a specific regional localization [e.g. de_DE] but there is an 2781 // available string in the generic language localization [e.g. de]; 2782 // consider that string to have fulfilled the localization requirement. 2783 String8 region(config.string(), 2); 2784 if (configSrcMap.find(region) == configSrcMap.end() && 2785 configSrcMap.count(defaultLocale) == 0) { 2786 missingConfigs.insert(config); 2787 } 2788 } 2789 } 2790 } while (comma != NULL); 2791 2792 if (!missingConfigs.empty()) { 2793 String8 configStr; 2794 for (const auto& iter : missingConfigs) { 2795 configStr.appendFormat(" %s", iter.string()); 2796 } 2797 SourcePos().warning("string '%s' is missing %u required localizations:%s", 2798 String8(nameIter.first).string(), 2799 (unsigned int)missingConfigs.size(), 2800 configStr.string()); 2801 } 2802 } 2803 } 2804 2805 return err; 2806 } 2807 2808 status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, 2809 const sp<AaptFile>& dest, 2810 const bool isBase) 2811 { 2812 const ConfigDescription nullConfig; 2813 2814 const size_t N = mOrderedPackages.size(); 2815 size_t pi; 2816 2817 const static String16 mipmap16("mipmap"); 2818 2819 bool useUTF8 = !bundle->getUTF16StringsOption(); 2820 2821 // The libraries this table references. 2822 Vector<sp<Package> > libraryPackages; 2823 const ResTable& table = mAssets->getIncludedResources(); 2824 const size_t basePackageCount = table.getBasePackageCount(); 2825 for (size_t i = 0; i < basePackageCount; i++) { 2826 size_t packageId = table.getBasePackageId(i); 2827 String16 packageName(table.getBasePackageName(i)); 2828 if (packageId > 0x01 && packageId != 0x7f && 2829 packageName != String16("android")) { 2830 libraryPackages.add(sp<Package>(new Package(packageName, packageId))); 2831 } 2832 } 2833 2834 // Iterate through all data, collecting all values (strings, 2835 // references, etc). 2836 StringPool valueStrings(useUTF8); 2837 Vector<sp<Entry> > allEntries; 2838 for (pi=0; pi<N; pi++) { 2839 sp<Package> p = mOrderedPackages.itemAt(pi); 2840 if (p->getTypes().size() == 0) { 2841 continue; 2842 } 2843 2844 StringPool typeStrings(useUTF8); 2845 StringPool keyStrings(useUTF8); 2846 2847 ssize_t stringsAdded = 0; 2848 const size_t N = p->getOrderedTypes().size(); 2849 for (size_t ti=0; ti<N; ti++) { 2850 sp<Type> t = p->getOrderedTypes().itemAt(ti); 2851 if (t == NULL) { 2852 typeStrings.add(String16("<empty>"), false); 2853 stringsAdded++; 2854 continue; 2855 } 2856 2857 while (stringsAdded < t->getIndex() - 1) { 2858 typeStrings.add(String16("<empty>"), false); 2859 stringsAdded++; 2860 } 2861 2862 const String16 typeName(t->getName()); 2863 typeStrings.add(typeName, false); 2864 stringsAdded++; 2865 2866 // This is a hack to tweak the sorting order of the final strings, 2867 // to put stuff that is generally not language-specific first. 2868 String8 configTypeName(typeName); 2869 if (configTypeName == "drawable" || configTypeName == "layout" 2870 || configTypeName == "color" || configTypeName == "anim" 2871 || configTypeName == "interpolator" || configTypeName == "animator" 2872 || configTypeName == "xml" || configTypeName == "menu" 2873 || configTypeName == "mipmap" || configTypeName == "raw") { 2874 configTypeName = "1complex"; 2875 } else { 2876 configTypeName = "2value"; 2877 } 2878 2879 // mipmaps don't get filtered, so they will 2880 // allways end up in the base. Make sure they 2881 // don't end up in a split. 2882 if (typeName == mipmap16 && !isBase) { 2883 continue; 2884 } 2885 2886 const bool filterable = (typeName != mipmap16); 2887 2888 const size_t N = t->getOrderedConfigs().size(); 2889 for (size_t ci=0; ci<N; ci++) { 2890 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci); 2891 if (c == NULL) { 2892 continue; 2893 } 2894 const size_t N = c->getEntries().size(); 2895 for (size_t ei=0; ei<N; ei++) { 2896 ConfigDescription config = c->getEntries().keyAt(ei); 2897 if (filterable && !filter->match(config)) { 2898 continue; 2899 } 2900 sp<Entry> e = c->getEntries().valueAt(ei); 2901 if (e == NULL) { 2902 continue; 2903 } 2904 e->setNameIndex(keyStrings.add(e->getName(), true)); 2905 2906 // If this entry has no values for other configs, 2907 // and is the default config, then it is special. Otherwise 2908 // we want to add it with the config info. 2909 ConfigDescription* valueConfig = NULL; 2910 if (N != 1 || config == nullConfig) { 2911 valueConfig = &config; 2912 } 2913 2914 status_t err = e->prepareFlatten(&valueStrings, this, 2915 &configTypeName, &config); 2916 if (err != NO_ERROR) { 2917 return err; 2918 } 2919 allEntries.add(e); 2920 } 2921 } 2922 } 2923 2924 p->setTypeStrings(typeStrings.createStringBlock()); 2925 p->setKeyStrings(keyStrings.createStringBlock()); 2926 } 2927 2928 if (bundle->getOutputAPKFile() != NULL) { 2929 // Now we want to sort the value strings for better locality. This will 2930 // cause the positions of the strings to change, so we need to go back 2931 // through out resource entries and update them accordingly. Only need 2932 // to do this if actually writing the output file. 2933 valueStrings.sortByConfig(); 2934 for (pi=0; pi<allEntries.size(); pi++) { 2935 allEntries[pi]->remapStringValue(&valueStrings); 2936 } 2937 } 2938 2939 ssize_t strAmt = 0; 2940 2941 // Now build the array of package chunks. 2942 Vector<sp<AaptFile> > flatPackages; 2943 for (pi=0; pi<N; pi++) { 2944 sp<Package> p = mOrderedPackages.itemAt(pi); 2945 if (p->getTypes().size() == 0) { 2946 // Empty, skip! 2947 continue; 2948 } 2949 2950 const size_t N = p->getTypeStrings().size(); 2951 2952 const size_t baseSize = sizeof(ResTable_package); 2953 2954 // Start the package data. 2955 sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8()); 2956 ResTable_package* header = (ResTable_package*)data->editData(baseSize); 2957 if (header == NULL) { 2958 fprintf(stderr, "ERROR: out of memory creating ResTable_package\n"); 2959 return NO_MEMORY; 2960 } 2961 memset(header, 0, sizeof(*header)); 2962 header->header.type = htods(RES_TABLE_PACKAGE_TYPE); 2963 header->header.headerSize = htods(sizeof(*header)); 2964 header->id = htodl(static_cast<uint32_t>(p->getAssignedId())); 2965 strcpy16_htod(header->name, p->getName().string()); 2966 2967 // Write the string blocks. 2968 const size_t typeStringsStart = data->getSize(); 2969 sp<AaptFile> strFile = p->getTypeStringsData(); 2970 ssize_t amt = data->writeData(strFile->getData(), strFile->getSize()); 2971 if (kPrintStringMetrics) { 2972 fprintf(stderr, "**** type strings: %zd\n", SSIZE(amt)); 2973 } 2974 strAmt += amt; 2975 if (amt < 0) { 2976 return amt; 2977 } 2978 const size_t keyStringsStart = data->getSize(); 2979 strFile = p->getKeyStringsData(); 2980 amt = data->writeData(strFile->getData(), strFile->getSize()); 2981 if (kPrintStringMetrics) { 2982 fprintf(stderr, "**** key strings: %zd\n", SSIZE(amt)); 2983 } 2984 strAmt += amt; 2985 if (amt < 0) { 2986 return amt; 2987 } 2988 2989 if (isBase) { 2990 status_t err = flattenLibraryTable(data, libraryPackages); 2991 if (err != NO_ERROR) { 2992 fprintf(stderr, "ERROR: failed to write library table\n"); 2993 return err; 2994 } 2995 } 2996 2997 // Build the type chunks inside of this package. 2998 for (size_t ti=0; ti<N; ti++) { 2999 // Retrieve them in the same order as the type string block. 3000 size_t len; 3001 String16 typeName(p->getTypeStrings().stringAt(ti, &len)); 3002 sp<Type> t = p->getTypes().valueFor(typeName); 3003 LOG_ALWAYS_FATAL_IF(t == NULL && typeName != String16("<empty>"), 3004 "Type name %s not found", 3005 String8(typeName).string()); 3006 if (t == NULL) { 3007 continue; 3008 } 3009 const bool filterable = (typeName != mipmap16); 3010 const bool skipEntireType = (typeName == mipmap16 && !isBase); 3011 3012 const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0; 3013 3014 // Until a non-NO_ENTRY value has been written for a resource, 3015 // that resource is invalid; validResources[i] represents 3016 // the item at t->getOrderedConfigs().itemAt(i). 3017 Vector<bool> validResources; 3018 validResources.insertAt(false, 0, N); 3019 3020 // First write the typeSpec chunk, containing information about 3021 // each resource entry in this type. 3022 { 3023 const size_t typeSpecSize = sizeof(ResTable_typeSpec) + sizeof(uint32_t)*N; 3024 const size_t typeSpecStart = data->getSize(); 3025 ResTable_typeSpec* tsHeader = (ResTable_typeSpec*) 3026 (((uint8_t*)data->editData(typeSpecStart+typeSpecSize)) + typeSpecStart); 3027 if (tsHeader == NULL) { 3028 fprintf(stderr, "ERROR: out of memory creating ResTable_typeSpec\n"); 3029 return NO_MEMORY; 3030 } 3031 memset(tsHeader, 0, sizeof(*tsHeader)); 3032 tsHeader->header.type = htods(RES_TABLE_TYPE_SPEC_TYPE); 3033 tsHeader->header.headerSize = htods(sizeof(*tsHeader)); 3034 tsHeader->header.size = htodl(typeSpecSize); 3035 tsHeader->id = ti+1; 3036 tsHeader->entryCount = htodl(N); 3037 3038 uint32_t* typeSpecFlags = (uint32_t*) 3039 (((uint8_t*)data->editData()) 3040 + typeSpecStart + sizeof(ResTable_typeSpec)); 3041 memset(typeSpecFlags, 0, sizeof(uint32_t)*N); 3042 3043 for (size_t ei=0; ei<N; ei++) { 3044 sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei); 3045 if (cl == NULL) { 3046 continue; 3047 } 3048 3049 if (cl->getPublic()) { 3050 typeSpecFlags[ei] |= htodl(ResTable_typeSpec::SPEC_PUBLIC); 3051 } 3052 3053 if (skipEntireType) { 3054 continue; 3055 } 3056 3057 const size_t CN = cl->getEntries().size(); 3058 for (size_t ci=0; ci<CN; ci++) { 3059 if (filterable && !filter->match(cl->getEntries().keyAt(ci))) { 3060 continue; 3061 } 3062 for (size_t cj=ci+1; cj<CN; cj++) { 3063 if (filterable && !filter->match(cl->getEntries().keyAt(cj))) { 3064 continue; 3065 } 3066 typeSpecFlags[ei] |= htodl( 3067 cl->getEntries().keyAt(ci).diff(cl->getEntries().keyAt(cj))); 3068 } 3069 } 3070 } 3071 } 3072 3073 if (skipEntireType) { 3074 continue; 3075 } 3076 3077 // We need to write one type chunk for each configuration for 3078 // which we have entries in this type. 3079 SortedVector<ConfigDescription> uniqueConfigs; 3080 if (t != NULL) { 3081 uniqueConfigs = t->getUniqueConfigs(); 3082 } 3083 3084 const size_t typeSize = sizeof(ResTable_type) + sizeof(uint32_t)*N; 3085 3086 const size_t NC = uniqueConfigs.size(); 3087 for (size_t ci=0; ci<NC; ci++) { 3088 const ConfigDescription& config = uniqueConfigs[ci]; 3089 3090 if (kIsDebug) { 3091 printf("Writing config %zu config: imsi:%d/%d lang:%c%c cnt:%c%c " 3092 "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d " 3093 "sw%ddp w%ddp h%ddp layout:%d\n", 3094 ti + 1, 3095 config.mcc, config.mnc, 3096 config.language[0] ? config.language[0] : '-', 3097 config.language[1] ? config.language[1] : '-', 3098 config.country[0] ? config.country[0] : '-', 3099 config.country[1] ? config.country[1] : '-', 3100 config.orientation, 3101 config.uiMode, 3102 config.touchscreen, 3103 config.density, 3104 config.keyboard, 3105 config.inputFlags, 3106 config.navigation, 3107 config.screenWidth, 3108 config.screenHeight, 3109 config.smallestScreenWidthDp, 3110 config.screenWidthDp, 3111 config.screenHeightDp, 3112 config.screenLayout); 3113 } 3114 3115 if (filterable && !filter->match(config)) { 3116 continue; 3117 } 3118 3119 const size_t typeStart = data->getSize(); 3120 3121 ResTable_type* tHeader = (ResTable_type*) 3122 (((uint8_t*)data->editData(typeStart+typeSize)) + typeStart); 3123 if (tHeader == NULL) { 3124 fprintf(stderr, "ERROR: out of memory creating ResTable_type\n"); 3125 return NO_MEMORY; 3126 } 3127 3128 memset(tHeader, 0, sizeof(*tHeader)); 3129 tHeader->header.type = htods(RES_TABLE_TYPE_TYPE); 3130 tHeader->header.headerSize = htods(sizeof(*tHeader)); 3131 tHeader->id = ti+1; 3132 tHeader->entryCount = htodl(N); 3133 tHeader->entriesStart = htodl(typeSize); 3134 tHeader->config = config; 3135 if (kIsDebug) { 3136 printf("Writing type %zu config: imsi:%d/%d lang:%c%c cnt:%c%c " 3137 "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d " 3138 "sw%ddp w%ddp h%ddp layout:%d\n", 3139 ti + 1, 3140 tHeader->config.mcc, tHeader->config.mnc, 3141 tHeader->config.language[0] ? tHeader->config.language[0] : '-', 3142 tHeader->config.language[1] ? tHeader->config.language[1] : '-', 3143 tHeader->config.country[0] ? tHeader->config.country[0] : '-', 3144 tHeader->config.country[1] ? tHeader->config.country[1] : '-', 3145 tHeader->config.orientation, 3146 tHeader->config.uiMode, 3147 tHeader->config.touchscreen, 3148 tHeader->config.density, 3149 tHeader->config.keyboard, 3150 tHeader->config.inputFlags, 3151 tHeader->config.navigation, 3152 tHeader->config.screenWidth, 3153 tHeader->config.screenHeight, 3154 tHeader->config.smallestScreenWidthDp, 3155 tHeader->config.screenWidthDp, 3156 tHeader->config.screenHeightDp, 3157 tHeader->config.screenLayout); 3158 } 3159 tHeader->config.swapHtoD(); 3160 3161 // Build the entries inside of this type. 3162 for (size_t ei=0; ei<N; ei++) { 3163 sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei); 3164 sp<Entry> e = NULL; 3165 if (cl != NULL) { 3166 e = cl->getEntries().valueFor(config); 3167 } 3168 3169 // Set the offset for this entry in its type. 3170 uint32_t* index = (uint32_t*) 3171 (((uint8_t*)data->editData()) 3172 + typeStart + sizeof(ResTable_type)); 3173 if (e != NULL) { 3174 index[ei] = htodl(data->getSize()-typeStart-typeSize); 3175 3176 // Create the entry. 3177 ssize_t amt = e->flatten(bundle, data, cl->getPublic()); 3178 if (amt < 0) { 3179 return amt; 3180 } 3181 validResources.editItemAt(ei) = true; 3182 } else { 3183 index[ei] = htodl(ResTable_type::NO_ENTRY); 3184 } 3185 } 3186 3187 // Fill in the rest of the type information. 3188 tHeader = (ResTable_type*) 3189 (((uint8_t*)data->editData()) + typeStart); 3190 tHeader->header.size = htodl(data->getSize()-typeStart); 3191 } 3192 3193 // If we're building splits, then each invocation of the flattening 3194 // step will have 'missing' entries. Don't warn/error for this case. 3195 if (bundle->getSplitConfigurations().isEmpty()) { 3196 bool missing_entry = false; 3197 const char* log_prefix = bundle->getErrorOnMissingConfigEntry() ? 3198 "error" : "warning"; 3199 for (size_t i = 0; i < N; ++i) { 3200 if (!validResources[i]) { 3201 sp<ConfigList> c = t->getOrderedConfigs().itemAt(i); 3202 if (c != NULL) { 3203 fprintf(stderr, "%s: no entries written for %s/%s (0x%08zx)\n", log_prefix, 3204 String8(typeName).string(), String8(c->getName()).string(), 3205 Res_MAKEID(p->getAssignedId() - 1, ti, i)); 3206 } 3207 missing_entry = true; 3208 } 3209 } 3210 if (bundle->getErrorOnMissingConfigEntry() && missing_entry) { 3211 fprintf(stderr, "Error: Missing entries, quit!\n"); 3212 return NOT_ENOUGH_DATA; 3213 } 3214 } 3215 } 3216 3217 // Fill in the rest of the package information. 3218 header = (ResTable_package*)data->editData(); 3219 header->header.size = htodl(data->getSize()); 3220 header->typeStrings = htodl(typeStringsStart); 3221 header->lastPublicType = htodl(p->getTypeStrings().size()); 3222 header->keyStrings = htodl(keyStringsStart); 3223 header->lastPublicKey = htodl(p->getKeyStrings().size()); 3224 3225 flatPackages.add(data); 3226 } 3227 3228 // And now write out the final chunks. 3229 const size_t dataStart = dest->getSize(); 3230 3231 { 3232 // blah 3233 ResTable_header header; 3234 memset(&header, 0, sizeof(header)); 3235 header.header.type = htods(RES_TABLE_TYPE); 3236 header.header.headerSize = htods(sizeof(header)); 3237 header.packageCount = htodl(flatPackages.size()); 3238 status_t err = dest->writeData(&header, sizeof(header)); 3239 if (err != NO_ERROR) { 3240 fprintf(stderr, "ERROR: out of memory creating ResTable_header\n"); 3241 return err; 3242 } 3243 } 3244 3245 ssize_t strStart = dest->getSize(); 3246 status_t err = valueStrings.writeStringBlock(dest); 3247 if (err != NO_ERROR) { 3248 return err; 3249 } 3250 3251 ssize_t amt = (dest->getSize()-strStart); 3252 strAmt += amt; 3253 if (kPrintStringMetrics) { 3254 fprintf(stderr, "**** value strings: %zd\n", SSIZE(amt)); 3255 fprintf(stderr, "**** total strings: %zd\n", SSIZE(strAmt)); 3256 } 3257 3258 for (pi=0; pi<flatPackages.size(); pi++) { 3259 err = dest->writeData(flatPackages[pi]->getData(), 3260 flatPackages[pi]->getSize()); 3261 if (err != NO_ERROR) { 3262 fprintf(stderr, "ERROR: out of memory creating package chunk for ResTable_header\n"); 3263 return err; 3264 } 3265 } 3266 3267 ResTable_header* header = (ResTable_header*) 3268 (((uint8_t*)dest->getData()) + dataStart); 3269 header->header.size = htodl(dest->getSize() - dataStart); 3270 3271 if (kPrintStringMetrics) { 3272 fprintf(stderr, "**** total resource table size: %zu / %zu%% strings\n", 3273 dest->getSize(), (size_t)(strAmt*100)/dest->getSize()); 3274 } 3275 3276 return NO_ERROR; 3277 } 3278 3279 status_t ResourceTable::flattenLibraryTable(const sp<AaptFile>& dest, const Vector<sp<Package> >& libs) { 3280 // Write out the library table if necessary 3281 if (libs.size() > 0) { 3282 if (kIsDebug) { 3283 fprintf(stderr, "Writing library reference table\n"); 3284 } 3285 3286 const size_t libStart = dest->getSize(); 3287 const size_t count = libs.size(); 3288 ResTable_lib_header* libHeader = (ResTable_lib_header*) dest->editDataInRange( 3289 libStart, sizeof(ResTable_lib_header)); 3290 3291 memset(libHeader, 0, sizeof(*libHeader)); 3292 libHeader->header.type = htods(RES_TABLE_LIBRARY_TYPE); 3293 libHeader->header.headerSize = htods(sizeof(*libHeader)); 3294 libHeader->header.size = htodl(sizeof(*libHeader) + (sizeof(ResTable_lib_entry) * count)); 3295 libHeader->count = htodl(count); 3296 3297 // Write the library entries 3298 for (size_t i = 0; i < count; i++) { 3299 const size_t entryStart = dest->getSize(); 3300 sp<Package> libPackage = libs[i]; 3301 if (kIsDebug) { 3302 fprintf(stderr, " Entry %s -> 0x%02x\n", 3303 String8(libPackage->getName()).string(), 3304 (uint8_t)libPackage->getAssignedId()); 3305 } 3306 3307 ResTable_lib_entry* entry = (ResTable_lib_entry*) dest->editDataInRange( 3308 entryStart, sizeof(ResTable_lib_entry)); 3309 memset(entry, 0, sizeof(*entry)); 3310 entry->packageId = htodl(libPackage->getAssignedId()); 3311 strcpy16_htod(entry->packageName, libPackage->getName().string()); 3312 } 3313 } 3314 return NO_ERROR; 3315 } 3316 3317 void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp) 3318 { 3319 fprintf(fp, 3320 "<!-- This file contains <public> resource definitions for all\n" 3321 " resources that were generated from the source data. -->\n" 3322 "\n" 3323 "<resources>\n"); 3324 3325 writePublicDefinitions(package, fp, true); 3326 writePublicDefinitions(package, fp, false); 3327 3328 fprintf(fp, 3329 "\n" 3330 "</resources>\n"); 3331 } 3332 3333 void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp, bool pub) 3334 { 3335 bool didHeader = false; 3336 3337 sp<Package> pkg = mPackages.valueFor(package); 3338 if (pkg != NULL) { 3339 const size_t NT = pkg->getOrderedTypes().size(); 3340 for (size_t i=0; i<NT; i++) { 3341 sp<Type> t = pkg->getOrderedTypes().itemAt(i); 3342 if (t == NULL) { 3343 continue; 3344 } 3345 3346 bool didType = false; 3347 3348 const size_t NC = t->getOrderedConfigs().size(); 3349 for (size_t j=0; j<NC; j++) { 3350 sp<ConfigList> c = t->getOrderedConfigs().itemAt(j); 3351 if (c == NULL) { 3352 continue; 3353 } 3354 3355 if (c->getPublic() != pub) { 3356 continue; 3357 } 3358 3359 if (!didType) { 3360 fprintf(fp, "\n"); 3361 didType = true; 3362 } 3363 if (!didHeader) { 3364 if (pub) { 3365 fprintf(fp," <!-- PUBLIC SECTION. These resources have been declared public.\n"); 3366 fprintf(fp," Changes to these definitions will break binary compatibility. -->\n\n"); 3367 } else { 3368 fprintf(fp," <!-- PRIVATE SECTION. These resources have not been declared public.\n"); 3369 fprintf(fp," You can make them public my moving these lines into a file in res/values. -->\n\n"); 3370 } 3371 didHeader = true; 3372 } 3373 if (!pub) { 3374 const size_t NE = c->getEntries().size(); 3375 for (size_t k=0; k<NE; k++) { 3376 const SourcePos& pos = c->getEntries().valueAt(k)->getPos(); 3377 if (pos.file != "") { 3378 fprintf(fp," <!-- Declared at %s:%d -->\n", 3379 pos.file.string(), pos.line); 3380 } 3381 } 3382 } 3383 fprintf(fp, " <public type=\"%s\" name=\"%s\" id=\"0x%08x\" />\n", 3384 String8(t->getName()).string(), 3385 String8(c->getName()).string(), 3386 getResId(pkg, t, c->getEntryIndex())); 3387 } 3388 } 3389 } 3390 } 3391 3392 ResourceTable::Item::Item(const SourcePos& _sourcePos, 3393 bool _isId, 3394 const String16& _value, 3395 const Vector<StringPool::entry_style_span>* _style, 3396 int32_t _format) 3397 : sourcePos(_sourcePos) 3398 , isId(_isId) 3399 , value(_value) 3400 , format(_format) 3401 , bagKeyId(0) 3402 , evaluating(false) 3403 { 3404 if (_style) { 3405 style = *_style; 3406 } 3407 } 3408 3409 ResourceTable::Entry::Entry(const Entry& entry) 3410 : RefBase() 3411 , mName(entry.mName) 3412 , mParent(entry.mParent) 3413 , mType(entry.mType) 3414 , mItem(entry.mItem) 3415 , mItemFormat(entry.mItemFormat) 3416 , mBag(entry.mBag) 3417 , mNameIndex(entry.mNameIndex) 3418 , mParentId(entry.mParentId) 3419 , mPos(entry.mPos) {} 3420 3421 ResourceTable::Entry& ResourceTable::Entry::operator=(const Entry& entry) { 3422 mName = entry.mName; 3423 mParent = entry.mParent; 3424 mType = entry.mType; 3425 mItem = entry.mItem; 3426 mItemFormat = entry.mItemFormat; 3427 mBag = entry.mBag; 3428 mNameIndex = entry.mNameIndex; 3429 mParentId = entry.mParentId; 3430 mPos = entry.mPos; 3431 return *this; 3432 } 3433 3434 status_t ResourceTable::Entry::makeItABag(const SourcePos& sourcePos) 3435 { 3436 if (mType == TYPE_BAG) { 3437 return NO_ERROR; 3438 } 3439 if (mType == TYPE_UNKNOWN) { 3440 mType = TYPE_BAG; 3441 return NO_ERROR; 3442 } 3443 sourcePos.error("Resource entry %s is already defined as a single item.\n" 3444 "%s:%d: Originally defined here.\n", 3445 String8(mName).string(), 3446 mItem.sourcePos.file.string(), mItem.sourcePos.line); 3447 return UNKNOWN_ERROR; 3448 } 3449 3450 status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos, 3451 const String16& value, 3452 const Vector<StringPool::entry_style_span>* style, 3453 int32_t format, 3454 const bool overwrite) 3455 { 3456 Item item(sourcePos, false, value, style); 3457 3458 if (mType == TYPE_BAG) { 3459 if (mBag.size() == 0) { 3460 sourcePos.error("Resource entry %s is already defined as a bag.", 3461 String8(mName).string()); 3462 } else { 3463 const Item& item(mBag.valueAt(0)); 3464 sourcePos.error("Resource entry %s is already defined as a bag.\n" 3465 "%s:%d: Originally defined here.\n", 3466 String8(mName).string(), 3467 item.sourcePos.file.string(), item.sourcePos.line); 3468 } 3469 return UNKNOWN_ERROR; 3470 } 3471 if ( (mType != TYPE_UNKNOWN) && (overwrite == false) ) { 3472 sourcePos.error("Resource entry %s is already defined.\n" 3473 "%s:%d: Originally defined here.\n", 3474 String8(mName).string(), 3475 mItem.sourcePos.file.string(), mItem.sourcePos.line); 3476 return UNKNOWN_ERROR; 3477 } 3478 3479 mType = TYPE_ITEM; 3480 mItem = item; 3481 mItemFormat = format; 3482 return NO_ERROR; 3483 } 3484 3485 status_t ResourceTable::Entry::addToBag(const SourcePos& sourcePos, 3486 const String16& key, const String16& value, 3487 const Vector<StringPool::entry_style_span>* style, 3488 bool replace, bool isId, int32_t format) 3489 { 3490 status_t err = makeItABag(sourcePos); 3491 if (err != NO_ERROR) { 3492 return err; 3493 } 3494 3495 Item item(sourcePos, isId, value, style, format); 3496 3497 // XXX NOTE: there is an error if you try to have a bag with two keys, 3498 // one an attr and one an id, with the same name. Not something we 3499 // currently ever have to worry about. 3500 ssize_t origKey = mBag.indexOfKey(key); 3501 if (origKey >= 0) { 3502 if (!replace) { 3503 const Item& item(mBag.valueAt(origKey)); 3504 sourcePos.error("Resource entry %s already has bag item %s.\n" 3505 "%s:%d: Originally defined here.\n", 3506 String8(mName).string(), String8(key).string(), 3507 item.sourcePos.file.string(), item.sourcePos.line); 3508 return UNKNOWN_ERROR; 3509 } 3510 //printf("Replacing %s with %s\n", 3511 // String8(mBag.valueFor(key).value).string(), String8(value).string()); 3512 mBag.replaceValueFor(key, item); 3513 } 3514 3515 mBag.add(key, item); 3516 return NO_ERROR; 3517 } 3518 3519 status_t ResourceTable::Entry::removeFromBag(const String16& key) { 3520 if (mType != Entry::TYPE_BAG) { 3521 return NO_ERROR; 3522 } 3523 3524 if (mBag.removeItem(key) >= 0) { 3525 return NO_ERROR; 3526 } 3527 return UNKNOWN_ERROR; 3528 } 3529 3530 status_t ResourceTable::Entry::emptyBag(const SourcePos& sourcePos) 3531 { 3532 status_t err = makeItABag(sourcePos); 3533 if (err != NO_ERROR) { 3534 return err; 3535 } 3536 3537 mBag.clear(); 3538 return NO_ERROR; 3539 } 3540 3541 status_t ResourceTable::Entry::generateAttributes(ResourceTable* table, 3542 const String16& package) 3543 { 3544 const String16 attr16("attr"); 3545 const String16 id16("id"); 3546 const size_t N = mBag.size(); 3547 for (size_t i=0; i<N; i++) { 3548 const String16& key = mBag.keyAt(i); 3549 const Item& it = mBag.valueAt(i); 3550 if (it.isId) { 3551 if (!table->hasBagOrEntry(key, &id16, &package)) { 3552 String16 value("false"); 3553 if (kIsDebug) { 3554 fprintf(stderr, "Generating %s:id/%s\n", 3555 String8(package).string(), 3556 String8(key).string()); 3557 } 3558 status_t err = table->addEntry(SourcePos(String8("<generated>"), 0), package, 3559 id16, key, value); 3560 if (err != NO_ERROR) { 3561 return err; 3562 } 3563 } 3564 } else if (!table->hasBagOrEntry(key, &attr16, &package)) { 3565 3566 #if 1 3567 // fprintf(stderr, "ERROR: Bag attribute '%s' has not been defined.\n", 3568 // String8(key).string()); 3569 // const Item& item(mBag.valueAt(i)); 3570 // fprintf(stderr, "Referenced from file %s line %d\n", 3571 // item.sourcePos.file.string(), item.sourcePos.line); 3572 // return UNKNOWN_ERROR; 3573 #else 3574 char numberStr[16]; 3575 sprintf(numberStr, "%d", ResTable_map::TYPE_ANY); 3576 status_t err = table->addBag(SourcePos("<generated>", 0), package, 3577 attr16, key, String16(""), 3578 String16("^type"), 3579 String16(numberStr), NULL, NULL); 3580 if (err != NO_ERROR) { 3581 return err; 3582 } 3583 #endif 3584 } 3585 } 3586 return NO_ERROR; 3587 } 3588 3589 status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table, 3590 const String16& /* package */) 3591 { 3592 bool hasErrors = false; 3593 3594 if (mType == TYPE_BAG) { 3595 const char* errorMsg; 3596 const String16 style16("style"); 3597 const String16 attr16("attr"); 3598 const String16 id16("id"); 3599 mParentId = 0; 3600 if (mParent.size() > 0) { 3601 mParentId = table->getResId(mParent, &style16, NULL, &errorMsg); 3602 if (mParentId == 0) { 3603 mPos.error("Error retrieving parent for item: %s '%s'.\n", 3604 errorMsg, String8(mParent).string()); 3605 hasErrors = true; 3606 } 3607 } 3608 const size_t N = mBag.size(); 3609 for (size_t i=0; i<N; i++) { 3610 const String16& key = mBag.keyAt(i); 3611 Item& it = mBag.editValueAt(i); 3612 it.bagKeyId = table->getResId(key, 3613 it.isId ? &id16 : &attr16, NULL, &errorMsg); 3614 //printf("Bag key of %s: #%08x\n", String8(key).string(), it.bagKeyId); 3615 if (it.bagKeyId == 0) { 3616 it.sourcePos.error("Error: %s: %s '%s'.\n", errorMsg, 3617 String8(it.isId ? id16 : attr16).string(), 3618 String8(key).string()); 3619 hasErrors = true; 3620 } 3621 } 3622 } 3623 return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR; 3624 } 3625 3626 status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table, 3627 const String8* configTypeName, const ConfigDescription* config) 3628 { 3629 if (mType == TYPE_ITEM) { 3630 Item& it = mItem; 3631 AccessorCookie ac(it.sourcePos, String8(mName), String8(it.value)); 3632 if (!table->stringToValue(&it.parsedValue, strings, 3633 it.value, false, true, 0, 3634 &it.style, NULL, &ac, mItemFormat, 3635 configTypeName, config)) { 3636 return UNKNOWN_ERROR; 3637 } 3638 } else if (mType == TYPE_BAG) { 3639 const size_t N = mBag.size(); 3640 for (size_t i=0; i<N; i++) { 3641 const String16& key = mBag.keyAt(i); 3642 Item& it = mBag.editValueAt(i); 3643 AccessorCookie ac(it.sourcePos, String8(key), String8(it.value)); 3644 if (!table->stringToValue(&it.parsedValue, strings, 3645 it.value, false, true, it.bagKeyId, 3646 &it.style, NULL, &ac, it.format, 3647 configTypeName, config)) { 3648 return UNKNOWN_ERROR; 3649 } 3650 } 3651 } else { 3652 mPos.error("Error: entry %s is not a single item or a bag.\n", 3653 String8(mName).string()); 3654 return UNKNOWN_ERROR; 3655 } 3656 return NO_ERROR; 3657 } 3658 3659 status_t ResourceTable::Entry::remapStringValue(StringPool* strings) 3660 { 3661 if (mType == TYPE_ITEM) { 3662 Item& it = mItem; 3663 if (it.parsedValue.dataType == Res_value::TYPE_STRING) { 3664 it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data); 3665 } 3666 } else if (mType == TYPE_BAG) { 3667 const size_t N = mBag.size(); 3668 for (size_t i=0; i<N; i++) { 3669 Item& it = mBag.editValueAt(i); 3670 if (it.parsedValue.dataType == Res_value::TYPE_STRING) { 3671 it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data); 3672 } 3673 } 3674 } else { 3675 mPos.error("Error: entry %s is not a single item or a bag.\n", 3676 String8(mName).string()); 3677 return UNKNOWN_ERROR; 3678 } 3679 return NO_ERROR; 3680 } 3681 3682 ssize_t ResourceTable::Entry::flatten(Bundle* /* bundle */, const sp<AaptFile>& data, bool isPublic) 3683 { 3684 size_t amt = 0; 3685 ResTable_entry header; 3686 memset(&header, 0, sizeof(header)); 3687 header.size = htods(sizeof(header)); 3688 const type ty = mType; 3689 if (ty == TYPE_BAG) { 3690 header.flags |= htods(header.FLAG_COMPLEX); 3691 } 3692 if (isPublic) { 3693 header.flags |= htods(header.FLAG_PUBLIC); 3694 } 3695 header.key.index = htodl(mNameIndex); 3696 if (ty != TYPE_BAG) { 3697 status_t err = data->writeData(&header, sizeof(header)); 3698 if (err != NO_ERROR) { 3699 fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n"); 3700 return err; 3701 } 3702 3703 const Item& it = mItem; 3704 Res_value par; 3705 memset(&par, 0, sizeof(par)); 3706 par.size = htods(it.parsedValue.size); 3707 par.dataType = it.parsedValue.dataType; 3708 par.res0 = it.parsedValue.res0; 3709 par.data = htodl(it.parsedValue.data); 3710 #if 0 3711 printf("Writing item (%s): type=%d, data=0x%x, res0=0x%x\n", 3712 String8(mName).string(), it.parsedValue.dataType, 3713 it.parsedValue.data, par.res0); 3714 #endif 3715 err = data->writeData(&par, it.parsedValue.size); 3716 if (err != NO_ERROR) { 3717 fprintf(stderr, "ERROR: out of memory creating Res_value\n"); 3718 return err; 3719 } 3720 amt += it.parsedValue.size; 3721 } else { 3722 size_t N = mBag.size(); 3723 size_t i; 3724 // Create correct ordering of items. 3725 KeyedVector<uint32_t, const Item*> items; 3726 for (i=0; i<N; i++) { 3727 const Item& it = mBag.valueAt(i); 3728 items.add(it.bagKeyId, &it); 3729 } 3730 N = items.size(); 3731 3732 ResTable_map_entry mapHeader; 3733 memcpy(&mapHeader, &header, sizeof(header)); 3734 mapHeader.size = htods(sizeof(mapHeader)); 3735 mapHeader.parent.ident = htodl(mParentId); 3736 mapHeader.count = htodl(N); 3737 status_t err = data->writeData(&mapHeader, sizeof(mapHeader)); 3738 if (err != NO_ERROR) { 3739 fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n"); 3740 return err; 3741 } 3742 3743 for (i=0; i<N; i++) { 3744 const Item& it = *items.valueAt(i); 3745 ResTable_map map; 3746 map.name.ident = htodl(it.bagKeyId); 3747 map.value.size = htods(it.parsedValue.size); 3748 map.value.dataType = it.parsedValue.dataType; 3749 map.value.res0 = it.parsedValue.res0; 3750 map.value.data = htodl(it.parsedValue.data); 3751 err = data->writeData(&map, sizeof(map)); 3752 if (err != NO_ERROR) { 3753 fprintf(stderr, "ERROR: out of memory creating Res_value\n"); 3754 return err; 3755 } 3756 amt += sizeof(map); 3757 } 3758 } 3759 return amt; 3760 } 3761 3762 void ResourceTable::ConfigList::appendComment(const String16& comment, 3763 bool onlyIfEmpty) 3764 { 3765 if (comment.size() <= 0) { 3766 return; 3767 } 3768 if (onlyIfEmpty && mComment.size() > 0) { 3769 return; 3770 } 3771 if (mComment.size() > 0) { 3772 mComment.append(String16("\n")); 3773 } 3774 mComment.append(comment); 3775 } 3776 3777 void ResourceTable::ConfigList::appendTypeComment(const String16& comment) 3778 { 3779 if (comment.size() <= 0) { 3780 return; 3781 } 3782 if (mTypeComment.size() > 0) { 3783 mTypeComment.append(String16("\n")); 3784 } 3785 mTypeComment.append(comment); 3786 } 3787 3788 status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos, 3789 const String16& name, 3790 const uint32_t ident) 3791 { 3792 #if 0 3793 int32_t entryIdx = Res_GETENTRY(ident); 3794 if (entryIdx < 0) { 3795 sourcePos.error("Public resource %s/%s has an invalid 0 identifier (0x%08x).\n", 3796 String8(mName).string(), String8(name).string(), ident); 3797 return UNKNOWN_ERROR; 3798 } 3799 #endif 3800 3801 int32_t typeIdx = Res_GETTYPE(ident); 3802 if (typeIdx >= 0) { 3803 typeIdx++; 3804 if (mPublicIndex > 0 && mPublicIndex != typeIdx) { 3805 sourcePos.error("Public resource %s/%s has conflicting type codes for its" 3806 " public identifiers (0x%x vs 0x%x).\n", 3807 String8(mName).string(), String8(name).string(), 3808 mPublicIndex, typeIdx); 3809 return UNKNOWN_ERROR; 3810 } 3811 mPublicIndex = typeIdx; 3812 } 3813 3814 if (mFirstPublicSourcePos == NULL) { 3815 mFirstPublicSourcePos = new SourcePos(sourcePos); 3816 } 3817 3818 if (mPublic.indexOfKey(name) < 0) { 3819 mPublic.add(name, Public(sourcePos, String16(), ident)); 3820 } else { 3821 Public& p = mPublic.editValueFor(name); 3822 if (p.ident != ident) { 3823 sourcePos.error("Public resource %s/%s has conflicting public identifiers" 3824 " (0x%08x vs 0x%08x).\n" 3825 "%s:%d: Originally defined here.\n", 3826 String8(mName).string(), String8(name).string(), p.ident, ident, 3827 p.sourcePos.file.string(), p.sourcePos.line); 3828 return UNKNOWN_ERROR; 3829 } 3830 } 3831 3832 return NO_ERROR; 3833 } 3834 3835 void ResourceTable::Type::canAddEntry(const String16& name) 3836 { 3837 mCanAddEntries.add(name); 3838 } 3839 3840 sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry, 3841 const SourcePos& sourcePos, 3842 const ResTable_config* config, 3843 bool doSetIndex, 3844 bool overlay, 3845 bool autoAddOverlay) 3846 { 3847 int pos = -1; 3848 sp<ConfigList> c = mConfigs.valueFor(entry); 3849 if (c == NULL) { 3850 if (overlay && !autoAddOverlay && mCanAddEntries.indexOf(entry) < 0) { 3851 sourcePos.error("Resource at %s appears in overlay but not" 3852 " in the base package; use <add-resource> to add.\n", 3853 String8(entry).string()); 3854 return NULL; 3855 } 3856 c = new ConfigList(entry, sourcePos); 3857 mConfigs.add(entry, c); 3858 pos = (int)mOrderedConfigs.size(); 3859 mOrderedConfigs.add(c); 3860 if (doSetIndex) { 3861 c->setEntryIndex(pos); 3862 } 3863 } 3864 3865 ConfigDescription cdesc; 3866 if (config) cdesc = *config; 3867 3868 sp<Entry> e = c->getEntries().valueFor(cdesc); 3869 if (e == NULL) { 3870 if (kIsDebug) { 3871 if (config != NULL) { 3872 printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c " 3873 "orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d " 3874 "sw%ddp w%ddp h%ddp layout:%d\n", 3875 sourcePos.file.string(), sourcePos.line, 3876 config->mcc, config->mnc, 3877 config->language[0] ? config->language[0] : '-', 3878 config->language[1] ? config->language[1] : '-', 3879 config->country[0] ? config->country[0] : '-', 3880 config->country[1] ? config->country[1] : '-', 3881 config->orientation, 3882 config->touchscreen, 3883 config->density, 3884 config->keyboard, 3885 config->inputFlags, 3886 config->navigation, 3887 config->screenWidth, 3888 config->screenHeight, 3889 config->smallestScreenWidthDp, 3890 config->screenWidthDp, 3891 config->screenHeightDp, 3892 config->screenLayout); 3893 } else { 3894 printf("New entry at %s:%d: NULL config\n", 3895 sourcePos.file.string(), sourcePos.line); 3896 } 3897 } 3898 e = new Entry(entry, sourcePos); 3899 c->addEntry(cdesc, e); 3900 /* 3901 if (doSetIndex) { 3902 if (pos < 0) { 3903 for (pos=0; pos<(int)mOrderedConfigs.size(); pos++) { 3904 if (mOrderedConfigs[pos] == c) { 3905 break; 3906 } 3907 } 3908 if (pos >= (int)mOrderedConfigs.size()) { 3909 sourcePos.error("Internal error: config not found in mOrderedConfigs when adding entry"); 3910 return NULL; 3911 } 3912 } 3913 e->setEntryIndex(pos); 3914 } 3915 */ 3916 } 3917 3918 return e; 3919 } 3920 3921 sp<ResourceTable::ConfigList> ResourceTable::Type::removeEntry(const String16& entry) { 3922 ssize_t idx = mConfigs.indexOfKey(entry); 3923 if (idx < 0) { 3924 return NULL; 3925 } 3926 3927 sp<ConfigList> removed = mConfigs.valueAt(idx); 3928 mConfigs.removeItemsAt(idx); 3929 3930 Vector<sp<ConfigList> >::iterator iter = std::find( 3931 mOrderedConfigs.begin(), mOrderedConfigs.end(), removed); 3932 if (iter != mOrderedConfigs.end()) { 3933 mOrderedConfigs.erase(iter); 3934 } 3935 3936 mPublic.removeItem(entry); 3937 return removed; 3938 } 3939 3940 SortedVector<ConfigDescription> ResourceTable::Type::getUniqueConfigs() const { 3941 SortedVector<ConfigDescription> unique; 3942 const size_t entryCount = mOrderedConfigs.size(); 3943 for (size_t i = 0; i < entryCount; i++) { 3944 if (mOrderedConfigs[i] == NULL) { 3945 continue; 3946 } 3947 const DefaultKeyedVector<ConfigDescription, sp<Entry> >& configs = 3948 mOrderedConfigs[i]->getEntries(); 3949 const size_t configCount = configs.size(); 3950 for (size_t j = 0; j < configCount; j++) { 3951 unique.add(configs.keyAt(j)); 3952 } 3953 } 3954 return unique; 3955 } 3956 3957 status_t ResourceTable::Type::applyPublicEntryOrder() 3958 { 3959 size_t N = mOrderedConfigs.size(); 3960 Vector<sp<ConfigList> > origOrder(mOrderedConfigs); 3961 bool hasError = false; 3962 3963 size_t i; 3964 for (i=0; i<N; i++) { 3965 mOrderedConfigs.replaceAt(NULL, i); 3966 } 3967 3968 const size_t NP = mPublic.size(); 3969 //printf("Ordering %d configs from %d public defs\n", N, NP); 3970 size_t j; 3971 for (j=0; j<NP; j++) { 3972 const String16& name = mPublic.keyAt(j); 3973 const Public& p = mPublic.valueAt(j); 3974 int32_t idx = Res_GETENTRY(p.ident); 3975 //printf("Looking for entry \"%s\"/\"%s\" (0x%08x) in %d...\n", 3976 // String8(mName).string(), String8(name).string(), p.ident, N); 3977 bool found = false; 3978 for (i=0; i<N; i++) { 3979 sp<ConfigList> e = origOrder.itemAt(i); 3980 //printf("#%d: \"%s\"\n", i, String8(e->getName()).string()); 3981 if (e->getName() == name) { 3982 if (idx >= (int32_t)mOrderedConfigs.size()) { 3983 mOrderedConfigs.resize(idx + 1); 3984 } 3985 3986 if (mOrderedConfigs.itemAt(idx) == NULL) { 3987 e->setPublic(true); 3988 e->setPublicSourcePos(p.sourcePos); 3989 mOrderedConfigs.replaceAt(e, idx); 3990 origOrder.removeAt(i); 3991 N--; 3992 found = true; 3993 break; 3994 } else { 3995 sp<ConfigList> oe = mOrderedConfigs.itemAt(idx); 3996 3997 p.sourcePos.error("Multiple entry names declared for public entry" 3998 " identifier 0x%x in type %s (%s vs %s).\n" 3999 "%s:%d: Originally defined here.", 4000 idx+1, String8(mName).string(), 4001 String8(oe->getName()).string(), 4002 String8(name).string(), 4003 oe->getPublicSourcePos().file.string(), 4004 oe->getPublicSourcePos().line); 4005 hasError = true; 4006 } 4007 } 4008 } 4009 4010 if (!found) { 4011 p.sourcePos.error("Public symbol %s/%s declared here is not defined.", 4012 String8(mName).string(), String8(name).string()); 4013 hasError = true; 4014 } 4015 } 4016 4017 //printf("Copying back in %d non-public configs, have %d\n", N, origOrder.size()); 4018 4019 if (N != origOrder.size()) { 4020 printf("Internal error: remaining private symbol count mismatch\n"); 4021 N = origOrder.size(); 4022 } 4023 4024 j = 0; 4025 for (i=0; i<N; i++) { 4026 sp<ConfigList> e = origOrder.itemAt(i); 4027 // There will always be enough room for the remaining entries. 4028 while (mOrderedConfigs.itemAt(j) != NULL) { 4029 j++; 4030 } 4031 mOrderedConfigs.replaceAt(e, j); 4032 j++; 4033 } 4034 4035 return hasError ? STATUST(UNKNOWN_ERROR) : NO_ERROR; 4036 } 4037 4038 ResourceTable::Package::Package(const String16& name, size_t packageId) 4039 : mName(name), mPackageId(packageId), 4040 mTypeStringsMapping(0xffffffff), 4041 mKeyStringsMapping(0xffffffff) 4042 { 4043 } 4044 4045 sp<ResourceTable::Type> ResourceTable::Package::getType(const String16& type, 4046 const SourcePos& sourcePos, 4047 bool doSetIndex) 4048 { 4049 sp<Type> t = mTypes.valueFor(type); 4050 if (t == NULL) { 4051 t = new Type(type, sourcePos); 4052 mTypes.add(type, t); 4053 mOrderedTypes.add(t); 4054 if (doSetIndex) { 4055 // For some reason the type's index is set to one plus the index 4056 // in the mOrderedTypes list, rather than just the index. 4057 t->setIndex(mOrderedTypes.size()); 4058 } 4059 } 4060 return t; 4061 } 4062 4063 status_t ResourceTable::Package::setTypeStrings(const sp<AaptFile>& data) 4064 { 4065 status_t err = setStrings(data, &mTypeStrings, &mTypeStringsMapping); 4066 if (err != NO_ERROR) { 4067 fprintf(stderr, "ERROR: Type string data is corrupt!\n"); 4068 return err; 4069 } 4070 4071 // Retain a reference to the new data after we've successfully replaced 4072 // all uses of the old reference (in setStrings() ). 4073 mTypeStringsData = data; 4074 return NO_ERROR; 4075 } 4076 4077 status_t ResourceTable::Package::setKeyStrings(const sp<AaptFile>& data) 4078 { 4079 status_t err = setStrings(data, &mKeyStrings, &mKeyStringsMapping); 4080 if (err != NO_ERROR) { 4081 fprintf(stderr, "ERROR: Key string data is corrupt!\n"); 4082 return err; 4083 } 4084 4085 // Retain a reference to the new data after we've successfully replaced 4086 // all uses of the old reference (in setStrings() ). 4087 mKeyStringsData = data; 4088 return NO_ERROR; 4089 } 4090 4091 status_t ResourceTable::Package::setStrings(const sp<AaptFile>& data, 4092 ResStringPool* strings, 4093 DefaultKeyedVector<String16, uint32_t>* mappings) 4094 { 4095 if (data->getData() == NULL) { 4096 return UNKNOWN_ERROR; 4097 } 4098 4099 status_t err = strings->setTo(data->getData(), data->getSize()); 4100 if (err == NO_ERROR) { 4101 const size_t N = strings->size(); 4102 for (size_t i=0; i<N; i++) { 4103 size_t len; 4104 mappings->add(String16(strings->stringAt(i, &len)), i); 4105 } 4106 } 4107 return err; 4108 } 4109 4110 status_t ResourceTable::Package::applyPublicTypeOrder() 4111 { 4112 size_t N = mOrderedTypes.size(); 4113 Vector<sp<Type> > origOrder(mOrderedTypes); 4114 4115 size_t i; 4116 for (i=0; i<N; i++) { 4117 mOrderedTypes.replaceAt(NULL, i); 4118 } 4119 4120 for (i=0; i<N; i++) { 4121 sp<Type> t = origOrder.itemAt(i); 4122 int32_t idx = t->getPublicIndex(); 4123 if (idx > 0) { 4124 idx--; 4125 while (idx >= (int32_t)mOrderedTypes.size()) { 4126 mOrderedTypes.add(); 4127 } 4128 if (mOrderedTypes.itemAt(idx) != NULL) { 4129 sp<Type> ot = mOrderedTypes.itemAt(idx); 4130 t->getFirstPublicSourcePos().error("Multiple type names declared for public type" 4131 " identifier 0x%x (%s vs %s).\n" 4132 "%s:%d: Originally defined here.", 4133 idx, String8(ot->getName()).string(), 4134 String8(t->getName()).string(), 4135 ot->getFirstPublicSourcePos().file.string(), 4136 ot->getFirstPublicSourcePos().line); 4137 return UNKNOWN_ERROR; 4138 } 4139 mOrderedTypes.replaceAt(t, idx); 4140 origOrder.removeAt(i); 4141 i--; 4142 N--; 4143 } 4144 } 4145 4146 size_t j=0; 4147 for (i=0; i<N; i++) { 4148 sp<Type> t = origOrder.itemAt(i); 4149 // There will always be enough room for the remaining types. 4150 while (mOrderedTypes.itemAt(j) != NULL) { 4151 j++; 4152 } 4153 mOrderedTypes.replaceAt(t, j); 4154 } 4155 4156 return NO_ERROR; 4157 } 4158 4159 void ResourceTable::Package::movePrivateAttrs() { 4160 sp<Type> attr = mTypes.valueFor(String16("attr")); 4161 if (attr == NULL) { 4162 // Nothing to do. 4163 return; 4164 } 4165 4166 Vector<sp<ConfigList> > privateAttrs; 4167 4168 bool hasPublic = false; 4169 const Vector<sp<ConfigList> >& configs = attr->getOrderedConfigs(); 4170 const size_t configCount = configs.size(); 4171 for (size_t i = 0; i < configCount; i++) { 4172 if (configs[i] == NULL) { 4173 continue; 4174 } 4175 4176 if (attr->isPublic(configs[i]->getName())) { 4177 hasPublic = true; 4178 } else { 4179 privateAttrs.add(configs[i]); 4180 } 4181 } 4182 4183 // Only if we have public attributes do we create a separate type for 4184 // private attributes. 4185 if (!hasPublic) { 4186 return; 4187 } 4188 4189 // Create a new type for private attributes. 4190 sp<Type> privateAttrType = getType(String16(kAttrPrivateType), SourcePos()); 4191 4192 const size_t privateAttrCount = privateAttrs.size(); 4193 for (size_t i = 0; i < privateAttrCount; i++) { 4194 const sp<ConfigList>& cl = privateAttrs[i]; 4195 4196 // Remove the private attributes from their current type. 4197 attr->removeEntry(cl->getName()); 4198 4199 // Add it to the new type. 4200 const DefaultKeyedVector<ConfigDescription, sp<Entry> >& entries = cl->getEntries(); 4201 const size_t entryCount = entries.size(); 4202 for (size_t j = 0; j < entryCount; j++) { 4203 const sp<Entry>& oldEntry = entries[j]; 4204 sp<Entry> entry = privateAttrType->getEntry( 4205 cl->getName(), oldEntry->getPos(), &entries.keyAt(j)); 4206 *entry = *oldEntry; 4207 } 4208 4209 // Move the symbols to the new type. 4210 4211 } 4212 } 4213 4214 sp<ResourceTable::Package> ResourceTable::getPackage(const String16& package) 4215 { 4216 if (package != mAssetsPackage) { 4217 return NULL; 4218 } 4219 return mPackages.valueFor(package); 4220 } 4221 4222 sp<ResourceTable::Type> ResourceTable::getType(const String16& package, 4223 const String16& type, 4224 const SourcePos& sourcePos, 4225 bool doSetIndex) 4226 { 4227 sp<Package> p = getPackage(package); 4228 if (p == NULL) { 4229 return NULL; 4230 } 4231 return p->getType(type, sourcePos, doSetIndex); 4232 } 4233 4234 sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package, 4235 const String16& type, 4236 const String16& name, 4237 const SourcePos& sourcePos, 4238 bool overlay, 4239 const ResTable_config* config, 4240 bool doSetIndex) 4241 { 4242 sp<Type> t = getType(package, type, sourcePos, doSetIndex); 4243 if (t == NULL) { 4244 return NULL; 4245 } 4246 return t->getEntry(name, sourcePos, config, doSetIndex, overlay, mBundle->getAutoAddOverlay()); 4247 } 4248 4249 sp<ResourceTable::ConfigList> ResourceTable::getConfigList(const String16& package, 4250 const String16& type, const String16& name) const 4251 { 4252 const size_t packageCount = mOrderedPackages.size(); 4253 for (size_t pi = 0; pi < packageCount; pi++) { 4254 const sp<Package>& p = mOrderedPackages[pi]; 4255 if (p == NULL || p->getName() != package) { 4256 continue; 4257 } 4258 4259 const Vector<sp<Type> >& types = p->getOrderedTypes(); 4260 const size_t typeCount = types.size(); 4261 for (size_t ti = 0; ti < typeCount; ti++) { 4262 const sp<Type>& t = types[ti]; 4263 if (t == NULL || t->getName() != type) { 4264 continue; 4265 } 4266 4267 const Vector<sp<ConfigList> >& configs = t->getOrderedConfigs(); 4268 const size_t configCount = configs.size(); 4269 for (size_t ci = 0; ci < configCount; ci++) { 4270 const sp<ConfigList>& cl = configs[ci]; 4271 if (cl == NULL || cl->getName() != name) { 4272 continue; 4273 } 4274 4275 return cl; 4276 } 4277 } 4278 } 4279 return NULL; 4280 } 4281 4282 sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID, 4283 const ResTable_config* config) const 4284 { 4285 size_t pid = Res_GETPACKAGE(resID)+1; 4286 const size_t N = mOrderedPackages.size(); 4287 sp<Package> p; 4288 for (size_t i = 0; i < N; i++) { 4289 sp<Package> check = mOrderedPackages[i]; 4290 if (check->getAssignedId() == pid) { 4291 p = check; 4292 break; 4293 } 4294 4295 } 4296 if (p == NULL) { 4297 fprintf(stderr, "warning: Package not found for resource #%08x\n", resID); 4298 return NULL; 4299 } 4300 4301 int tid = Res_GETTYPE(resID); 4302 if (tid < 0 || tid >= (int)p->getOrderedTypes().size()) { 4303 fprintf(stderr, "warning: Type not found for resource #%08x\n", resID); 4304 return NULL; 4305 } 4306 sp<Type> t = p->getOrderedTypes()[tid]; 4307 4308 int eid = Res_GETENTRY(resID); 4309 if (eid < 0 || eid >= (int)t->getOrderedConfigs().size()) { 4310 fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID); 4311 return NULL; 4312 } 4313 4314 sp<ConfigList> c = t->getOrderedConfigs()[eid]; 4315 if (c == NULL) { 4316 fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID); 4317 return NULL; 4318 } 4319 4320 ConfigDescription cdesc; 4321 if (config) cdesc = *config; 4322 sp<Entry> e = c->getEntries().valueFor(cdesc); 4323 if (c == NULL) { 4324 fprintf(stderr, "warning: Entry configuration not found for resource #%08x\n", resID); 4325 return NULL; 4326 } 4327 4328 return e; 4329 } 4330 4331 const ResourceTable::Item* ResourceTable::getItem(uint32_t resID, uint32_t attrID) const 4332 { 4333 sp<const Entry> e = getEntry(resID); 4334 if (e == NULL) { 4335 return NULL; 4336 } 4337 4338 const size_t N = e->getBag().size(); 4339 for (size_t i=0; i<N; i++) { 4340 const Item& it = e->getBag().valueAt(i); 4341 if (it.bagKeyId == 0) { 4342 fprintf(stderr, "warning: ID not yet assigned to '%s' in bag '%s'\n", 4343 String8(e->getName()).string(), 4344 String8(e->getBag().keyAt(i)).string()); 4345 } 4346 if (it.bagKeyId == attrID) { 4347 return ⁢ 4348 } 4349 } 4350 4351 return NULL; 4352 } 4353 4354 bool ResourceTable::getItemValue( 4355 uint32_t resID, uint32_t attrID, Res_value* outValue) 4356 { 4357 const Item* item = getItem(resID, attrID); 4358 4359 bool res = false; 4360 if (item != NULL) { 4361 if (item->evaluating) { 4362 sp<const Entry> e = getEntry(resID); 4363 const size_t N = e->getBag().size(); 4364 size_t i; 4365 for (i=0; i<N; i++) { 4366 if (&e->getBag().valueAt(i) == item) { 4367 break; 4368 } 4369 } 4370 fprintf(stderr, "warning: Circular reference detected in key '%s' of bag '%s'\n", 4371 String8(e->getName()).string(), 4372 String8(e->getBag().keyAt(i)).string()); 4373 return false; 4374 } 4375 item->evaluating = true; 4376 res = stringToValue(outValue, NULL, item->value, false, false, item->bagKeyId); 4377 if (kIsDebug) { 4378 if (res) { 4379 printf("getItemValue of #%08x[#%08x] (%s): type=#%08x, data=#%08x\n", 4380 resID, attrID, String8(getEntry(resID)->getName()).string(), 4381 outValue->dataType, outValue->data); 4382 } else { 4383 printf("getItemValue of #%08x[#%08x]: failed\n", 4384 resID, attrID); 4385 } 4386 } 4387 item->evaluating = false; 4388 } 4389 return res; 4390 } 4391 4392 /** 4393 * Returns the SDK version at which the attribute was 4394 * made public, or -1 if the resource ID is not an attribute 4395 * or is not public. 4396 */ 4397 int ResourceTable::getPublicAttributeSdkLevel(uint32_t attrId) const { 4398 if (Res_GETPACKAGE(attrId) + 1 != 0x01 || Res_GETTYPE(attrId) + 1 != 0x01) { 4399 return -1; 4400 } 4401 4402 uint32_t specFlags; 4403 if (!mAssets->getIncludedResources().getResourceFlags(attrId, &specFlags)) { 4404 return -1; 4405 } 4406 4407 if ((specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) { 4408 return -1; 4409 } 4410 4411 const size_t entryId = Res_GETENTRY(attrId); 4412 if (entryId <= 0x021c) { 4413 return 1; 4414 } else if (entryId <= 0x021d) { 4415 return 2; 4416 } else if (entryId <= 0x0269) { 4417 return SDK_CUPCAKE; 4418 } else if (entryId <= 0x028d) { 4419 return SDK_DONUT; 4420 } else if (entryId <= 0x02ad) { 4421 return SDK_ECLAIR; 4422 } else if (entryId <= 0x02b3) { 4423 return SDK_ECLAIR_0_1; 4424 } else if (entryId <= 0x02b5) { 4425 return SDK_ECLAIR_MR1; 4426 } else if (entryId <= 0x02bd) { 4427 return SDK_FROYO; 4428 } else if (entryId <= 0x02cb) { 4429 return SDK_GINGERBREAD; 4430 } else if (entryId <= 0x0361) { 4431 return SDK_HONEYCOMB; 4432 } else if (entryId <= 0x0366) { 4433 return SDK_HONEYCOMB_MR1; 4434 } else if (entryId <= 0x03a6) { 4435 return SDK_HONEYCOMB_MR2; 4436 } else if (entryId <= 0x03ae) { 4437 return SDK_JELLY_BEAN; 4438 } else if (entryId <= 0x03cc) { 4439 return SDK_JELLY_BEAN_MR1; 4440 } else if (entryId <= 0x03da) { 4441 return SDK_JELLY_BEAN_MR2; 4442 } else if (entryId <= 0x03f1) { 4443 return SDK_KITKAT; 4444 } else if (entryId <= 0x03f6) { 4445 return SDK_KITKAT_WATCH; 4446 } else if (entryId <= 0x04ce) { 4447 return SDK_LOLLIPOP; 4448 } else { 4449 // Anything else is marked as defined in 4450 // SDK_LOLLIPOP_MR1 since after this 4451 // version no attribute compat work 4452 // needs to be done. 4453 return SDK_LOLLIPOP_MR1; 4454 } 4455 } 4456 4457 /** 4458 * First check the Manifest, then check the command line flag. 4459 */ 4460 static int getMinSdkVersion(const Bundle* bundle) { 4461 if (bundle->getManifestMinSdkVersion() != NULL && strlen(bundle->getManifestMinSdkVersion()) > 0) { 4462 return atoi(bundle->getManifestMinSdkVersion()); 4463 } else if (bundle->getMinSdkVersion() != NULL && strlen(bundle->getMinSdkVersion()) > 0) { 4464 return atoi(bundle->getMinSdkVersion()); 4465 } 4466 return 0; 4467 } 4468 4469 bool ResourceTable::shouldGenerateVersionedResource( 4470 const sp<ResourceTable::ConfigList>& configList, 4471 const ConfigDescription& sourceConfig, 4472 const int sdkVersionToGenerate) { 4473 assert(sdkVersionToGenerate > sourceConfig.sdkVersion); 4474 const DefaultKeyedVector<ConfigDescription, sp<ResourceTable::Entry>>& entries 4475 = configList->getEntries(); 4476 ssize_t idx = entries.indexOfKey(sourceConfig); 4477 4478 // The source config came from this list, so it should be here. 4479 assert(idx >= 0); 4480 4481 // The next configuration either only varies in sdkVersion, or it is completely different 4482 // and therefore incompatible. If it is incompatible, we must generate the versioned resource. 4483 4484 // NOTE: The ordering of configurations takes sdkVersion as higher precedence than other 4485 // qualifiers, so we need to iterate through the entire list to be sure there 4486 // are no higher sdk level versions of this resource. 4487 ConfigDescription tempConfig(sourceConfig); 4488 for (size_t i = static_cast<size_t>(idx) + 1; i < entries.size(); i++) { 4489 const ConfigDescription& nextConfig = entries.keyAt(i); 4490 tempConfig.sdkVersion = nextConfig.sdkVersion; 4491 if (tempConfig == nextConfig) { 4492 // The two configs are the same, check the sdk version. 4493 return sdkVersionToGenerate < nextConfig.sdkVersion; 4494 } 4495 } 4496 4497 // No match was found, so we should generate the versioned resource. 4498 return true; 4499 } 4500 4501 /** 4502 * Modifies the entries in the resource table to account for compatibility 4503 * issues with older versions of Android. 4504 * 4505 * This primarily handles the issue of private/public attribute clashes 4506 * in framework resources. 4507 * 4508 * AAPT has traditionally assigned resource IDs to public attributes, 4509 * and then followed those public definitions with private attributes. 4510 * 4511 * --- PUBLIC --- 4512 * | 0x01010234 | attr/color 4513 * | 0x01010235 | attr/background 4514 * 4515 * --- PRIVATE --- 4516 * | 0x01010236 | attr/secret 4517 * | 0x01010237 | attr/shhh 4518 * 4519 * Each release, when attributes are added, they take the place of the private 4520 * attributes and the private attributes are shifted down again. 4521 * 4522 * --- PUBLIC --- 4523 * | 0x01010234 | attr/color 4524 * | 0x01010235 | attr/background 4525 * | 0x01010236 | attr/shinyNewAttr 4526 * | 0x01010237 | attr/highlyValuedFeature 4527 * 4528 * --- PRIVATE --- 4529 * | 0x01010238 | attr/secret 4530 * | 0x01010239 | attr/shhh 4531 * 4532 * Platform code may look for private attributes set in a theme. If an app 4533 * compiled against a newer version of the platform uses a new public 4534 * attribute that happens to have the same ID as the private attribute 4535 * the older platform is expecting, then the behavior is undefined. 4536 * 4537 * We get around this by detecting any newly defined attributes (in L), 4538 * copy the resource into a -v21 qualified resource, and delete the 4539 * attribute from the original resource. This ensures that older platforms 4540 * don't see the new attribute, but when running on L+ platforms, the 4541 * attribute will be respected. 4542 */ 4543 status_t ResourceTable::modifyForCompat(const Bundle* bundle) { 4544 const int minSdk = getMinSdkVersion(bundle); 4545 if (minSdk >= SDK_LOLLIPOP_MR1) { 4546 // Lollipop MR1 and up handles public attributes differently, no 4547 // need to do any compat modifications. 4548 return NO_ERROR; 4549 } 4550 4551 const String16 attr16("attr"); 4552 4553 const size_t packageCount = mOrderedPackages.size(); 4554 for (size_t pi = 0; pi < packageCount; pi++) { 4555 sp<Package> p = mOrderedPackages.itemAt(pi); 4556 if (p == NULL || p->getTypes().size() == 0) { 4557 // Empty, skip! 4558 continue; 4559 } 4560 4561 const size_t typeCount = p->getOrderedTypes().size(); 4562 for (size_t ti = 0; ti < typeCount; ti++) { 4563 sp<Type> t = p->getOrderedTypes().itemAt(ti); 4564 if (t == NULL) { 4565 continue; 4566 } 4567 4568 const size_t configCount = t->getOrderedConfigs().size(); 4569 for (size_t ci = 0; ci < configCount; ci++) { 4570 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci); 4571 if (c == NULL) { 4572 continue; 4573 } 4574 4575 Vector<key_value_pair_t<ConfigDescription, sp<Entry> > > entriesToAdd; 4576 const DefaultKeyedVector<ConfigDescription, sp<Entry> >& entries = 4577 c->getEntries(); 4578 const size_t entryCount = entries.size(); 4579 for (size_t ei = 0; ei < entryCount; ei++) { 4580 sp<Entry> e = entries.valueAt(ei); 4581 if (e == NULL || e->getType() != Entry::TYPE_BAG) { 4582 continue; 4583 } 4584 4585 const ConfigDescription& config = entries.keyAt(ei); 4586 if (config.sdkVersion >= SDK_LOLLIPOP_MR1) { 4587 continue; 4588 } 4589 4590 KeyedVector<int, Vector<String16> > attributesToRemove; 4591 const KeyedVector<String16, Item>& bag = e->getBag(); 4592 const size_t bagCount = bag.size(); 4593 for (size_t bi = 0; bi < bagCount; bi++) { 4594 const uint32_t attrId = getResId(bag.keyAt(bi), &attr16); 4595 const int sdkLevel = getPublicAttributeSdkLevel(attrId); 4596 if (sdkLevel > 1 && sdkLevel > config.sdkVersion && sdkLevel > minSdk) { 4597 AaptUtil::appendValue(attributesToRemove, sdkLevel, bag.keyAt(bi)); 4598 } 4599 } 4600 4601 if (attributesToRemove.isEmpty()) { 4602 continue; 4603 } 4604 4605 const size_t sdkCount = attributesToRemove.size(); 4606 for (size_t i = 0; i < sdkCount; i++) { 4607 const int sdkLevel = attributesToRemove.keyAt(i); 4608 4609 if (!shouldGenerateVersionedResource(c, config, sdkLevel)) { 4610 // There is a style that will override this generated one. 4611 continue; 4612 } 4613 4614 // Duplicate the entry under the same configuration 4615 // but with sdkVersion == sdkLevel. 4616 ConfigDescription newConfig(config); 4617 newConfig.sdkVersion = sdkLevel; 4618 4619 sp<Entry> newEntry = new Entry(*e); 4620 4621 // Remove all items that have a higher SDK level than 4622 // the one we are synthesizing. 4623 for (size_t j = 0; j < sdkCount; j++) { 4624 if (j == i) { 4625 continue; 4626 } 4627 4628 if (attributesToRemove.keyAt(j) > sdkLevel) { 4629 const size_t attrCount = attributesToRemove[j].size(); 4630 for (size_t k = 0; k < attrCount; k++) { 4631 newEntry->removeFromBag(attributesToRemove[j][k]); 4632 } 4633 } 4634 } 4635 4636 entriesToAdd.add(key_value_pair_t<ConfigDescription, sp<Entry> >( 4637 newConfig, newEntry)); 4638 } 4639 4640 // Remove the attribute from the original. 4641 for (size_t i = 0; i < attributesToRemove.size(); i++) { 4642 for (size_t j = 0; j < attributesToRemove[i].size(); j++) { 4643 e->removeFromBag(attributesToRemove[i][j]); 4644 } 4645 } 4646 } 4647 4648 const size_t entriesToAddCount = entriesToAdd.size(); 4649 for (size_t i = 0; i < entriesToAddCount; i++) { 4650 assert(entries.indexOfKey(entriesToAdd[i].key) < 0); 4651 4652 if (bundle->getVerbose()) { 4653 entriesToAdd[i].value->getPos() 4654 .printf("using v%d attributes; synthesizing resource %s:%s/%s for configuration %s.", 4655 entriesToAdd[i].key.sdkVersion, 4656 String8(p->getName()).string(), 4657 String8(t->getName()).string(), 4658 String8(entriesToAdd[i].value->getName()).string(), 4659 entriesToAdd[i].key.toString().string()); 4660 } 4661 4662 sp<Entry> newEntry = t->getEntry(c->getName(), 4663 entriesToAdd[i].value->getPos(), 4664 &entriesToAdd[i].key); 4665 4666 *newEntry = *entriesToAdd[i].value; 4667 } 4668 } 4669 } 4670 } 4671 return NO_ERROR; 4672 } 4673 4674 status_t ResourceTable::modifyForCompat(const Bundle* bundle, 4675 const String16& resourceName, 4676 const sp<AaptFile>& target, 4677 const sp<XMLNode>& root) { 4678 const String16 vector16("vector"); 4679 const String16 animatedVector16("animated-vector"); 4680 4681 const int minSdk = getMinSdkVersion(bundle); 4682 if (minSdk >= SDK_LOLLIPOP_MR1) { 4683 // Lollipop MR1 and up handles public attributes differently, no 4684 // need to do any compat modifications. 4685 return NO_ERROR; 4686 } 4687 4688 const ConfigDescription config(target->getGroupEntry().toParams()); 4689 if (target->getResourceType() == "" || config.sdkVersion >= SDK_LOLLIPOP_MR1) { 4690 // Skip resources that have no type (AndroidManifest.xml) or are already version qualified 4691 // with v21 or higher. 4692 return NO_ERROR; 4693 } 4694 4695 sp<XMLNode> newRoot = NULL; 4696 int sdkVersionToGenerate = SDK_LOLLIPOP_MR1; 4697 4698 Vector<sp<XMLNode> > nodesToVisit; 4699 nodesToVisit.push(root); 4700 while (!nodesToVisit.isEmpty()) { 4701 sp<XMLNode> node = nodesToVisit.top(); 4702 nodesToVisit.pop(); 4703 4704 if (bundle->getNoVersionVectors() && (node->getElementName() == vector16 || 4705 node->getElementName() == animatedVector16)) { 4706 // We were told not to version vector tags, so skip the children here. 4707 continue; 4708 } 4709 4710 const Vector<XMLNode::attribute_entry>& attrs = node->getAttributes(); 4711 for (size_t i = 0; i < attrs.size(); i++) { 4712 const XMLNode::attribute_entry& attr = attrs[i]; 4713 const int sdkLevel = getPublicAttributeSdkLevel(attr.nameResId); 4714 if (sdkLevel > 1 && sdkLevel > config.sdkVersion && sdkLevel > minSdk) { 4715 if (newRoot == NULL) { 4716 newRoot = root->clone(); 4717 } 4718 4719 // Find the smallest sdk version that we need to synthesize for 4720 // and do that one. Subsequent versions will be processed on 4721 // the next pass. 4722 sdkVersionToGenerate = std::min(sdkLevel, sdkVersionToGenerate); 4723 4724 if (bundle->getVerbose()) { 4725 SourcePos(node->getFilename(), node->getStartLineNumber()).printf( 4726 "removing attribute %s%s%s from <%s>", 4727 String8(attr.ns).string(), 4728 (attr.ns.size() == 0 ? "" : ":"), 4729 String8(attr.name).string(), 4730 String8(node->getElementName()).string()); 4731 } 4732 node->removeAttribute(i); 4733 i--; 4734 } 4735 } 4736 4737 // Schedule a visit to the children. 4738 const Vector<sp<XMLNode> >& children = node->getChildren(); 4739 const size_t childCount = children.size(); 4740 for (size_t i = 0; i < childCount; i++) { 4741 nodesToVisit.push(children[i]); 4742 } 4743 } 4744 4745 if (newRoot == NULL) { 4746 return NO_ERROR; 4747 } 4748 4749 // Look to see if we already have an overriding v21 configuration. 4750 sp<ConfigList> cl = getConfigList(String16(mAssets->getPackage()), 4751 String16(target->getResourceType()), resourceName); 4752 if (shouldGenerateVersionedResource(cl, config, sdkVersionToGenerate)) { 4753 // We don't have an overriding entry for v21, so we must duplicate this one. 4754 ConfigDescription newConfig(config); 4755 newConfig.sdkVersion = sdkVersionToGenerate; 4756 sp<AaptFile> newFile = new AaptFile(target->getSourceFile(), 4757 AaptGroupEntry(newConfig), target->getResourceType()); 4758 String8 resPath = String8::format("res/%s/%s", 4759 newFile->getGroupEntry().toDirName(target->getResourceType()).string(), 4760 target->getSourceFile().getPathLeaf().string()); 4761 resPath.convertToResPath(); 4762 4763 // Add a resource table entry. 4764 if (bundle->getVerbose()) { 4765 SourcePos(target->getSourceFile(), -1).printf( 4766 "using v%d attributes; synthesizing resource %s:%s/%s for configuration %s.", 4767 newConfig.sdkVersion, 4768 mAssets->getPackage().string(), 4769 newFile->getResourceType().string(), 4770 String8(resourceName).string(), 4771 newConfig.toString().string()); 4772 } 4773 4774 addEntry(SourcePos(), 4775 String16(mAssets->getPackage()), 4776 String16(target->getResourceType()), 4777 resourceName, 4778 String16(resPath), 4779 NULL, 4780 &newConfig); 4781 4782 // Schedule this to be compiled. 4783 CompileResourceWorkItem item; 4784 item.resourceName = resourceName; 4785 item.resPath = resPath; 4786 item.file = newFile; 4787 mWorkQueue.push(item); 4788 } 4789 4790 return NO_ERROR; 4791 } 4792 4793 void ResourceTable::getDensityVaryingResources( 4794 KeyedVector<Symbol, Vector<SymbolDefinition> >& resources) { 4795 const ConfigDescription nullConfig; 4796 4797 const size_t packageCount = mOrderedPackages.size(); 4798 for (size_t p = 0; p < packageCount; p++) { 4799 const Vector<sp<Type> >& types = mOrderedPackages[p]->getOrderedTypes(); 4800 const size_t typeCount = types.size(); 4801 for (size_t t = 0; t < typeCount; t++) { 4802 const Vector<sp<ConfigList> >& configs = types[t]->getOrderedConfigs(); 4803 const size_t configCount = configs.size(); 4804 for (size_t c = 0; c < configCount; c++) { 4805 const DefaultKeyedVector<ConfigDescription, sp<Entry> >& configEntries 4806 = configs[c]->getEntries(); 4807 const size_t configEntryCount = configEntries.size(); 4808 for (size_t ce = 0; ce < configEntryCount; ce++) { 4809 const ConfigDescription& config = configEntries.keyAt(ce); 4810 if (AaptConfig::isDensityOnly(config)) { 4811 // This configuration only varies with regards to density. 4812 const Symbol symbol( 4813 mOrderedPackages[p]->getName(), 4814 types[t]->getName(), 4815 configs[c]->getName(), 4816 getResId(mOrderedPackages[p], types[t], 4817 configs[c]->getEntryIndex())); 4818 4819 const sp<Entry>& entry = configEntries.valueAt(ce); 4820 AaptUtil::appendValue(resources, symbol, 4821 SymbolDefinition(symbol, config, entry->getPos())); 4822 } 4823 } 4824 } 4825 } 4826 } 4827 } 4828