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 
extractResourceName(const StringPiece16 & str,StringPiece16 * outPackage,StringPiece16 * outType,StringPiece16 * outEntry)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 
parseResourceName(const StringPiece16 & str,ResourceNameRef * outRef,bool * outPrivate)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 
tryParseReference(const StringPiece16 & str,ResourceNameRef * outRef,bool * outCreate,bool * outPrivate)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 
isReference(const StringPiece16 & str)139 bool isReference(const StringPiece16& str) {
140     return tryParseReference(str, nullptr, nullptr, nullptr);
141 }
142 
tryParseAttributeReference(const StringPiece16 & str,ResourceNameRef * outRef)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 
isAttributeReference(const StringPiece16 & str)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  */
parseStyleParentReference(const StringPiece16 & str,std::string * outError)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 
tryParseReference(const StringPiece16 & str,bool * outCreate)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 
tryParseNullOrEmpty(const StringPiece16 & str)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 
tryParseEnumSymbol(const Attribute * enumAttr,const StringPiece16 & str)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 
tryParseFlagSymbol(const Attribute * flagAttr,const StringPiece16 & str)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 
parseHex(char16_t c,bool * outError)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 
tryParseColor(const StringPiece16 & str)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 
tryParseBool(const StringPiece16 & str,bool * outValue)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 
tryParseBool(const StringPiece16 & str)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 
tryParseInt(const StringPiece16 & str)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 
tryParseFloat(const StringPiece16 & str)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 
androidTypeToAttributeTypeMask(uint16_t type)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 
parseItemForAttribute(const StringPiece16 & value,uint32_t typeMask,std::function<void (const ResourceName &)> onCreateReference)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  */
parseItemForAttribute(const StringPiece16 & str,const Attribute * attr,std::function<void (const ResourceName &)> onCreateReference)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 
buildResourceFileName(const ResourceFile & resFile,const NameMangler * mangler)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