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 "android-base/stringprintf.h"
22 #include "androidfw/ResourceTypes.h"
23 #include "androidfw/ResourceUtils.h"
24 
25 #include "NameMangler.h"
26 #include "SdkConstants.h"
27 #include "format/binary/ResourceTypeExtensions.h"
28 #include "text/Unicode.h"
29 #include "text/Utf8Iterator.h"
30 #include "util/Files.h"
31 #include "util/Util.h"
32 
33 using ::aapt::text::Utf8Iterator;
34 using ::android::StringPiece;
35 using ::android::StringPiece16;
36 using ::android::base::StringPrintf;
37 
38 namespace aapt {
39 namespace ResourceUtils {
40 
ToResourceName(const android::ResTable::resource_name & name_in)41 Maybe<ResourceName> ToResourceName(
42     const android::ResTable::resource_name& name_in) {
43   ResourceName name_out;
44   if (!name_in.package) {
45     return {};
46   }
47 
48   name_out.package =
49       util::Utf16ToUtf8(StringPiece16(name_in.package, name_in.packageLen));
50 
51   const ResourceType* type;
52   if (name_in.type) {
53     type = ParseResourceType(
54         util::Utf16ToUtf8(StringPiece16(name_in.type, name_in.typeLen)));
55   } else if (name_in.type8) {
56     type = ParseResourceType(StringPiece(name_in.type8, name_in.typeLen));
57   } else {
58     return {};
59   }
60 
61   if (!type) {
62     return {};
63   }
64 
65   name_out.type = *type;
66 
67   if (name_in.name) {
68     name_out.entry =
69         util::Utf16ToUtf8(StringPiece16(name_in.name, name_in.nameLen));
70   } else if (name_in.name8) {
71     name_out.entry.assign(name_in.name8, name_in.nameLen);
72   } else {
73     return {};
74   }
75   return name_out;
76 }
77 
ParseResourceName(const StringPiece & str,ResourceNameRef * out_ref,bool * out_private)78 bool ParseResourceName(const StringPiece& str, ResourceNameRef* out_ref,
79                        bool* out_private) {
80   if (str.empty()) {
81     return false;
82   }
83 
84   size_t offset = 0;
85   bool priv = false;
86   if (str.data()[0] == '*') {
87     priv = true;
88     offset = 1;
89   }
90 
91   StringPiece package;
92   StringPiece type;
93   StringPiece entry;
94   if (!android::ExtractResourceName(str.substr(offset, str.size() - offset), &package, &type,
95                                     &entry)) {
96     return false;
97   }
98 
99   const ResourceType* parsed_type = ParseResourceType(type);
100   if (!parsed_type) {
101     return false;
102   }
103 
104   if (entry.empty()) {
105     return false;
106   }
107 
108   if (out_ref) {
109     out_ref->package = package;
110     out_ref->type = *parsed_type;
111     out_ref->entry = entry;
112   }
113 
114   if (out_private) {
115     *out_private = priv;
116   }
117   return true;
118 }
119 
ParseReference(const StringPiece & str,ResourceNameRef * out_ref,bool * out_create,bool * out_private)120 bool ParseReference(const StringPiece& str, ResourceNameRef* out_ref,
121                     bool* out_create, bool* out_private) {
122   StringPiece trimmed_str(util::TrimWhitespace(str));
123   if (trimmed_str.empty()) {
124     return false;
125   }
126 
127   bool create = false;
128   bool priv = false;
129   if (trimmed_str.data()[0] == '@') {
130     size_t offset = 1;
131     if (trimmed_str.data()[1] == '+') {
132       create = true;
133       offset += 1;
134     }
135 
136     ResourceNameRef name;
137     if (!ParseResourceName(
138             trimmed_str.substr(offset, trimmed_str.size() - offset), &name,
139             &priv)) {
140       return false;
141     }
142 
143     if (create && priv) {
144       return false;
145     }
146 
147     if (create && name.type != ResourceType::kId) {
148       return false;
149     }
150 
151     if (out_ref) {
152       *out_ref = name;
153     }
154 
155     if (out_create) {
156       *out_create = create;
157     }
158 
159     if (out_private) {
160       *out_private = priv;
161     }
162     return true;
163   }
164   return false;
165 }
166 
IsReference(const StringPiece & str)167 bool IsReference(const StringPiece& str) {
168   return ParseReference(str, nullptr, nullptr, nullptr);
169 }
170 
ParseAttributeReference(const StringPiece & str,ResourceNameRef * out_ref)171 bool ParseAttributeReference(const StringPiece& str, ResourceNameRef* out_ref) {
172   StringPiece trimmed_str(util::TrimWhitespace(str));
173   if (trimmed_str.empty()) {
174     return false;
175   }
176 
177   if (*trimmed_str.data() == '?') {
178     StringPiece package;
179     StringPiece type;
180     StringPiece entry;
181     if (!android::ExtractResourceName(trimmed_str.substr(1, trimmed_str.size() - 1), &package,
182                                       &type, &entry)) {
183       return false;
184     }
185 
186     if (!type.empty() && type != "attr") {
187       return false;
188     }
189 
190     if (entry.empty()) {
191       return false;
192     }
193 
194     if (out_ref) {
195       out_ref->package = package;
196       out_ref->type = ResourceType::kAttr;
197       out_ref->entry = entry;
198     }
199     return true;
200   }
201   return false;
202 }
203 
IsAttributeReference(const StringPiece & str)204 bool IsAttributeReference(const StringPiece& str) {
205   return ParseAttributeReference(str, nullptr);
206 }
207 
208 /*
209  * Style parent's are a bit different. We accept the following formats:
210  *
211  * @[[*]package:][style/]<entry>
212  * ?[[*]package:]style/<entry>
213  * <[*]package>:[style/]<entry>
214  * [[*]package:style/]<entry>
215  */
ParseStyleParentReference(const StringPiece & str,std::string * out_error)216 Maybe<Reference> ParseStyleParentReference(const StringPiece& str,
217                                            std::string* out_error) {
218   if (str.empty()) {
219     return {};
220   }
221 
222   StringPiece name = str;
223 
224   bool has_leading_identifiers = false;
225   bool private_ref = false;
226 
227   // Skip over these identifiers. A style's parent is a normal reference.
228   if (name.data()[0] == '@' || name.data()[0] == '?') {
229     has_leading_identifiers = true;
230     name = name.substr(1, name.size() - 1);
231   }
232 
233   if (name.data()[0] == '*') {
234     private_ref = true;
235     name = name.substr(1, name.size() - 1);
236   }
237 
238   ResourceNameRef ref;
239   ref.type = ResourceType::kStyle;
240 
241   StringPiece type_str;
242   android::ExtractResourceName(name, &ref.package, &type_str, &ref.entry);
243   if (!type_str.empty()) {
244     // If we have a type, make sure it is a Style.
245     const ResourceType* parsed_type = ParseResourceType(type_str);
246     if (!parsed_type || *parsed_type != ResourceType::kStyle) {
247       std::stringstream err;
248       err << "invalid resource type '" << type_str << "' for parent of style";
249       *out_error = err.str();
250       return {};
251     }
252   }
253 
254   if (!has_leading_identifiers && ref.package.empty() && !type_str.empty()) {
255     std::stringstream err;
256     err << "invalid parent reference '" << str << "'";
257     *out_error = err.str();
258     return {};
259   }
260 
261   Reference result(ref);
262   result.private_reference = private_ref;
263   return result;
264 }
265 
ParseXmlAttributeName(const StringPiece & str)266 Maybe<Reference> ParseXmlAttributeName(const StringPiece& str) {
267   StringPiece trimmed_str = util::TrimWhitespace(str);
268   const char* start = trimmed_str.data();
269   const char* const end = start + trimmed_str.size();
270   const char* p = start;
271 
272   Reference ref;
273   if (p != end && *p == '*') {
274     ref.private_reference = true;
275     start++;
276     p++;
277   }
278 
279   StringPiece package;
280   StringPiece name;
281   while (p != end) {
282     if (*p == ':') {
283       package = StringPiece(start, p - start);
284       name = StringPiece(p + 1, end - (p + 1));
285       break;
286     }
287     p++;
288   }
289 
290   ref.name = ResourceName(package, ResourceType::kAttr, name.empty() ? trimmed_str : name);
291   return Maybe<Reference>(std::move(ref));
292 }
293 
TryParseReference(const StringPiece & str,bool * out_create)294 std::unique_ptr<Reference> TryParseReference(const StringPiece& str,
295                                              bool* out_create) {
296   ResourceNameRef ref;
297   bool private_ref = false;
298   if (ParseReference(str, &ref, out_create, &private_ref)) {
299     std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
300     value->private_reference = private_ref;
301     return value;
302   }
303 
304   if (ParseAttributeReference(str, &ref)) {
305     if (out_create) {
306       *out_create = false;
307     }
308     return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
309   }
310   return {};
311 }
312 
TryParseNullOrEmpty(const StringPiece & str)313 std::unique_ptr<Item> TryParseNullOrEmpty(const StringPiece& str) {
314   const StringPiece trimmed_str(util::TrimWhitespace(str));
315   if (trimmed_str == "@null") {
316     return MakeNull();
317   } else if (trimmed_str == "@empty") {
318     return MakeEmpty();
319   }
320   return {};
321 }
322 
MakeNull()323 std::unique_ptr<Reference> MakeNull() {
324   // TYPE_NULL with data set to 0 is interpreted by the runtime as an error.
325   // Instead we set the data type to TYPE_REFERENCE with a value of 0.
326   return util::make_unique<Reference>();
327 }
328 
MakeEmpty()329 std::unique_ptr<BinaryPrimitive> MakeEmpty() {
330   return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_NULL,
331                                             android::Res_value::DATA_NULL_EMPTY);
332 }
333 
TryParseEnumSymbol(const Attribute * enum_attr,const StringPiece & str)334 std::unique_ptr<BinaryPrimitive> TryParseEnumSymbol(const Attribute* enum_attr,
335                                                     const StringPiece& str) {
336   StringPiece trimmed_str(util::TrimWhitespace(str));
337   for (const Attribute::Symbol& symbol : enum_attr->symbols) {
338     // Enum symbols are stored as @package:id/symbol resources,
339     // so we need to match against the 'entry' part of the identifier.
340     const ResourceName& enum_symbol_resource_name = symbol.symbol.name.value();
341     if (trimmed_str == enum_symbol_resource_name.entry) {
342       android::Res_value value = {};
343       value.dataType = android::Res_value::TYPE_INT_DEC;
344       value.data = symbol.value;
345       return util::make_unique<BinaryPrimitive>(value);
346     }
347   }
348   return {};
349 }
350 
TryParseFlagSymbol(const Attribute * flag_attr,const StringPiece & str)351 std::unique_ptr<BinaryPrimitive> TryParseFlagSymbol(const Attribute* flag_attr,
352                                                     const StringPiece& str) {
353   android::Res_value flags = {};
354   flags.dataType = android::Res_value::TYPE_INT_HEX;
355   flags.data = 0u;
356 
357   if (util::TrimWhitespace(str).empty()) {
358     // Empty string is a valid flag (0).
359     return util::make_unique<BinaryPrimitive>(flags);
360   }
361 
362   for (StringPiece part : util::Tokenize(str, '|')) {
363     StringPiece trimmed_part = util::TrimWhitespace(part);
364 
365     bool flag_set = false;
366     for (const Attribute::Symbol& symbol : flag_attr->symbols) {
367       // Flag symbols are stored as @package:id/symbol resources,
368       // so we need to match against the 'entry' part of the identifier.
369       const ResourceName& flag_symbol_resource_name =
370           symbol.symbol.name.value();
371       if (trimmed_part == flag_symbol_resource_name.entry) {
372         flags.data |= symbol.value;
373         flag_set = true;
374         break;
375       }
376     }
377 
378     if (!flag_set) {
379       return {};
380     }
381   }
382   return util::make_unique<BinaryPrimitive>(flags);
383 }
384 
ParseHex(char c,bool * out_error)385 static uint32_t ParseHex(char c, bool* out_error) {
386   if (c >= '0' && c <= '9') {
387     return c - '0';
388   } else if (c >= 'a' && c <= 'f') {
389     return c - 'a' + 0xa;
390   } else if (c >= 'A' && c <= 'F') {
391     return c - 'A' + 0xa;
392   } else {
393     *out_error = true;
394     return 0xffffffffu;
395   }
396 }
397 
TryParseColor(const StringPiece & str)398 std::unique_ptr<BinaryPrimitive> TryParseColor(const StringPiece& str) {
399   StringPiece color_str(util::TrimWhitespace(str));
400   const char* start = color_str.data();
401   const size_t len = color_str.size();
402   if (len == 0 || start[0] != '#') {
403     return {};
404   }
405 
406   android::Res_value value = {};
407   bool error = false;
408   if (len == 4) {
409     value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
410     value.data = 0xff000000u;
411     value.data |= ParseHex(start[1], &error) << 20;
412     value.data |= ParseHex(start[1], &error) << 16;
413     value.data |= ParseHex(start[2], &error) << 12;
414     value.data |= ParseHex(start[2], &error) << 8;
415     value.data |= ParseHex(start[3], &error) << 4;
416     value.data |= ParseHex(start[3], &error);
417   } else if (len == 5) {
418     value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4;
419     value.data |= ParseHex(start[1], &error) << 28;
420     value.data |= ParseHex(start[1], &error) << 24;
421     value.data |= ParseHex(start[2], &error) << 20;
422     value.data |= ParseHex(start[2], &error) << 16;
423     value.data |= ParseHex(start[3], &error) << 12;
424     value.data |= ParseHex(start[3], &error) << 8;
425     value.data |= ParseHex(start[4], &error) << 4;
426     value.data |= ParseHex(start[4], &error);
427   } else if (len == 7) {
428     value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8;
429     value.data = 0xff000000u;
430     value.data |= ParseHex(start[1], &error) << 20;
431     value.data |= ParseHex(start[2], &error) << 16;
432     value.data |= ParseHex(start[3], &error) << 12;
433     value.data |= ParseHex(start[4], &error) << 8;
434     value.data |= ParseHex(start[5], &error) << 4;
435     value.data |= ParseHex(start[6], &error);
436   } else if (len == 9) {
437     value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8;
438     value.data |= ParseHex(start[1], &error) << 28;
439     value.data |= ParseHex(start[2], &error) << 24;
440     value.data |= ParseHex(start[3], &error) << 20;
441     value.data |= ParseHex(start[4], &error) << 16;
442     value.data |= ParseHex(start[5], &error) << 12;
443     value.data |= ParseHex(start[6], &error) << 8;
444     value.data |= ParseHex(start[7], &error) << 4;
445     value.data |= ParseHex(start[8], &error);
446   } else {
447     return {};
448   }
449   return error ? std::unique_ptr<BinaryPrimitive>()
450                : util::make_unique<BinaryPrimitive>(value);
451 }
452 
ParseBool(const StringPiece & str)453 Maybe<bool> ParseBool(const StringPiece& str) {
454   StringPiece trimmed_str(util::TrimWhitespace(str));
455   if (trimmed_str == "true" || trimmed_str == "TRUE" || trimmed_str == "True") {
456     return Maybe<bool>(true);
457   } else if (trimmed_str == "false" || trimmed_str == "FALSE" ||
458              trimmed_str == "False") {
459     return Maybe<bool>(false);
460   }
461   return {};
462 }
463 
ParseInt(const StringPiece & str)464 Maybe<uint32_t> ParseInt(const StringPiece& str) {
465   std::u16string str16 = util::Utf8ToUtf16(str);
466   android::Res_value value;
467   if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
468     return value.data;
469   }
470   return {};
471 }
472 
ParseResourceId(const StringPiece & str)473 Maybe<ResourceId> ParseResourceId(const StringPiece& str) {
474   StringPiece trimmed_str(util::TrimWhitespace(str));
475 
476   std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
477   android::Res_value value;
478   if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
479     if (value.dataType == android::Res_value::TYPE_INT_HEX) {
480       ResourceId id(value.data);
481       if (id.is_valid_dynamic()) {
482         return id;
483       }
484     }
485   }
486   return {};
487 }
488 
ParseSdkVersion(const StringPiece & str)489 Maybe<int> ParseSdkVersion(const StringPiece& str) {
490   StringPiece trimmed_str(util::TrimWhitespace(str));
491 
492   std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
493   android::Res_value value;
494   if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
495     return static_cast<int>(value.data);
496   }
497 
498   // Try parsing the code name.
499   std::pair<StringPiece, int> entry = GetDevelopmentSdkCodeNameAndVersion();
500   if (entry.first == trimmed_str) {
501     return entry.second;
502   }
503   return {};
504 }
505 
TryParseBool(const StringPiece & str)506 std::unique_ptr<BinaryPrimitive> TryParseBool(const StringPiece& str) {
507   if (Maybe<bool> maybe_result = ParseBool(str)) {
508     const uint32_t data = maybe_result.value() ? 0xffffffffu : 0u;
509     return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_BOOLEAN, data);
510   }
511   return {};
512 }
513 
MakeBool(bool val)514 std::unique_ptr<BinaryPrimitive> MakeBool(bool val) {
515   return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_BOOLEAN,
516                                             val ? 0xffffffffu : 0u);
517 }
518 
TryParseInt(const StringPiece & str)519 std::unique_ptr<BinaryPrimitive> TryParseInt(const StringPiece& str) {
520   std::u16string str16 = util::Utf8ToUtf16(util::TrimWhitespace(str));
521   android::Res_value value;
522   if (!android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
523     return {};
524   }
525   return util::make_unique<BinaryPrimitive>(value);
526 }
527 
MakeInt(uint32_t val)528 std::unique_ptr<BinaryPrimitive> MakeInt(uint32_t val) {
529   return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_DEC, val);
530 }
531 
TryParseFloat(const StringPiece & str)532 std::unique_ptr<BinaryPrimitive> TryParseFloat(const StringPiece& str) {
533   std::u16string str16 = util::Utf8ToUtf16(util::TrimWhitespace(str));
534   android::Res_value value;
535   if (!android::ResTable::stringToFloat(str16.data(), str16.size(), &value)) {
536     return {};
537   }
538   return util::make_unique<BinaryPrimitive>(value);
539 }
540 
AndroidTypeToAttributeTypeMask(uint16_t type)541 uint32_t AndroidTypeToAttributeTypeMask(uint16_t type) {
542   switch (type) {
543     case android::Res_value::TYPE_NULL:
544     case android::Res_value::TYPE_REFERENCE:
545     case android::Res_value::TYPE_ATTRIBUTE:
546     case android::Res_value::TYPE_DYNAMIC_REFERENCE:
547     case android::Res_value::TYPE_DYNAMIC_ATTRIBUTE:
548       return android::ResTable_map::TYPE_REFERENCE;
549 
550     case android::Res_value::TYPE_STRING:
551       return android::ResTable_map::TYPE_STRING;
552 
553     case android::Res_value::TYPE_FLOAT:
554       return android::ResTable_map::TYPE_FLOAT;
555 
556     case android::Res_value::TYPE_DIMENSION:
557       return android::ResTable_map::TYPE_DIMENSION;
558 
559     case android::Res_value::TYPE_FRACTION:
560       return android::ResTable_map::TYPE_FRACTION;
561 
562     case android::Res_value::TYPE_INT_DEC:
563     case android::Res_value::TYPE_INT_HEX:
564       return android::ResTable_map::TYPE_INTEGER |
565              android::ResTable_map::TYPE_ENUM |
566              android::ResTable_map::TYPE_FLAGS;
567 
568     case android::Res_value::TYPE_INT_BOOLEAN:
569       return android::ResTable_map::TYPE_BOOLEAN;
570 
571     case android::Res_value::TYPE_INT_COLOR_ARGB8:
572     case android::Res_value::TYPE_INT_COLOR_RGB8:
573     case android::Res_value::TYPE_INT_COLOR_ARGB4:
574     case android::Res_value::TYPE_INT_COLOR_RGB4:
575       return android::ResTable_map::TYPE_COLOR;
576 
577     default:
578       return 0;
579   };
580 }
581 
TryParseItemForAttribute(const StringPiece & value,uint32_t type_mask,const std::function<void (const ResourceName &)> & on_create_reference)582 std::unique_ptr<Item> TryParseItemForAttribute(
583     const StringPiece& value, uint32_t type_mask,
584     const std::function<void(const ResourceName&)>& on_create_reference) {
585   using android::ResTable_map;
586 
587   auto null_or_empty = TryParseNullOrEmpty(value);
588   if (null_or_empty) {
589     return null_or_empty;
590   }
591 
592   bool create = false;
593   auto reference = TryParseReference(value, &create);
594   if (reference) {
595     if (create && on_create_reference) {
596       on_create_reference(reference->name.value());
597     }
598     return std::move(reference);
599   }
600 
601   if (type_mask & ResTable_map::TYPE_COLOR) {
602     // Try parsing this as a color.
603     auto color = TryParseColor(value);
604     if (color) {
605       return std::move(color);
606     }
607   }
608 
609   if (type_mask & ResTable_map::TYPE_BOOLEAN) {
610     // Try parsing this as a boolean.
611     auto boolean = TryParseBool(value);
612     if (boolean) {
613       return std::move(boolean);
614     }
615   }
616 
617   if (type_mask & ResTable_map::TYPE_INTEGER) {
618     // Try parsing this as an integer.
619     auto integer = TryParseInt(value);
620     if (integer) {
621       return std::move(integer);
622     }
623   }
624 
625   const uint32_t float_mask =
626       ResTable_map::TYPE_FLOAT | ResTable_map::TYPE_DIMENSION | ResTable_map::TYPE_FRACTION;
627   if (type_mask & float_mask) {
628     // Try parsing this as a float.
629     auto floating_point = TryParseFloat(value);
630     if (floating_point) {
631       if (type_mask & AndroidTypeToAttributeTypeMask(floating_point->value.dataType)) {
632         return std::move(floating_point);
633       }
634     }
635   }
636   return {};
637 }
638 
639 /**
640  * We successively try to parse the string as a resource type that the Attribute
641  * allows.
642  */
TryParseItemForAttribute(const StringPiece & str,const Attribute * attr,const std::function<void (const ResourceName &)> & on_create_reference)643 std::unique_ptr<Item> TryParseItemForAttribute(
644     const StringPiece& str, const Attribute* attr,
645     const std::function<void(const ResourceName&)>& on_create_reference) {
646   using android::ResTable_map;
647 
648   const uint32_t type_mask = attr->type_mask;
649   auto value = TryParseItemForAttribute(str, type_mask, on_create_reference);
650   if (value) {
651     return value;
652   }
653 
654   if (type_mask & ResTable_map::TYPE_ENUM) {
655     // Try parsing this as an enum.
656     auto enum_value = TryParseEnumSymbol(attr, str);
657     if (enum_value) {
658       return std::move(enum_value);
659     }
660   }
661 
662   if (type_mask & ResTable_map::TYPE_FLAGS) {
663     // Try parsing this as a flag.
664     auto flag_value = TryParseFlagSymbol(attr, str);
665     if (flag_value) {
666       return std::move(flag_value);
667     }
668   }
669   return {};
670 }
671 
BuildResourceFileName(const ResourceFile & res_file,const NameMangler * mangler)672 std::string BuildResourceFileName(const ResourceFile& res_file, const NameMangler* mangler) {
673   std::stringstream out;
674   out << "res/" << res_file.name.type;
675   if (res_file.config != ConfigDescription{}) {
676     out << "-" << res_file.config;
677   }
678   out << "/";
679 
680   if (mangler && mangler->ShouldMangle(res_file.name.package)) {
681     out << NameMangler::MangleEntry(res_file.name.package, res_file.name.entry);
682   } else {
683     out << res_file.name.entry;
684   }
685   out << file::GetExtension(res_file.source.path);
686   return out.str();
687 }
688 
ParseBinaryResValue(const ResourceType & type,const ConfigDescription & config,const android::ResStringPool & src_pool,const android::Res_value & res_value,StringPool * dst_pool)689 std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const ConfigDescription& config,
690                                           const android::ResStringPool& src_pool,
691                                           const android::Res_value& res_value,
692                                           StringPool* dst_pool) {
693   if (type == ResourceType::kId) {
694     return util::make_unique<Id>();
695   }
696 
697   const uint32_t data = util::DeviceToHost32(res_value.data);
698   switch (res_value.dataType) {
699     case android::Res_value::TYPE_STRING: {
700       const std::string str = util::GetString(src_pool, data);
701       const android::ResStringPool_span* spans = src_pool.styleAt(data);
702 
703       // Check if the string has a valid style associated with it.
704       if (spans != nullptr && spans->name.index != android::ResStringPool_span::END) {
705         StyleString style_str = {str};
706         while (spans->name.index != android::ResStringPool_span::END) {
707           style_str.spans.push_back(Span{util::GetString(src_pool, spans->name.index),
708                                          spans->firstChar, spans->lastChar});
709           spans++;
710         }
711         return util::make_unique<StyledString>(dst_pool->MakeRef(
712             style_str, StringPool::Context(StringPool::Context::kNormalPriority, config)));
713       } else {
714         if (type != ResourceType::kString && util::StartsWith(str, "res/")) {
715           // This must be a FileReference.
716           std::unique_ptr<FileReference> file_ref =
717               util::make_unique<FileReference>(dst_pool->MakeRef(
718                   str, StringPool::Context(StringPool::Context::kHighPriority, config)));
719           if (type == ResourceType::kRaw) {
720             file_ref->type = ResourceFile::Type::kUnknown;
721           } else if (util::EndsWith(*file_ref->path, ".xml")) {
722             file_ref->type = ResourceFile::Type::kBinaryXml;
723           } else if (util::EndsWith(*file_ref->path, ".png")) {
724             file_ref->type = ResourceFile::Type::kPng;
725           }
726           return std::move(file_ref);
727         }
728 
729         // There are no styles associated with this string, so treat it as a simple string.
730         return util::make_unique<String>(dst_pool->MakeRef(str, StringPool::Context(config)));
731       }
732     } break;
733 
734     case android::Res_value::TYPE_REFERENCE:
735     case android::Res_value::TYPE_ATTRIBUTE:
736     case android::Res_value::TYPE_DYNAMIC_REFERENCE:
737     case android::Res_value::TYPE_DYNAMIC_ATTRIBUTE: {
738       Reference::Type ref_type = Reference::Type::kResource;
739       if (res_value.dataType == android::Res_value::TYPE_ATTRIBUTE ||
740           res_value.dataType == android::Res_value::TYPE_DYNAMIC_ATTRIBUTE) {
741         ref_type = Reference::Type::kAttribute;
742       }
743 
744       if (data == 0u) {
745         // A reference of 0, must be the magic @null reference.
746         return util::make_unique<Reference>();
747       }
748 
749       // This is a normal reference.
750       return util::make_unique<Reference>(data, ref_type);
751     } break;
752   }
753 
754   // Treat this as a raw binary primitive.
755   return util::make_unique<BinaryPrimitive>(res_value);
756 }
757 
758 // Converts the codepoint to UTF-8 and appends it to the string.
AppendCodepointToUtf8String(char32_t codepoint,std::string * output)759 static bool AppendCodepointToUtf8String(char32_t codepoint, std::string* output) {
760   ssize_t len = utf32_to_utf8_length(&codepoint, 1);
761   if (len < 0) {
762     return false;
763   }
764 
765   const size_t start_append_pos = output->size();
766 
767   // Make room for the next character.
768   output->resize(output->size() + len);
769 
770   char* dst = &*(output->begin() + start_append_pos);
771   utf32_to_utf8(&codepoint, 1, dst, len + 1);
772   return true;
773 }
774 
775 // Reads up to 4 UTF-8 characters that represent a Unicode escape sequence, and appends the
776 // Unicode codepoint represented by the escape sequence to the string.
AppendUnicodeEscapeSequence(Utf8Iterator * iter,std::string * output)777 static bool AppendUnicodeEscapeSequence(Utf8Iterator* iter, std::string* output) {
778   char32_t code = 0;
779   for (size_t i = 0; i < 4 && iter->HasNext(); i++) {
780     char32_t codepoint = iter->Next();
781     char32_t a;
782     if (codepoint >= U'0' && codepoint <= U'9') {
783       a = codepoint - U'0';
784     } else if (codepoint >= U'a' && codepoint <= U'f') {
785       a = codepoint - U'a' + 10;
786     } else if (codepoint >= U'A' && codepoint <= U'F') {
787       a = codepoint - U'A' + 10;
788     } else {
789       return {};
790     }
791     code = (code << 4) | a;
792   }
793   return AppendCodepointToUtf8String(code, output);
794 }
795 
StringBuilder(bool preserve_spaces)796 StringBuilder::StringBuilder(bool preserve_spaces)
797     : preserve_spaces_(preserve_spaces), quote_(preserve_spaces) {
798 }
799 
AppendText(const std::string & text)800 StringBuilder& StringBuilder::AppendText(const std::string& text) {
801   if (!error_.empty()) {
802     return *this;
803   }
804 
805   const size_t previous_len = xml_string_.text.size();
806   Utf8Iterator iter(text);
807   while (iter.HasNext()) {
808     char32_t codepoint = iter.Next();
809     if (!quote_ && iswspace(codepoint)) {
810       if (!last_codepoint_was_space_) {
811         // Emit a space if it's the first.
812         xml_string_.text += ' ';
813         last_codepoint_was_space_ = true;
814       }
815 
816       // Keep eating spaces.
817       continue;
818     }
819 
820     // This is not a space.
821     last_codepoint_was_space_ = false;
822 
823     if (codepoint == U'\\') {
824       if (iter.HasNext()) {
825         codepoint = iter.Next();
826         switch (codepoint) {
827           case U't':
828             xml_string_.text += '\t';
829             break;
830 
831           case U'n':
832             xml_string_.text += '\n';
833             break;
834 
835           case U'#':
836           case U'@':
837           case U'?':
838           case U'"':
839           case U'\'':
840           case U'\\':
841             xml_string_.text += static_cast<char>(codepoint);
842             break;
843 
844           case U'u':
845             if (!AppendUnicodeEscapeSequence(&iter, &xml_string_.text)) {
846               error_ =
847                   StringPrintf("invalid unicode escape sequence in string\n\"%s\"", text.c_str());
848               return *this;
849             }
850             break;
851 
852           default:
853             // Ignore the escape character and just include the codepoint.
854             AppendCodepointToUtf8String(codepoint, &xml_string_.text);
855             break;
856         }
857       }
858     } else if (!preserve_spaces_ && codepoint == U'"') {
859       // Only toggle the quote state when we are not preserving spaces.
860       quote_ = !quote_;
861 
862     } else if (!quote_ && codepoint == U'\'') {
863       // This should be escaped.
864       error_ = StringPrintf("unescaped apostrophe in string\n\"%s\"", text.c_str());
865       return *this;
866 
867     } else {
868       AppendCodepointToUtf8String(codepoint, &xml_string_.text);
869     }
870   }
871 
872   // Accumulate the added string's UTF-16 length.
873   const uint8_t* utf8_data = reinterpret_cast<const uint8_t*>(xml_string_.text.c_str());
874   const size_t utf8_length = xml_string_.text.size();
875   ssize_t len = utf8_to_utf16_length(utf8_data + previous_len, utf8_length - previous_len);
876   if (len < 0) {
877     error_ = StringPrintf("invalid unicode code point in string\n\"%s\"", utf8_data + previous_len);
878     return *this;
879   }
880 
881   utf16_len_ += static_cast<uint32_t>(len);
882   return *this;
883 }
884 
StartSpan(const std::string & name)885 StringBuilder::SpanHandle StringBuilder::StartSpan(const std::string& name) {
886   if (!error_.empty()) {
887     return 0u;
888   }
889 
890   // When we start a span, all state associated with whitespace truncation and quotation is ended.
891   ResetTextState();
892   Span span;
893   span.name = name;
894   span.first_char = span.last_char = utf16_len_;
895   xml_string_.spans.push_back(std::move(span));
896   return xml_string_.spans.size() - 1;
897 }
898 
EndSpan(SpanHandle handle)899 void StringBuilder::EndSpan(SpanHandle handle) {
900   if (!error_.empty()) {
901     return;
902   }
903 
904   // When we end a span, all state associated with whitespace truncation and quotation is ended.
905   ResetTextState();
906   xml_string_.spans[handle].last_char = utf16_len_ - 1u;
907 }
908 
StartUntranslatable()909 StringBuilder::UntranslatableHandle StringBuilder::StartUntranslatable() {
910   if (!error_.empty()) {
911     return 0u;
912   }
913 
914   UntranslatableSection section;
915   section.start = section.end = xml_string_.text.size();
916   xml_string_.untranslatable_sections.push_back(section);
917   return xml_string_.untranslatable_sections.size() - 1;
918 }
919 
EndUntranslatable(UntranslatableHandle handle)920 void StringBuilder::EndUntranslatable(UntranslatableHandle handle) {
921   if (!error_.empty()) {
922     return;
923   }
924   xml_string_.untranslatable_sections[handle].end = xml_string_.text.size();
925 }
926 
GetFlattenedString() const927 FlattenedXmlString StringBuilder::GetFlattenedString() const {
928   return xml_string_;
929 }
930 
to_string() const931 std::string StringBuilder::to_string() const {
932   return xml_string_.text;
933 }
934 
operator bool() const935 StringBuilder::operator bool() const {
936   return error_.empty();
937 }
938 
GetError() const939 std::string StringBuilder::GetError() const {
940   return error_;
941 }
942 
ResetTextState()943 void StringBuilder::ResetTextState() {
944   quote_ = preserve_spaces_;
945   last_codepoint_was_space_ = false;
946 }
947 
948 }  // namespace ResourceUtils
949 }  // namespace aapt
950