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