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