Home | History | Annotate | Download | only in aapt2
      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