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