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 "ResourceUtils.h" 18 19 #include <sstream> 20 21 #include "androidfw/ResourceTypes.h" 22 #include "androidfw/ResourceUtils.h" 23 24 #include "NameMangler.h" 25 #include "SdkConstants.h" 26 #include "flatten/ResourceTypeExtensions.h" 27 #include "util/Files.h" 28 #include "util/Util.h" 29 30 using android::StringPiece; 31 using android::StringPiece16; 32 33 namespace aapt { 34 namespace ResourceUtils { 35 36 Maybe<ResourceName> ToResourceName( 37 const android::ResTable::resource_name& name_in) { 38 ResourceName name_out; 39 if (!name_in.package) { 40 return {}; 41 } 42 43 name_out.package = 44 util::Utf16ToUtf8(StringPiece16(name_in.package, name_in.packageLen)); 45 46 const ResourceType* type; 47 if (name_in.type) { 48 type = ParseResourceType( 49 util::Utf16ToUtf8(StringPiece16(name_in.type, name_in.typeLen))); 50 } else if (name_in.type8) { 51 type = ParseResourceType(StringPiece(name_in.type8, name_in.typeLen)); 52 } else { 53 return {}; 54 } 55 56 if (!type) { 57 return {}; 58 } 59 60 name_out.type = *type; 61 62 if (name_in.name) { 63 name_out.entry = 64 util::Utf16ToUtf8(StringPiece16(name_in.name, name_in.nameLen)); 65 } else if (name_in.name8) { 66 name_out.entry.assign(name_in.name8, name_in.nameLen); 67 } else { 68 return {}; 69 } 70 return name_out; 71 } 72 73 bool ParseResourceName(const StringPiece& str, ResourceNameRef* out_ref, 74 bool* out_private) { 75 if (str.empty()) { 76 return false; 77 } 78 79 size_t offset = 0; 80 bool priv = false; 81 if (str.data()[0] == '*') { 82 priv = true; 83 offset = 1; 84 } 85 86 StringPiece package; 87 StringPiece type; 88 StringPiece entry; 89 if (!android::ExtractResourceName(str.substr(offset, str.size() - offset), &package, &type, 90 &entry)) { 91 return false; 92 } 93 94 const ResourceType* parsed_type = ParseResourceType(type); 95 if (!parsed_type) { 96 return false; 97 } 98 99 if (entry.empty()) { 100 return false; 101 } 102 103 if (out_ref) { 104 out_ref->package = package; 105 out_ref->type = *parsed_type; 106 out_ref->entry = entry; 107 } 108 109 if (out_private) { 110 *out_private = priv; 111 } 112 return true; 113 } 114 115 bool ParseReference(const StringPiece& str, ResourceNameRef* out_ref, 116 bool* out_create, bool* out_private) { 117 StringPiece trimmed_str(util::TrimWhitespace(str)); 118 if (trimmed_str.empty()) { 119 return false; 120 } 121 122 bool create = false; 123 bool priv = false; 124 if (trimmed_str.data()[0] == '@') { 125 size_t offset = 1; 126 if (trimmed_str.data()[1] == '+') { 127 create = true; 128 offset += 1; 129 } 130 131 ResourceNameRef name; 132 if (!ParseResourceName( 133 trimmed_str.substr(offset, trimmed_str.size() - offset), &name, 134 &priv)) { 135 return false; 136 } 137 138 if (create && priv) { 139 return false; 140 } 141 142 if (create && name.type != ResourceType::kId) { 143 return false; 144 } 145 146 if (out_ref) { 147 *out_ref = name; 148 } 149 150 if (out_create) { 151 *out_create = create; 152 } 153 154 if (out_private) { 155 *out_private = priv; 156 } 157 return true; 158 } 159 return false; 160 } 161 162 bool IsReference(const StringPiece& str) { 163 return ParseReference(str, nullptr, nullptr, nullptr); 164 } 165 166 bool ParseAttributeReference(const StringPiece& str, ResourceNameRef* out_ref) { 167 StringPiece trimmed_str(util::TrimWhitespace(str)); 168 if (trimmed_str.empty()) { 169 return false; 170 } 171 172 if (*trimmed_str.data() == '?') { 173 StringPiece package; 174 StringPiece type; 175 StringPiece entry; 176 if (!android::ExtractResourceName(trimmed_str.substr(1, trimmed_str.size() - 1), &package, 177 &type, &entry)) { 178 return false; 179 } 180 181 if (!type.empty() && type != "attr") { 182 return false; 183 } 184 185 if (entry.empty()) { 186 return false; 187 } 188 189 if (out_ref) { 190 out_ref->package = package; 191 out_ref->type = ResourceType::kAttr; 192 out_ref->entry = entry; 193 } 194 return true; 195 } 196 return false; 197 } 198 199 bool IsAttributeReference(const StringPiece& str) { 200 return ParseAttributeReference(str, nullptr); 201 } 202 203 /* 204 * Style parent's are a bit different. We accept the following formats: 205 * 206 * @[[*]package:][style/]<entry> 207 * ?[[*]package:]style/<entry> 208 * <[*]package>:[style/]<entry> 209 * [[*]package:style/]<entry> 210 */ 211 Maybe<Reference> ParseStyleParentReference(const StringPiece& str, 212 std::string* out_error) { 213 if (str.empty()) { 214 return {}; 215 } 216 217 StringPiece name = str; 218 219 bool has_leading_identifiers = false; 220 bool private_ref = false; 221 222 // Skip over these identifiers. A style's parent is a normal reference. 223 if (name.data()[0] == '@' || name.data()[0] == '?') { 224 has_leading_identifiers = true; 225 name = name.substr(1, name.size() - 1); 226 } 227 228 if (name.data()[0] == '*') { 229 private_ref = true; 230 name = name.substr(1, name.size() - 1); 231 } 232 233 ResourceNameRef ref; 234 ref.type = ResourceType::kStyle; 235 236 StringPiece type_str; 237 android::ExtractResourceName(name, &ref.package, &type_str, &ref.entry); 238 if (!type_str.empty()) { 239 // If we have a type, make sure it is a Style. 240 const ResourceType* parsed_type = ParseResourceType(type_str); 241 if (!parsed_type || *parsed_type != ResourceType::kStyle) { 242 std::stringstream err; 243 err << "invalid resource type '" << type_str << "' for parent of style"; 244 *out_error = err.str(); 245 return {}; 246 } 247 } 248 249 if (!has_leading_identifiers && ref.package.empty() && !type_str.empty()) { 250 std::stringstream err; 251 err << "invalid parent reference '" << str << "'"; 252 *out_error = err.str(); 253 return {}; 254 } 255 256 Reference result(ref); 257 result.private_reference = private_ref; 258 return result; 259 } 260 261 Maybe<Reference> ParseXmlAttributeName(const StringPiece& str) { 262 StringPiece trimmed_str = util::TrimWhitespace(str); 263 const char* start = trimmed_str.data(); 264 const char* const end = start + trimmed_str.size(); 265 const char* p = start; 266 267 Reference ref; 268 if (p != end && *p == '*') { 269 ref.private_reference = true; 270 start++; 271 p++; 272 } 273 274 StringPiece package; 275 StringPiece name; 276 while (p != end) { 277 if (*p == ':') { 278 package = StringPiece(start, p - start); 279 name = StringPiece(p + 1, end - (p + 1)); 280 break; 281 } 282 p++; 283 } 284 285 ref.name = ResourceName(package, ResourceType::kAttr, name.empty() ? trimmed_str : name); 286 return Maybe<Reference>(std::move(ref)); 287 } 288 289 std::unique_ptr<Reference> TryParseReference(const StringPiece& str, 290 bool* out_create) { 291 ResourceNameRef ref; 292 bool private_ref = false; 293 if (ParseReference(str, &ref, out_create, &private_ref)) { 294 std::unique_ptr<Reference> value = util::make_unique<Reference>(ref); 295 value->private_reference = private_ref; 296 return value; 297 } 298 299 if (ParseAttributeReference(str, &ref)) { 300 if (out_create) { 301 *out_create = false; 302 } 303 return util::make_unique<Reference>(ref, Reference::Type::kAttribute); 304 } 305 return {}; 306 } 307 308 std::unique_ptr<Item> TryParseNullOrEmpty(const StringPiece& str) { 309 const StringPiece trimmed_str(util::TrimWhitespace(str)); 310 if (trimmed_str == "@null") { 311 return MakeNull(); 312 } else if (trimmed_str == "@empty") { 313 return MakeEmpty(); 314 } 315 return {}; 316 } 317 318 std::unique_ptr<Reference> MakeNull() { 319 // TYPE_NULL with data set to 0 is interpreted by the runtime as an error. 320 // Instead we set the data type to TYPE_REFERENCE with a value of 0. 321 return util::make_unique<Reference>(); 322 } 323 324 std::unique_ptr<BinaryPrimitive> MakeEmpty() { 325 return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_NULL, 326 android::Res_value::DATA_NULL_EMPTY); 327 } 328 329 std::unique_ptr<BinaryPrimitive> TryParseEnumSymbol(const Attribute* enum_attr, 330 const StringPiece& str) { 331 StringPiece trimmed_str(util::TrimWhitespace(str)); 332 for (const Attribute::Symbol& symbol : enum_attr->symbols) { 333 // Enum symbols are stored as @package:id/symbol resources, 334 // so we need to match against the 'entry' part of the identifier. 335 const ResourceName& enum_symbol_resource_name = symbol.symbol.name.value(); 336 if (trimmed_str == enum_symbol_resource_name.entry) { 337 android::Res_value value = {}; 338 value.dataType = android::Res_value::TYPE_INT_DEC; 339 value.data = symbol.value; 340 return util::make_unique<BinaryPrimitive>(value); 341 } 342 } 343 return {}; 344 } 345 346 std::unique_ptr<BinaryPrimitive> TryParseFlagSymbol(const Attribute* flag_attr, 347 const StringPiece& str) { 348 android::Res_value flags = {}; 349 flags.dataType = android::Res_value::TYPE_INT_HEX; 350 flags.data = 0u; 351 352 if (util::TrimWhitespace(str).empty()) { 353 // Empty string is a valid flag (0). 354 return util::make_unique<BinaryPrimitive>(flags); 355 } 356 357 for (StringPiece part : util::Tokenize(str, '|')) { 358 StringPiece trimmed_part = util::TrimWhitespace(part); 359 360 bool flag_set = false; 361 for (const Attribute::Symbol& symbol : flag_attr->symbols) { 362 // Flag symbols are stored as @package:id/symbol resources, 363 // so we need to match against the 'entry' part of the identifier. 364 const ResourceName& flag_symbol_resource_name = 365 symbol.symbol.name.value(); 366 if (trimmed_part == flag_symbol_resource_name.entry) { 367 flags.data |= symbol.value; 368 flag_set = true; 369 break; 370 } 371 } 372 373 if (!flag_set) { 374 return {}; 375 } 376 } 377 return util::make_unique<BinaryPrimitive>(flags); 378 } 379 380 static uint32_t ParseHex(char c, bool* out_error) { 381 if (c >= '0' && c <= '9') { 382 return c - '0'; 383 } else if (c >= 'a' && c <= 'f') { 384 return c - 'a' + 0xa; 385 } else if (c >= 'A' && c <= 'F') { 386 return c - 'A' + 0xa; 387 } else { 388 *out_error = true; 389 return 0xffffffffu; 390 } 391 } 392 393 std::unique_ptr<BinaryPrimitive> TryParseColor(const StringPiece& str) { 394 StringPiece color_str(util::TrimWhitespace(str)); 395 const char* start = color_str.data(); 396 const size_t len = color_str.size(); 397 if (len == 0 || start[0] != '#') { 398 return {}; 399 } 400 401 android::Res_value value = {}; 402 bool error = false; 403 if (len == 4) { 404 value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4; 405 value.data = 0xff000000u; 406 value.data |= ParseHex(start[1], &error) << 20; 407 value.data |= ParseHex(start[1], &error) << 16; 408 value.data |= ParseHex(start[2], &error) << 12; 409 value.data |= ParseHex(start[2], &error) << 8; 410 value.data |= ParseHex(start[3], &error) << 4; 411 value.data |= ParseHex(start[3], &error); 412 } else if (len == 5) { 413 value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4; 414 value.data |= ParseHex(start[1], &error) << 28; 415 value.data |= ParseHex(start[1], &error) << 24; 416 value.data |= ParseHex(start[2], &error) << 20; 417 value.data |= ParseHex(start[2], &error) << 16; 418 value.data |= ParseHex(start[3], &error) << 12; 419 value.data |= ParseHex(start[3], &error) << 8; 420 value.data |= ParseHex(start[4], &error) << 4; 421 value.data |= ParseHex(start[4], &error); 422 } else if (len == 7) { 423 value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8; 424 value.data = 0xff000000u; 425 value.data |= ParseHex(start[1], &error) << 20; 426 value.data |= ParseHex(start[2], &error) << 16; 427 value.data |= ParseHex(start[3], &error) << 12; 428 value.data |= ParseHex(start[4], &error) << 8; 429 value.data |= ParseHex(start[5], &error) << 4; 430 value.data |= ParseHex(start[6], &error); 431 } else if (len == 9) { 432 value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8; 433 value.data |= ParseHex(start[1], &error) << 28; 434 value.data |= ParseHex(start[2], &error) << 24; 435 value.data |= ParseHex(start[3], &error) << 20; 436 value.data |= ParseHex(start[4], &error) << 16; 437 value.data |= ParseHex(start[5], &error) << 12; 438 value.data |= ParseHex(start[6], &error) << 8; 439 value.data |= ParseHex(start[7], &error) << 4; 440 value.data |= ParseHex(start[8], &error); 441 } else { 442 return {}; 443 } 444 return error ? std::unique_ptr<BinaryPrimitive>() 445 : util::make_unique<BinaryPrimitive>(value); 446 } 447 448 Maybe<bool> ParseBool(const StringPiece& str) { 449 StringPiece trimmed_str(util::TrimWhitespace(str)); 450 if (trimmed_str == "true" || trimmed_str == "TRUE" || trimmed_str == "True") { 451 return Maybe<bool>(true); 452 } else if (trimmed_str == "false" || trimmed_str == "FALSE" || 453 trimmed_str == "False") { 454 return Maybe<bool>(false); 455 } 456 return {}; 457 } 458 459 Maybe<uint32_t> ParseInt(const StringPiece& str) { 460 std::u16string str16 = util::Utf8ToUtf16(str); 461 android::Res_value value; 462 if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) { 463 return value.data; 464 } 465 return {}; 466 } 467 468 Maybe<ResourceId> ParseResourceId(const StringPiece& str) { 469 StringPiece trimmed_str(util::TrimWhitespace(str)); 470 471 std::u16string str16 = util::Utf8ToUtf16(trimmed_str); 472 android::Res_value value; 473 if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) { 474 if (value.dataType == android::Res_value::TYPE_INT_HEX) { 475 ResourceId id(value.data); 476 if (id.is_valid_dynamic()) { 477 return id; 478 } 479 } 480 } 481 return {}; 482 } 483 484 Maybe<int> ParseSdkVersion(const StringPiece& str) { 485 StringPiece trimmed_str(util::TrimWhitespace(str)); 486 487 std::u16string str16 = util::Utf8ToUtf16(trimmed_str); 488 android::Res_value value; 489 if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) { 490 return static_cast<int>(value.data); 491 } 492 493 // Try parsing the code name. 494 std::pair<StringPiece, int> entry = GetDevelopmentSdkCodeNameAndVersion(); 495 if (entry.first == trimmed_str) { 496 return entry.second; 497 } 498 return {}; 499 } 500 501 std::unique_ptr<BinaryPrimitive> TryParseBool(const StringPiece& str) { 502 if (Maybe<bool> maybe_result = ParseBool(str)) { 503 const uint32_t data = maybe_result.value() ? 0xffffffffu : 0u; 504 return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_BOOLEAN, data); 505 } 506 return {}; 507 } 508 509 std::unique_ptr<BinaryPrimitive> MakeBool(bool val) { 510 return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_BOOLEAN, 511 val ? 0xffffffffu : 0u); 512 } 513 514 std::unique_ptr<BinaryPrimitive> TryParseInt(const StringPiece& str) { 515 std::u16string str16 = util::Utf8ToUtf16(str); 516 android::Res_value value; 517 if (!android::ResTable::stringToInt(str16.data(), str16.size(), &value)) { 518 return {}; 519 } 520 return util::make_unique<BinaryPrimitive>(value); 521 } 522 523 std::unique_ptr<BinaryPrimitive> TryParseFloat(const StringPiece& str) { 524 std::u16string str16 = util::Utf8ToUtf16(str); 525 android::Res_value value; 526 if (!android::ResTable::stringToFloat(str16.data(), str16.size(), &value)) { 527 return {}; 528 } 529 return util::make_unique<BinaryPrimitive>(value); 530 } 531 532 uint32_t AndroidTypeToAttributeTypeMask(uint16_t type) { 533 switch (type) { 534 case android::Res_value::TYPE_NULL: 535 case android::Res_value::TYPE_REFERENCE: 536 case android::Res_value::TYPE_ATTRIBUTE: 537 case android::Res_value::TYPE_DYNAMIC_REFERENCE: 538 case android::Res_value::TYPE_DYNAMIC_ATTRIBUTE: 539 return android::ResTable_map::TYPE_REFERENCE; 540 541 case android::Res_value::TYPE_STRING: 542 return android::ResTable_map::TYPE_STRING; 543 544 case android::Res_value::TYPE_FLOAT: 545 return android::ResTable_map::TYPE_FLOAT; 546 547 case android::Res_value::TYPE_DIMENSION: 548 return android::ResTable_map::TYPE_DIMENSION; 549 550 case android::Res_value::TYPE_FRACTION: 551 return android::ResTable_map::TYPE_FRACTION; 552 553 case android::Res_value::TYPE_INT_DEC: 554 case android::Res_value::TYPE_INT_HEX: 555 return android::ResTable_map::TYPE_INTEGER | 556 android::ResTable_map::TYPE_ENUM | 557 android::ResTable_map::TYPE_FLAGS; 558 559 case android::Res_value::TYPE_INT_BOOLEAN: 560 return android::ResTable_map::TYPE_BOOLEAN; 561 562 case android::Res_value::TYPE_INT_COLOR_ARGB8: 563 case android::Res_value::TYPE_INT_COLOR_RGB8: 564 case android::Res_value::TYPE_INT_COLOR_ARGB4: 565 case android::Res_value::TYPE_INT_COLOR_RGB4: 566 return android::ResTable_map::TYPE_COLOR; 567 568 default: 569 return 0; 570 }; 571 } 572 573 std::unique_ptr<Item> TryParseItemForAttribute( 574 const StringPiece& value, uint32_t type_mask, 575 const std::function<void(const ResourceName&)>& on_create_reference) { 576 using android::ResTable_map; 577 578 auto null_or_empty = TryParseNullOrEmpty(value); 579 if (null_or_empty) { 580 return null_or_empty; 581 } 582 583 bool create = false; 584 auto reference = TryParseReference(value, &create); 585 if (reference) { 586 if (create && on_create_reference) { 587 on_create_reference(reference->name.value()); 588 } 589 return std::move(reference); 590 } 591 592 if (type_mask & ResTable_map::TYPE_COLOR) { 593 // Try parsing this as a color. 594 auto color = TryParseColor(value); 595 if (color) { 596 return std::move(color); 597 } 598 } 599 600 if (type_mask & ResTable_map::TYPE_BOOLEAN) { 601 // Try parsing this as a boolean. 602 auto boolean = TryParseBool(value); 603 if (boolean) { 604 return std::move(boolean); 605 } 606 } 607 608 if (type_mask & ResTable_map::TYPE_INTEGER) { 609 // Try parsing this as an integer. 610 auto integer = TryParseInt(value); 611 if (integer) { 612 return std::move(integer); 613 } 614 } 615 616 const uint32_t float_mask = 617 ResTable_map::TYPE_FLOAT | ResTable_map::TYPE_DIMENSION | ResTable_map::TYPE_FRACTION; 618 if (type_mask & float_mask) { 619 // Try parsing this as a float. 620 auto floating_point = TryParseFloat(value); 621 if (floating_point) { 622 if (type_mask & AndroidTypeToAttributeTypeMask(floating_point->value.dataType)) { 623 return std::move(floating_point); 624 } 625 } 626 } 627 return {}; 628 } 629 630 /** 631 * We successively try to parse the string as a resource type that the Attribute 632 * allows. 633 */ 634 std::unique_ptr<Item> TryParseItemForAttribute( 635 const StringPiece& str, const Attribute* attr, 636 const std::function<void(const ResourceName&)>& on_create_reference) { 637 using android::ResTable_map; 638 639 const uint32_t type_mask = attr->type_mask; 640 auto value = TryParseItemForAttribute(str, type_mask, on_create_reference); 641 if (value) { 642 return value; 643 } 644 645 if (type_mask & ResTable_map::TYPE_ENUM) { 646 // Try parsing this as an enum. 647 auto enum_value = TryParseEnumSymbol(attr, str); 648 if (enum_value) { 649 return std::move(enum_value); 650 } 651 } 652 653 if (type_mask & ResTable_map::TYPE_FLAGS) { 654 // Try parsing this as a flag. 655 auto flag_value = TryParseFlagSymbol(attr, str); 656 if (flag_value) { 657 return std::move(flag_value); 658 } 659 } 660 return {}; 661 } 662 663 std::string BuildResourceFileName(const ResourceFile& res_file, const NameMangler* mangler) { 664 std::stringstream out; 665 out << "res/" << res_file.name.type; 666 if (res_file.config != ConfigDescription{}) { 667 out << "-" << res_file.config; 668 } 669 out << "/"; 670 671 if (mangler && mangler->ShouldMangle(res_file.name.package)) { 672 out << NameMangler::MangleEntry(res_file.name.package, res_file.name.entry); 673 } else { 674 out << res_file.name.entry; 675 } 676 out << file::GetExtension(res_file.source.path); 677 return out.str(); 678 } 679 680 std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const ConfigDescription& config, 681 const android::ResStringPool& src_pool, 682 const android::Res_value& res_value, 683 StringPool* dst_pool) { 684 if (type == ResourceType::kId) { 685 return util::make_unique<Id>(); 686 } 687 688 const uint32_t data = util::DeviceToHost32(res_value.data); 689 switch (res_value.dataType) { 690 case android::Res_value::TYPE_STRING: { 691 const std::string str = util::GetString(src_pool, data); 692 const android::ResStringPool_span* spans = src_pool.styleAt(data); 693 694 // Check if the string has a valid style associated with it. 695 if (spans != nullptr && spans->name.index != android::ResStringPool_span::END) { 696 StyleString style_str = {str}; 697 while (spans->name.index != android::ResStringPool_span::END) { 698 style_str.spans.push_back(Span{util::GetString(src_pool, spans->name.index), 699 spans->firstChar, spans->lastChar}); 700 spans++; 701 } 702 return util::make_unique<StyledString>(dst_pool->MakeRef( 703 style_str, StringPool::Context(StringPool::Context::kStylePriority, config))); 704 } else { 705 if (type != ResourceType::kString && util::StartsWith(str, "res/")) { 706 // This must be a FileReference. 707 return util::make_unique<FileReference>(dst_pool->MakeRef( 708 str, StringPool::Context(StringPool::Context::kHighPriority, config))); 709 } 710 711 // There are no styles associated with this string, so treat it as a simple string. 712 return util::make_unique<String>(dst_pool->MakeRef(str, StringPool::Context(config))); 713 } 714 } break; 715 716 case android::Res_value::TYPE_REFERENCE: 717 case android::Res_value::TYPE_ATTRIBUTE: 718 case android::Res_value::TYPE_DYNAMIC_REFERENCE: 719 case android::Res_value::TYPE_DYNAMIC_ATTRIBUTE: { 720 Reference::Type ref_type = Reference::Type::kResource; 721 if (res_value.dataType == android::Res_value::TYPE_ATTRIBUTE || 722 res_value.dataType == android::Res_value::TYPE_DYNAMIC_ATTRIBUTE) { 723 ref_type = Reference::Type::kAttribute; 724 } 725 726 if (data == 0u) { 727 // A reference of 0, must be the magic @null reference. 728 return util::make_unique<Reference>(); 729 } 730 731 // This is a normal reference. 732 return util::make_unique<Reference>(data, ref_type); 733 } break; 734 } 735 736 // Treat this as a raw binary primitive. 737 return util::make_unique<BinaryPrimitive>(res_value); 738 } 739 740 } // namespace ResourceUtils 741 } // namespace aapt 742