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