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 = android::Res_value::TYPE_INT_DEC;
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_dynamic()) {
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<void (const ResourceName &)> & on_create_reference)629 std::unique_ptr<Item> TryParseItemForAttribute(
630     const StringPiece& value, uint32_t type_mask,
631     const std::function<void(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     if (create && on_create_reference) {
643       on_create_reference(reference->name.value());
644     }
645     return std::move(reference);
646   }
647 
648   if (type_mask & ResTable_map::TYPE_COLOR) {
649     // Try parsing this as a color.
650     auto color = TryParseColor(value);
651     if (color) {
652       return std::move(color);
653     }
654   }
655 
656   if (type_mask & ResTable_map::TYPE_BOOLEAN) {
657     // Try parsing this as a boolean.
658     auto boolean = TryParseBool(value);
659     if (boolean) {
660       return std::move(boolean);
661     }
662   }
663 
664   if (type_mask & ResTable_map::TYPE_INTEGER) {
665     // Try parsing this as an integer.
666     auto integer = TryParseInt(value);
667     if (integer) {
668       return std::move(integer);
669     }
670   }
671 
672   const uint32_t float_mask =
673       ResTable_map::TYPE_FLOAT | ResTable_map::TYPE_DIMENSION | ResTable_map::TYPE_FRACTION;
674   if (type_mask & float_mask) {
675     // Try parsing this as a float.
676     auto floating_point = TryParseFloat(value);
677     if (floating_point) {
678       if (type_mask & AndroidTypeToAttributeTypeMask(floating_point->value.dataType)) {
679         return std::move(floating_point);
680       }
681     }
682   }
683   return {};
684 }
685 
686 /**
687  * We successively try to parse the string as a resource type that the Attribute
688  * allows.
689  */
TryParseItemForAttribute(const StringPiece & str,const Attribute * attr,const std::function<void (const ResourceName &)> & on_create_reference)690 std::unique_ptr<Item> TryParseItemForAttribute(
691     const StringPiece& str, const Attribute* attr,
692     const std::function<void(const ResourceName&)>& on_create_reference) {
693   using android::ResTable_map;
694 
695   const uint32_t type_mask = attr->type_mask;
696   auto value = TryParseItemForAttribute(str, type_mask, on_create_reference);
697   if (value) {
698     return value;
699   }
700 
701   if (type_mask & ResTable_map::TYPE_ENUM) {
702     // Try parsing this as an enum.
703     auto enum_value = TryParseEnumSymbol(attr, str);
704     if (enum_value) {
705       return std::move(enum_value);
706     }
707   }
708 
709   if (type_mask & ResTable_map::TYPE_FLAGS) {
710     // Try parsing this as a flag.
711     auto flag_value = TryParseFlagSymbol(attr, str);
712     if (flag_value) {
713       return std::move(flag_value);
714     }
715   }
716   return {};
717 }
718 
BuildResourceFileName(const ResourceFile & res_file,const NameMangler * mangler)719 std::string BuildResourceFileName(const ResourceFile& res_file, const NameMangler* mangler) {
720   std::stringstream out;
721   out << "res/" << res_file.name.type;
722   if (res_file.config != ConfigDescription{}) {
723     out << "-" << res_file.config;
724   }
725   out << "/";
726 
727   if (mangler && mangler->ShouldMangle(res_file.name.package)) {
728     out << NameMangler::MangleEntry(res_file.name.package, res_file.name.entry);
729   } else {
730     out << res_file.name.entry;
731   }
732   out << file::GetExtension(res_file.source.path);
733   return out.str();
734 }
735 
ParseBinaryResValue(const ResourceType & type,const ConfigDescription & config,const android::ResStringPool & src_pool,const android::Res_value & res_value,StringPool * dst_pool)736 std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const ConfigDescription& config,
737                                           const android::ResStringPool& src_pool,
738                                           const android::Res_value& res_value,
739                                           StringPool* dst_pool) {
740   if (type == ResourceType::kId) {
741     return util::make_unique<Id>();
742   }
743 
744   const uint32_t data = util::DeviceToHost32(res_value.data);
745   switch (res_value.dataType) {
746     case android::Res_value::TYPE_STRING: {
747       const std::string str = util::GetString(src_pool, data);
748       const android::ResStringPool_span* spans = src_pool.styleAt(data);
749 
750       // Check if the string has a valid style associated with it.
751       if (spans != nullptr && spans->name.index != android::ResStringPool_span::END) {
752         StyleString style_str = {str};
753         while (spans->name.index != android::ResStringPool_span::END) {
754           style_str.spans.push_back(Span{util::GetString(src_pool, spans->name.index),
755                                          spans->firstChar, spans->lastChar});
756           spans++;
757         }
758         return util::make_unique<StyledString>(dst_pool->MakeRef(
759             style_str, StringPool::Context(StringPool::Context::kNormalPriority, config)));
760       } else {
761         if (type != ResourceType::kString && util::StartsWith(str, "res/")) {
762           // This must be a FileReference.
763           std::unique_ptr<FileReference> file_ref =
764               util::make_unique<FileReference>(dst_pool->MakeRef(
765                   str, StringPool::Context(StringPool::Context::kHighPriority, config)));
766           if (type == ResourceType::kRaw) {
767             file_ref->type = ResourceFile::Type::kUnknown;
768           } else if (util::EndsWith(*file_ref->path, ".xml")) {
769             file_ref->type = ResourceFile::Type::kBinaryXml;
770           } else if (util::EndsWith(*file_ref->path, ".png")) {
771             file_ref->type = ResourceFile::Type::kPng;
772           }
773           return std::move(file_ref);
774         }
775 
776         // There are no styles associated with this string, so treat it as a simple string.
777         return util::make_unique<String>(dst_pool->MakeRef(str, StringPool::Context(config)));
778       }
779     } break;
780 
781     case android::Res_value::TYPE_REFERENCE:
782     case android::Res_value::TYPE_ATTRIBUTE:
783     case android::Res_value::TYPE_DYNAMIC_REFERENCE:
784     case android::Res_value::TYPE_DYNAMIC_ATTRIBUTE: {
785       Reference::Type ref_type = Reference::Type::kResource;
786       if (res_value.dataType == android::Res_value::TYPE_ATTRIBUTE ||
787           res_value.dataType == android::Res_value::TYPE_DYNAMIC_ATTRIBUTE) {
788         ref_type = Reference::Type::kAttribute;
789       }
790 
791       if (data == 0u) {
792         // A reference of 0, must be the magic @null reference.
793         return util::make_unique<Reference>();
794       }
795 
796       // This is a normal reference.
797       return util::make_unique<Reference>(data, ref_type);
798     } break;
799   }
800 
801   // Treat this as a raw binary primitive.
802   return util::make_unique<BinaryPrimitive>(res_value);
803 }
804 
805 // Converts the codepoint to UTF-8 and appends it to the string.
AppendCodepointToUtf8String(char32_t codepoint,std::string * output)806 static bool AppendCodepointToUtf8String(char32_t codepoint, std::string* output) {
807   ssize_t len = utf32_to_utf8_length(&codepoint, 1);
808   if (len < 0) {
809     return false;
810   }
811 
812   const size_t start_append_pos = output->size();
813 
814   // Make room for the next character.
815   output->resize(output->size() + len);
816 
817   char* dst = &*(output->begin() + start_append_pos);
818   utf32_to_utf8(&codepoint, 1, dst, len + 1);
819   return true;
820 }
821 
822 // Reads up to 4 UTF-8 characters that represent a Unicode escape sequence, and appends the
823 // Unicode codepoint represented by the escape sequence to the string.
AppendUnicodeEscapeSequence(Utf8Iterator * iter,std::string * output)824 static bool AppendUnicodeEscapeSequence(Utf8Iterator* iter, std::string* output) {
825   char32_t code = 0;
826   for (size_t i = 0; i < 4 && iter->HasNext(); i++) {
827     char32_t codepoint = iter->Next();
828     char32_t a;
829     if (codepoint >= U'0' && codepoint <= U'9') {
830       a = codepoint - U'0';
831     } else if (codepoint >= U'a' && codepoint <= U'f') {
832       a = codepoint - U'a' + 10;
833     } else if (codepoint >= U'A' && codepoint <= U'F') {
834       a = codepoint - U'A' + 10;
835     } else {
836       return {};
837     }
838     code = (code << 4) | a;
839   }
840   return AppendCodepointToUtf8String(code, output);
841 }
842 
StringBuilder(bool preserve_spaces)843 StringBuilder::StringBuilder(bool preserve_spaces)
844     : preserve_spaces_(preserve_spaces), quote_(preserve_spaces) {
845 }
846 
AppendText(const std::string & text)847 StringBuilder& StringBuilder::AppendText(const std::string& text) {
848   if (!error_.empty()) {
849     return *this;
850   }
851 
852   const size_t previous_len = xml_string_.text.size();
853   Utf8Iterator iter(text);
854   while (iter.HasNext()) {
855     char32_t codepoint = iter.Next();
856     if (!preserve_spaces_ && !quote_ && (codepoint <= std::numeric_limits<char>::max())
857                                          && isspace(static_cast<char>(codepoint))) {
858       if (!last_codepoint_was_space_) {
859         // Emit a space if it's the first.
860         xml_string_.text += ' ';
861         last_codepoint_was_space_ = true;
862       }
863 
864       // Keep eating spaces.
865       continue;
866     }
867 
868     // This is not a space.
869     last_codepoint_was_space_ = false;
870 
871     if (codepoint == U'\\') {
872       if (iter.HasNext()) {
873         codepoint = iter.Next();
874         switch (codepoint) {
875           case U't':
876             xml_string_.text += '\t';
877             break;
878           case U'n':
879             xml_string_.text += '\n';
880             break;
881 
882           case U'#':
883           case U'@':
884           case U'?':
885           case U'"':
886           case U'\'':
887           case U'\\':
888             xml_string_.text += static_cast<char>(codepoint);
889             break;
890 
891           case U'u':
892             if (!AppendUnicodeEscapeSequence(&iter, &xml_string_.text)) {
893               error_ =
894                   StringPrintf("invalid unicode escape sequence in string\n\"%s\"", text.c_str());
895               return *this;
896             }
897             break;
898 
899           default:
900             // Ignore the escape character and just include the codepoint.
901             AppendCodepointToUtf8String(codepoint, &xml_string_.text);
902             break;
903         }
904       }
905     } else if (!preserve_spaces_ && codepoint == U'"') {
906       // Only toggle the quote state when we are not preserving spaces.
907       quote_ = !quote_;
908 
909     } else if (!preserve_spaces_ && !quote_ && codepoint == U'\'') {
910       // This should be escaped when we are not preserving spaces
911       error_ = StringPrintf("unescaped apostrophe in string\n\"%s\"", text.c_str());
912       return *this;
913 
914     } else {
915       AppendCodepointToUtf8String(codepoint, &xml_string_.text);
916     }
917   }
918 
919   // Accumulate the added string's UTF-16 length.
920   const uint8_t* utf8_data = reinterpret_cast<const uint8_t*>(xml_string_.text.c_str());
921   const size_t utf8_length = xml_string_.text.size();
922   ssize_t len = utf8_to_utf16_length(utf8_data + previous_len, utf8_length - previous_len);
923   if (len < 0) {
924     error_ = StringPrintf("invalid unicode code point in string\n\"%s\"", utf8_data + previous_len);
925     return *this;
926   }
927 
928   utf16_len_ += static_cast<uint32_t>(len);
929   return *this;
930 }
931 
StartSpan(const std::string & name)932 StringBuilder::SpanHandle StringBuilder::StartSpan(const std::string& name) {
933   if (!error_.empty()) {
934     return 0u;
935   }
936 
937   // When we start a span, all state associated with whitespace truncation and quotation is ended.
938   ResetTextState();
939   Span span;
940   span.name = name;
941   span.first_char = span.last_char = utf16_len_;
942   xml_string_.spans.push_back(std::move(span));
943   return xml_string_.spans.size() - 1;
944 }
945 
EndSpan(SpanHandle handle)946 void StringBuilder::EndSpan(SpanHandle handle) {
947   if (!error_.empty()) {
948     return;
949   }
950 
951   // When we end a span, all state associated with whitespace truncation and quotation is ended.
952   ResetTextState();
953   xml_string_.spans[handle].last_char = utf16_len_ - 1u;
954 }
955 
StartUntranslatable()956 StringBuilder::UntranslatableHandle StringBuilder::StartUntranslatable() {
957   if (!error_.empty()) {
958     return 0u;
959   }
960 
961   UntranslatableSection section;
962   section.start = section.end = xml_string_.text.size();
963   xml_string_.untranslatable_sections.push_back(section);
964   return xml_string_.untranslatable_sections.size() - 1;
965 }
966 
EndUntranslatable(UntranslatableHandle handle)967 void StringBuilder::EndUntranslatable(UntranslatableHandle handle) {
968   if (!error_.empty()) {
969     return;
970   }
971   xml_string_.untranslatable_sections[handle].end = xml_string_.text.size();
972 }
973 
GetFlattenedString() const974 FlattenedXmlString StringBuilder::GetFlattenedString() const {
975   return xml_string_;
976 }
977 
to_string() const978 std::string StringBuilder::to_string() const {
979   return xml_string_.text;
980 }
981 
operator bool() const982 StringBuilder::operator bool() const {
983   return error_.empty();
984 }
985 
GetError() const986 std::string StringBuilder::GetError() const {
987   return error_;
988 }
989 
ResetTextState()990 void StringBuilder::ResetTextState() {
991   quote_ = preserve_spaces_;
992   last_codepoint_was_space_ = false;
993 }
994 
995 }  // namespace ResourceUtils
996 }  // namespace aapt
997