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