1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "NameMangler.h" 18 #include "ResourceUtils.h" 19 #include "flatten/ResourceTypeExtensions.h" 20 #include "util/Files.h" 21 #include "util/Util.h" 22 23 #include <androidfw/ResourceTypes.h> 24 #include <sstream> 25 26 namespace aapt { 27 namespace ResourceUtils { 28 29 bool extractResourceName(const StringPiece16& str, StringPiece16* outPackage, 30 StringPiece16* outType, StringPiece16* outEntry) { 31 bool hasPackageSeparator = false; 32 bool hasTypeSeparator = false; 33 const char16_t* start = str.data(); 34 const char16_t* end = start + str.size(); 35 const char16_t* current = start; 36 while (current != end) { 37 if (outType->size() == 0 && *current == u'/') { 38 hasTypeSeparator = true; 39 outType->assign(start, current - start); 40 start = current + 1; 41 } else if (outPackage->size() == 0 && *current == u':') { 42 hasPackageSeparator = true; 43 outPackage->assign(start, current - start); 44 start = current + 1; 45 } 46 current++; 47 } 48 outEntry->assign(start, end - start); 49 50 return !(hasPackageSeparator && outPackage->empty()) && !(hasTypeSeparator && outType->empty()); 51 } 52 53 bool parseResourceName(const StringPiece16& str, ResourceNameRef* outRef, bool* outPrivate) { 54 if (str.empty()) { 55 return false; 56 } 57 58 size_t offset = 0; 59 bool priv = false; 60 if (str.data()[0] == u'*') { 61 priv = true; 62 offset = 1; 63 } 64 65 StringPiece16 package; 66 StringPiece16 type; 67 StringPiece16 entry; 68 if (!extractResourceName(str.substr(offset, str.size() - offset), &package, &type, &entry)) { 69 return false; 70 } 71 72 const ResourceType* parsedType = parseResourceType(type); 73 if (!parsedType) { 74 return false; 75 } 76 77 if (entry.empty()) { 78 return false; 79 } 80 81 if (outRef) { 82 outRef->package = package; 83 outRef->type = *parsedType; 84 outRef->entry = entry; 85 } 86 87 if (outPrivate) { 88 *outPrivate = priv; 89 } 90 return true; 91 } 92 93 bool tryParseReference(const StringPiece16& str, ResourceNameRef* outRef, bool* outCreate, 94 bool* outPrivate) { 95 StringPiece16 trimmedStr(util::trimWhitespace(str)); 96 if (trimmedStr.empty()) { 97 return false; 98 } 99 100 bool create = false; 101 bool priv = false; 102 if (trimmedStr.data()[0] == u'@') { 103 size_t offset = 1; 104 if (trimmedStr.data()[1] == u'+') { 105 create = true; 106 offset += 1; 107 } 108 109 ResourceNameRef name; 110 if (!parseResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset), 111 &name, &priv)) { 112 return false; 113 } 114 115 if (create && priv) { 116 return false; 117 } 118 119 if (create && name.type != ResourceType::kId) { 120 return false; 121 } 122 123 if (outRef) { 124 *outRef = name; 125 } 126 127 if (outCreate) { 128 *outCreate = create; 129 } 130 131 if (outPrivate) { 132 *outPrivate = priv; 133 } 134 return true; 135 } 136 return false; 137 } 138 139 bool isReference(const StringPiece16& str) { 140 return tryParseReference(str, nullptr, nullptr, nullptr); 141 } 142 143 bool tryParseAttributeReference(const StringPiece16& str, ResourceNameRef* outRef) { 144 StringPiece16 trimmedStr(util::trimWhitespace(str)); 145 if (trimmedStr.empty()) { 146 return false; 147 } 148 149 if (*trimmedStr.data() == u'?') { 150 StringPiece16 package; 151 StringPiece16 type; 152 StringPiece16 entry; 153 if (!extractResourceName(trimmedStr.substr(1, trimmedStr.size() - 1), 154 &package, &type, &entry)) { 155 return false; 156 } 157 158 if (!type.empty() && type != u"attr") { 159 return false; 160 } 161 162 if (entry.empty()) { 163 return false; 164 } 165 166 if (outRef) { 167 outRef->package = package; 168 outRef->type = ResourceType::kAttr; 169 outRef->entry = entry; 170 } 171 return true; 172 } 173 return false; 174 } 175 176 bool isAttributeReference(const StringPiece16& str) { 177 return tryParseAttributeReference(str, nullptr); 178 } 179 180 /* 181 * Style parent's are a bit different. We accept the following formats: 182 * 183 * @[[*]package:][style/]<entry> 184 * ?[[*]package:]style/<entry> 185 * <[*]package>:[style/]<entry> 186 * [[*]package:style/]<entry> 187 */ 188 Maybe<Reference> parseStyleParentReference(const StringPiece16& str, std::string* outError) { 189 if (str.empty()) { 190 return {}; 191 } 192 193 StringPiece16 name = str; 194 195 bool hasLeadingIdentifiers = false; 196 bool privateRef = false; 197 198 // Skip over these identifiers. A style's parent is a normal reference. 199 if (name.data()[0] == u'@' || name.data()[0] == u'?') { 200 hasLeadingIdentifiers = true; 201 name = name.substr(1, name.size() - 1); 202 } 203 204 if (name.data()[0] == u'*') { 205 privateRef = true; 206 name = name.substr(1, name.size() - 1); 207 } 208 209 ResourceNameRef ref; 210 ref.type = ResourceType::kStyle; 211 212 StringPiece16 typeStr; 213 extractResourceName(name, &ref.package, &typeStr, &ref.entry); 214 if (!typeStr.empty()) { 215 // If we have a type, make sure it is a Style. 216 const ResourceType* parsedType = parseResourceType(typeStr); 217 if (!parsedType || *parsedType != ResourceType::kStyle) { 218 std::stringstream err; 219 err << "invalid resource type '" << typeStr << "' for parent of style"; 220 *outError = err.str(); 221 return {}; 222 } 223 } 224 225 if (!hasLeadingIdentifiers && ref.package.empty() && !typeStr.empty()) { 226 std::stringstream err; 227 err << "invalid parent reference '" << str << "'"; 228 *outError = err.str(); 229 return {}; 230 } 231 232 Reference result(ref); 233 result.privateReference = privateRef; 234 return result; 235 } 236 237 std::unique_ptr<Reference> tryParseReference(const StringPiece16& str, bool* outCreate) { 238 ResourceNameRef ref; 239 bool privateRef = false; 240 if (tryParseReference(str, &ref, outCreate, &privateRef)) { 241 std::unique_ptr<Reference> value = util::make_unique<Reference>(ref); 242 value->privateReference = privateRef; 243 return value; 244 } 245 246 if (tryParseAttributeReference(str, &ref)) { 247 if (outCreate) { 248 *outCreate = false; 249 } 250 return util::make_unique<Reference>(ref, Reference::Type::kAttribute); 251 } 252 return {}; 253 } 254 255 std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece16& str) { 256 StringPiece16 trimmedStr(util::trimWhitespace(str)); 257 android::Res_value value = { }; 258 if (trimmedStr == u"@null") { 259 // TYPE_NULL with data set to 0 is interpreted by the runtime as an error. 260 // Instead we set the data type to TYPE_REFERENCE with a value of 0. 261 value.dataType = android::Res_value::TYPE_REFERENCE; 262 } else if (trimmedStr == u"@empty") { 263 // TYPE_NULL with value of DATA_NULL_EMPTY is handled fine by the runtime. 264 value.dataType = android::Res_value::TYPE_NULL; 265 value.data = android::Res_value::DATA_NULL_EMPTY; 266 } else { 267 return {}; 268 } 269 return util::make_unique<BinaryPrimitive>(value); 270 } 271 272 std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute* enumAttr, 273 const StringPiece16& str) { 274 StringPiece16 trimmedStr(util::trimWhitespace(str)); 275 for (const Attribute::Symbol& symbol : enumAttr->symbols) { 276 // Enum symbols are stored as @package:id/symbol resources, 277 // so we need to match against the 'entry' part of the identifier. 278 const ResourceName& enumSymbolResourceName = symbol.symbol.name.value(); 279 if (trimmedStr == enumSymbolResourceName.entry) { 280 android::Res_value value = { }; 281 value.dataType = android::Res_value::TYPE_INT_DEC; 282 value.data = symbol.value; 283 return util::make_unique<BinaryPrimitive>(value); 284 } 285 } 286 return {}; 287 } 288 289 std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* flagAttr, 290 const StringPiece16& str) { 291 android::Res_value flags = { }; 292 flags.dataType = android::Res_value::TYPE_INT_DEC; 293 flags.data = 0u; 294 295 if (util::trimWhitespace(str).empty()) { 296 // Empty string is a valid flag (0). 297 return util::make_unique<BinaryPrimitive>(flags); 298 } 299 300 for (StringPiece16 part : util::tokenize(str, u'|')) { 301 StringPiece16 trimmedPart = util::trimWhitespace(part); 302 303 bool flagSet = false; 304 for (const Attribute::Symbol& symbol : flagAttr->symbols) { 305 // Flag symbols are stored as @package:id/symbol resources, 306 // so we need to match against the 'entry' part of the identifier. 307 const ResourceName& flagSymbolResourceName = symbol.symbol.name.value(); 308 if (trimmedPart == flagSymbolResourceName.entry) { 309 flags.data |= symbol.value; 310 flagSet = true; 311 break; 312 } 313 } 314 315 if (!flagSet) { 316 return {}; 317 } 318 } 319 return util::make_unique<BinaryPrimitive>(flags); 320 } 321 322 static uint32_t parseHex(char16_t c, bool* outError) { 323 if (c >= u'0' && c <= u'9') { 324 return c - u'0'; 325 } else if (c >= u'a' && c <= u'f') { 326 return c - u'a' + 0xa; 327 } else if (c >= u'A' && c <= u'F') { 328 return c - u'A' + 0xa; 329 } else { 330 *outError = true; 331 return 0xffffffffu; 332 } 333 } 334 335 std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece16& str) { 336 StringPiece16 colorStr(util::trimWhitespace(str)); 337 const char16_t* start = colorStr.data(); 338 const size_t len = colorStr.size(); 339 if (len == 0 || start[0] != u'#') { 340 return {}; 341 } 342 343 android::Res_value value = { }; 344 bool error = false; 345 if (len == 4) { 346 value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4; 347 value.data = 0xff000000u; 348 value.data |= parseHex(start[1], &error) << 20; 349 value.data |= parseHex(start[1], &error) << 16; 350 value.data |= parseHex(start[2], &error) << 12; 351 value.data |= parseHex(start[2], &error) << 8; 352 value.data |= parseHex(start[3], &error) << 4; 353 value.data |= parseHex(start[3], &error); 354 } else if (len == 5) { 355 value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4; 356 value.data |= parseHex(start[1], &error) << 28; 357 value.data |= parseHex(start[1], &error) << 24; 358 value.data |= parseHex(start[2], &error) << 20; 359 value.data |= parseHex(start[2], &error) << 16; 360 value.data |= parseHex(start[3], &error) << 12; 361 value.data |= parseHex(start[3], &error) << 8; 362 value.data |= parseHex(start[4], &error) << 4; 363 value.data |= parseHex(start[4], &error); 364 } else if (len == 7) { 365 value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8; 366 value.data = 0xff000000u; 367 value.data |= parseHex(start[1], &error) << 20; 368 value.data |= parseHex(start[2], &error) << 16; 369 value.data |= parseHex(start[3], &error) << 12; 370 value.data |= parseHex(start[4], &error) << 8; 371 value.data |= parseHex(start[5], &error) << 4; 372 value.data |= parseHex(start[6], &error); 373 } else if (len == 9) { 374 value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8; 375 value.data |= parseHex(start[1], &error) << 28; 376 value.data |= parseHex(start[2], &error) << 24; 377 value.data |= parseHex(start[3], &error) << 20; 378 value.data |= parseHex(start[4], &error) << 16; 379 value.data |= parseHex(start[5], &error) << 12; 380 value.data |= parseHex(start[6], &error) << 8; 381 value.data |= parseHex(start[7], &error) << 4; 382 value.data |= parseHex(start[8], &error); 383 } else { 384 return {}; 385 } 386 return error ? std::unique_ptr<BinaryPrimitive>() : util::make_unique<BinaryPrimitive>(value); 387 } 388 389 bool tryParseBool(const StringPiece16& str, bool* outValue) { 390 StringPiece16 trimmedStr(util::trimWhitespace(str)); 391 if (trimmedStr == u"true" || trimmedStr == u"TRUE" || trimmedStr == u"True") { 392 if (outValue) { 393 *outValue = true; 394 } 395 return true; 396 } else if (trimmedStr == u"false" || trimmedStr == u"FALSE" || trimmedStr == u"False") { 397 if (outValue) { 398 *outValue = false; 399 } 400 return true; 401 } 402 return false; 403 } 404 405 std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece16& str) { 406 bool result = false; 407 if (tryParseBool(str, &result)) { 408 android::Res_value value = {}; 409 value.dataType = android::Res_value::TYPE_INT_BOOLEAN; 410 411 if (result) { 412 value.data = 0xffffffffu; 413 } else { 414 value.data = 0; 415 } 416 return util::make_unique<BinaryPrimitive>(value); 417 } 418 return {}; 419 } 420 421 std::unique_ptr<BinaryPrimitive> tryParseInt(const StringPiece16& str) { 422 android::Res_value value; 423 if (!android::ResTable::stringToInt(str.data(), str.size(), &value)) { 424 return {}; 425 } 426 return util::make_unique<BinaryPrimitive>(value); 427 } 428 429 std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece16& str) { 430 android::Res_value value; 431 if (!android::ResTable::stringToFloat(str.data(), str.size(), &value)) { 432 return {}; 433 } 434 return util::make_unique<BinaryPrimitive>(value); 435 } 436 437 uint32_t androidTypeToAttributeTypeMask(uint16_t type) { 438 switch (type) { 439 case android::Res_value::TYPE_NULL: 440 case android::Res_value::TYPE_REFERENCE: 441 case android::Res_value::TYPE_ATTRIBUTE: 442 case android::Res_value::TYPE_DYNAMIC_REFERENCE: 443 return android::ResTable_map::TYPE_REFERENCE; 444 445 case android::Res_value::TYPE_STRING: 446 return android::ResTable_map::TYPE_STRING; 447 448 case android::Res_value::TYPE_FLOAT: 449 return android::ResTable_map::TYPE_FLOAT; 450 451 case android::Res_value::TYPE_DIMENSION: 452 return android::ResTable_map::TYPE_DIMENSION; 453 454 case android::Res_value::TYPE_FRACTION: 455 return android::ResTable_map::TYPE_FRACTION; 456 457 case android::Res_value::TYPE_INT_DEC: 458 case android::Res_value::TYPE_INT_HEX: 459 return android::ResTable_map::TYPE_INTEGER | android::ResTable_map::TYPE_ENUM 460 | android::ResTable_map::TYPE_FLAGS; 461 462 case android::Res_value::TYPE_INT_BOOLEAN: 463 return android::ResTable_map::TYPE_BOOLEAN; 464 465 case android::Res_value::TYPE_INT_COLOR_ARGB8: 466 case android::Res_value::TYPE_INT_COLOR_RGB8: 467 case android::Res_value::TYPE_INT_COLOR_ARGB4: 468 case android::Res_value::TYPE_INT_COLOR_RGB4: 469 return android::ResTable_map::TYPE_COLOR; 470 471 default: 472 return 0; 473 }; 474 } 475 476 std::unique_ptr<Item> parseItemForAttribute( 477 const StringPiece16& value, uint32_t typeMask, 478 std::function<void(const ResourceName&)> onCreateReference) { 479 std::unique_ptr<BinaryPrimitive> nullOrEmpty = tryParseNullOrEmpty(value); 480 if (nullOrEmpty) { 481 return std::move(nullOrEmpty); 482 } 483 484 bool create = false; 485 std::unique_ptr<Reference> reference = tryParseReference(value, &create); 486 if (reference) { 487 if (create && onCreateReference) { 488 onCreateReference(reference->name.value()); 489 } 490 return std::move(reference); 491 } 492 493 if (typeMask & android::ResTable_map::TYPE_COLOR) { 494 // Try parsing this as a color. 495 std::unique_ptr<BinaryPrimitive> color = tryParseColor(value); 496 if (color) { 497 return std::move(color); 498 } 499 } 500 501 if (typeMask & android::ResTable_map::TYPE_BOOLEAN) { 502 // Try parsing this as a boolean. 503 std::unique_ptr<BinaryPrimitive> boolean = tryParseBool(value); 504 if (boolean) { 505 return std::move(boolean); 506 } 507 } 508 509 if (typeMask & android::ResTable_map::TYPE_INTEGER) { 510 // Try parsing this as an integer. 511 std::unique_ptr<BinaryPrimitive> integer = tryParseInt(value); 512 if (integer) { 513 return std::move(integer); 514 } 515 } 516 517 const uint32_t floatMask = android::ResTable_map::TYPE_FLOAT 518 | android::ResTable_map::TYPE_DIMENSION | android::ResTable_map::TYPE_FRACTION; 519 if (typeMask & floatMask) { 520 // Try parsing this as a float. 521 std::unique_ptr<BinaryPrimitive> floatingPoint = tryParseFloat(value); 522 if (floatingPoint) { 523 if (typeMask & androidTypeToAttributeTypeMask(floatingPoint->value.dataType)) { 524 return std::move(floatingPoint); 525 } 526 } 527 } 528 return {}; 529 } 530 531 /** 532 * We successively try to parse the string as a resource type that the Attribute 533 * allows. 534 */ 535 std::unique_ptr<Item> parseItemForAttribute( 536 const StringPiece16& str, const Attribute* attr, 537 std::function<void(const ResourceName&)> onCreateReference) { 538 const uint32_t typeMask = attr->typeMask; 539 std::unique_ptr<Item> value = parseItemForAttribute(str, typeMask, onCreateReference); 540 if (value) { 541 return value; 542 } 543 544 if (typeMask & android::ResTable_map::TYPE_ENUM) { 545 // Try parsing this as an enum. 546 std::unique_ptr<BinaryPrimitive> enumValue = tryParseEnumSymbol(attr, str); 547 if (enumValue) { 548 return std::move(enumValue); 549 } 550 } 551 552 if (typeMask & android::ResTable_map::TYPE_FLAGS) { 553 // Try parsing this as a flag. 554 std::unique_ptr<BinaryPrimitive> flagValue = tryParseFlagSymbol(attr, str); 555 if (flagValue) { 556 return std::move(flagValue); 557 } 558 } 559 return {}; 560 } 561 562 std::string buildResourceFileName(const ResourceFile& resFile, const NameMangler* mangler) { 563 std::stringstream out; 564 out << "res/" << resFile.name.type; 565 if (resFile.config != ConfigDescription{}) { 566 out << "-" << resFile.config; 567 } 568 out << "/"; 569 570 if (mangler && mangler->shouldMangle(resFile.name.package)) { 571 out << NameMangler::mangleEntry(resFile.name.package, resFile.name.entry); 572 } else { 573 out << resFile.name.entry; 574 } 575 out << file::getExtension(resFile.source.path); 576 return out.str(); 577 } 578 579 } // namespace ResourceUtils 580 } // namespace aapt 581