1 /*
2  * Copyright (C) 2017 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 "cmd/Util.h"
18 
19 #include <vector>
20 
21 #include "android-base/logging.h"
22 #include "androidfw/ConfigDescription.h"
23 #include "androidfw/Locale.h"
24 #include "ResourceUtils.h"
25 #include "ValueVisitor.h"
26 #include "split/TableSplitter.h"
27 
28 #include "util/Util.h"
29 
30 using ::android::ConfigDescription;
31 using ::android::LocaleValue;
32 using ::android::StringPiece;
33 using ::android::base::StringPrintf;
34 
35 namespace aapt {
36 
ParseTargetDensityParameter(StringPiece arg,android::IDiagnostics * diag)37 std::optional<uint16_t> ParseTargetDensityParameter(StringPiece arg, android::IDiagnostics* diag) {
38   ConfigDescription preferred_density_config;
39   if (!ConfigDescription::Parse(arg, &preferred_density_config)) {
40     diag->Error(android::DiagMessage()
41                 << "invalid density '" << arg << "' for --preferred-density option");
42     return {};
43   }
44 
45   // Clear the version that can be automatically added.
46   preferred_density_config.sdkVersion = 0;
47 
48   if (preferred_density_config.diff(ConfigDescription::DefaultConfig()) !=
49       ConfigDescription::CONFIG_DENSITY) {
50     diag->Error(android::DiagMessage() << "invalid preferred density '" << arg << "'. "
51                                        << "Preferred density must only be a density value");
52     return {};
53   }
54   return preferred_density_config.density;
55 }
56 
ParseSplitParameter(StringPiece arg,android::IDiagnostics * diag,std::string * out_path,SplitConstraints * out_split)57 bool ParseSplitParameter(StringPiece arg, android::IDiagnostics* diag, std::string* out_path,
58                          SplitConstraints* out_split) {
59   CHECK(diag != nullptr);
60   CHECK(out_path != nullptr);
61   CHECK(out_split != nullptr);
62 
63 #ifdef _WIN32
64   const char sSeparator = ';';
65 #else
66   const char sSeparator = ':';
67 #endif
68 
69   std::vector<std::string> parts = util::Split(arg, sSeparator);
70   if (parts.size() != 2) {
71     diag->Error(android::DiagMessage() << "invalid split parameter '" << arg << "'");
72     diag->Note(android::DiagMessage() << "should be --split path/to/output.apk" << sSeparator
73                                       << "<config>[,<config>...].");
74     return false;
75   }
76 
77   *out_path = parts[0];
78   out_split->name = parts[1];
79   for (StringPiece config_str : util::Tokenize(parts[1], ',')) {
80     ConfigDescription config;
81     if (!ConfigDescription::Parse(config_str, &config)) {
82       diag->Error(android::DiagMessage()
83                   << "invalid config '" << config_str << "' in split parameter '" << arg << "'");
84       return false;
85     }
86     out_split->configs.insert(config);
87   }
88   return true;
89 }
90 
ParseConfigFilterParameters(const std::vector<std::string> & args,android::IDiagnostics * diag)91 std::unique_ptr<IConfigFilter> ParseConfigFilterParameters(const std::vector<std::string>& args,
92                                                            android::IDiagnostics* diag) {
93   std::unique_ptr<AxisConfigFilter> filter = util::make_unique<AxisConfigFilter>();
94   for (const std::string& config_arg : args) {
95     for (StringPiece config_str : util::Tokenize(config_arg, ',')) {
96       ConfigDescription config;
97       LocaleValue lv;
98       if (lv.InitFromFilterString(config_str)) {
99         lv.WriteTo(&config);
100       } else if (!ConfigDescription::Parse(config_str, &config)) {
101         diag->Error(android::DiagMessage()
102                     << "invalid config '" << config_str << "' for -c option");
103         return {};
104       }
105 
106       if (config.density != 0) {
107         diag->Warn(android::DiagMessage() << "ignoring density '" << config << "' for -c option");
108       } else {
109         filter->AddConfig(config);
110       }
111     }
112   }
113   return std::move(filter);
114 }
115 
ParseFeatureFlagsParameter(StringPiece arg,android::IDiagnostics * diag,FeatureFlagValues * out_feature_flag_values)116 bool ParseFeatureFlagsParameter(StringPiece arg, android::IDiagnostics* diag,
117                                 FeatureFlagValues* out_feature_flag_values) {
118   if (arg.empty()) {
119     return true;
120   }
121 
122   for (StringPiece flag_and_value : util::Tokenize(arg, ',')) {
123     std::vector<std::string> parts = util::Split(flag_and_value, '=');
124     if (parts.empty()) {
125       continue;
126     }
127 
128     if (parts.size() > 2) {
129       diag->Error(android::DiagMessage()
130                   << "Invalid feature flag and optional value '" << flag_and_value
131                   << "'. Must be in the format 'flag_name[=true|false]");
132       return false;
133     }
134 
135     StringPiece flag_name = util::TrimWhitespace(parts[0]);
136     if (flag_name.empty()) {
137       diag->Error(android::DiagMessage() << "No name given for one or more flags in: " << arg);
138       return false;
139     }
140 
141     std::optional<bool> flag_value = {};
142     if (parts.size() == 2) {
143       StringPiece str_flag_value = util::TrimWhitespace(parts[1]);
144       if (!str_flag_value.empty()) {
145         flag_value = ResourceUtils::ParseBool(parts[1]);
146         if (!flag_value.has_value()) {
147           diag->Error(android::DiagMessage() << "Invalid value for feature flag '" << flag_and_value
148                                              << "'. Value must be 'true' or 'false'");
149           return false;
150         }
151       }
152     }
153 
154     if (auto [it, inserted] =
155             out_feature_flag_values->try_emplace(std::string(flag_name), flag_value);
156         !inserted) {
157       // We are allowing the same flag to appear multiple times, last value wins.
158       diag->Note(android::DiagMessage()
159                  << "Value for feature flag '" << flag_name << "' was given more than once");
160       it->second = flag_value;
161     }
162   }
163   return true;
164 }
165 
166 // Adjust the SplitConstraints so that their SDK version is stripped if it
167 // is less than or equal to the minSdk. Otherwise the resources that have had
168 // their SDK version stripped due to minSdk won't ever match.
AdjustSplitConstraintsForMinSdk(int min_sdk,const std::vector<SplitConstraints> & split_constraints)169 std::vector<SplitConstraints> AdjustSplitConstraintsForMinSdk(
170     int min_sdk, const std::vector<SplitConstraints>& split_constraints) {
171   std::vector<SplitConstraints> adjusted_constraints;
172   adjusted_constraints.reserve(split_constraints.size());
173   for (const SplitConstraints& constraints : split_constraints) {
174     SplitConstraints constraint;
175     for (const ConfigDescription& config : constraints.configs) {
176       const ConfigDescription &configToInsert = (config.sdkVersion <= min_sdk)
177           ? config.CopyWithoutSdkVersion()
178           : config;
179       // only add the config if it actually selects something
180       if (configToInsert != ConfigDescription::DefaultConfig()) {
181         constraint.configs.insert(configToInsert);
182       }
183     }
184     constraint.name = constraints.name;
185     adjusted_constraints.push_back(std::move(constraint));
186   }
187   return adjusted_constraints;
188 }
189 
CreateAttributeWithId(const ResourceId & id)190 static xml::AaptAttribute CreateAttributeWithId(const ResourceId& id) {
191   return xml::AaptAttribute(Attribute(), id);
192 }
193 
CreateAndroidNamespaceDecl()194 static xml::NamespaceDecl CreateAndroidNamespaceDecl() {
195   xml::NamespaceDecl decl;
196   decl.prefix = "android";
197   decl.uri = xml::kSchemaAndroid;
198   return decl;
199 }
200 
201 // Returns a copy of 'name' which conforms to the regex '[a-zA-Z]+[a-zA-Z0-9_]*' by
202 // replacing nonconforming characters with underscores.
203 //
204 // See frameworks/base/core/java/android/content/pm/PackageParser.java which
205 // checks this at runtime.
MakePackageSafeName(const std::string & name)206 std::string MakePackageSafeName(const std::string &name) {
207   std::string result(name);
208   bool first = true;
209   for (char &c : result) {
210     if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
211       first = false;
212       continue;
213     }
214     if (!first) {
215       if (c >= '0' && c <= '9') {
216         continue;
217       }
218     }
219 
220     c = '_';
221     first = false;
222   }
223   return result;
224 }
225 
GenerateSplitManifest(const AppInfo & app_info,const SplitConstraints & constraints)226 std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info,
227                                                         const SplitConstraints& constraints) {
228   const ResourceId kVersionCode(0x0101021b);
229   const ResourceId kVersionCodeMajor(0x01010576);
230   const ResourceId kRevisionCode(0x010104d5);
231   const ResourceId kHasCode(0x0101000c);
232 
233   std::unique_ptr<xml::Element> manifest_el = util::make_unique<xml::Element>();
234   manifest_el->namespace_decls.push_back(CreateAndroidNamespaceDecl());
235   manifest_el->name = "manifest";
236   manifest_el->attributes.push_back(xml::Attribute{"", "package", app_info.package});
237 
238   if (app_info.version_code) {
239     const uint32_t version_code = app_info.version_code.value();
240     manifest_el->attributes.push_back(xml::Attribute{
241         xml::kSchemaAndroid, "versionCode", std::to_string(version_code),
242         CreateAttributeWithId(kVersionCode),
243         util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_DEC, version_code)});
244   }
245 
246   if (app_info.version_code_major) {
247     const uint32_t version_code_major = app_info.version_code_major.value();
248     manifest_el->attributes.push_back(xml::Attribute{
249         xml::kSchemaAndroid, "versionCodeMajor", std::to_string(version_code_major),
250         CreateAttributeWithId(kVersionCodeMajor),
251         util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_DEC, version_code_major)});
252   }
253 
254   if (app_info.revision_code) {
255     const uint32_t revision_code = app_info.revision_code.value();
256     manifest_el->attributes.push_back(xml::Attribute{
257         xml::kSchemaAndroid, "revisionCode", std::to_string(revision_code),
258         CreateAttributeWithId(kRevisionCode),
259         util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_DEC, revision_code)});
260   }
261 
262   std::stringstream split_name;
263   if (app_info.split_name) {
264     split_name << app_info.split_name.value() << ".";
265   }
266   std::vector<std::string> sanitized_config_names;
267   for (const auto &config : constraints.configs) {
268     sanitized_config_names.push_back(MakePackageSafeName(config.toString().c_str()));
269   }
270   split_name << "config." << util::Joiner(sanitized_config_names, "_");
271 
272   manifest_el->attributes.push_back(xml::Attribute{"", "split", split_name.str()});
273 
274   if (app_info.split_name) {
275     manifest_el->attributes.push_back(
276         xml::Attribute{"", "configForSplit", app_info.split_name.value()});
277   }
278 
279   // Splits may contain more configurations than originally desired (fall-back densities, etc.).
280   // This makes programmatic discovery of split targeting difficult. Encode the original
281   // split constraints intended for this split.
282   std::stringstream target_config_str;
283   target_config_str << util::Joiner(constraints.configs, ",");
284   manifest_el->attributes.push_back(xml::Attribute{"", "targetConfig", target_config_str.str()});
285 
286   std::unique_ptr<xml::Element> application_el = util::make_unique<xml::Element>();
287   application_el->name = "application";
288   application_el->attributes.push_back(
289       xml::Attribute{xml::kSchemaAndroid, "hasCode", "false", CreateAttributeWithId(kHasCode),
290                      util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_BOOLEAN, 0u)});
291 
292   manifest_el->AppendChild(std::move(application_el));
293 
294   std::unique_ptr<xml::XmlResource> doc = util::make_unique<xml::XmlResource>();
295   doc->root = std::move(manifest_el);
296   return doc;
297 }
298 
ExtractCompiledString(const xml::Attribute & attr,std::string * out_error)299 static std::optional<std::string> ExtractCompiledString(const xml::Attribute& attr,
300                                                         std::string* out_error) {
301   if (attr.compiled_value != nullptr) {
302     const String* compiled_str = ValueCast<String>(attr.compiled_value.get());
303     if (compiled_str != nullptr) {
304       if (!compiled_str->value->empty()) {
305         return *compiled_str->value;
306       } else {
307         *out_error = "compiled value is an empty string";
308         return {};
309       }
310     }
311     *out_error = "compiled value is not a string";
312     return {};
313   }
314 
315   // Fallback to the plain text value if there is one.
316   if (!attr.value.empty()) {
317     return attr.value;
318   }
319   *out_error = "value is an empty string";
320   return {};
321 }
322 
ExtractCompiledInt(const xml::Attribute & attr,std::string * out_error)323 static std::optional<uint32_t> ExtractCompiledInt(const xml::Attribute& attr,
324                                                   std::string* out_error) {
325   if (attr.compiled_value != nullptr) {
326     const BinaryPrimitive* compiled_prim = ValueCast<BinaryPrimitive>(attr.compiled_value.get());
327     if (compiled_prim != nullptr) {
328       if (compiled_prim->value.dataType >= android::Res_value::TYPE_FIRST_INT &&
329           compiled_prim->value.dataType <= android::Res_value::TYPE_LAST_INT) {
330         return compiled_prim->value.data;
331       }
332     }
333     *out_error = "compiled value is not an integer";
334     return {};
335   }
336 
337   // Fallback to the plain text value if there is one.
338   std::optional<uint32_t> integer = ResourceUtils::ParseInt(attr.value);
339   if (integer) {
340     return integer;
341   }
342   std::stringstream error_msg;
343   error_msg << "'" << attr.value << "' is not a valid integer";
344   *out_error = error_msg.str();
345   return {};
346 }
347 
ExtractSdkVersion(const xml::Attribute & attr,std::string * out_error)348 static std::optional<int> ExtractSdkVersion(const xml::Attribute& attr, std::string* out_error) {
349   if (attr.compiled_value != nullptr) {
350     const BinaryPrimitive* compiled_prim = ValueCast<BinaryPrimitive>(attr.compiled_value.get());
351     if (compiled_prim != nullptr) {
352       if (compiled_prim->value.dataType >= android::Res_value::TYPE_FIRST_INT &&
353           compiled_prim->value.dataType <= android::Res_value::TYPE_LAST_INT) {
354         return compiled_prim->value.data;
355       }
356       *out_error = "compiled value is not an integer or string";
357       return {};
358     }
359 
360     const String* compiled_str = ValueCast<String>(attr.compiled_value.get());
361     if (compiled_str != nullptr) {
362       std::optional<int> sdk_version = ResourceUtils::ParseSdkVersion(*compiled_str->value);
363       if (sdk_version) {
364         return sdk_version;
365       }
366 
367       *out_error = "compiled string value is not a valid SDK version";
368       return {};
369     }
370     *out_error = "compiled value is not an integer or string";
371     return {};
372   }
373 
374   // Fallback to the plain text value if there is one.
375   std::optional<int> sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
376   if (sdk_version) {
377     return sdk_version;
378   }
379   std::stringstream error_msg;
380   error_msg << "'" << attr.value << "' is not a valid SDK version";
381   *out_error = error_msg.str();
382   return {};
383 }
384 
ExtractAppInfoFromBinaryManifest(const xml::XmlResource & xml_res,android::IDiagnostics * diag)385 std::optional<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
386                                                         android::IDiagnostics* diag) {
387   // Make sure the first element is <manifest> with package attribute.
388   const xml::Element* manifest_el = xml_res.root.get();
389   if (manifest_el == nullptr) {
390     return {};
391   }
392 
393   AppInfo app_info;
394 
395   if (!manifest_el->namespace_uri.empty() || manifest_el->name != "manifest") {
396     diag->Error(android::DiagMessage(xml_res.file.source) << "root tag must be <manifest>");
397     return {};
398   }
399 
400   const xml::Attribute* package_attr = manifest_el->FindAttribute({}, "package");
401   if (!package_attr) {
402     diag->Error(android::DiagMessage(xml_res.file.source)
403                 << "<manifest> must have a 'package' attribute");
404     return {};
405   }
406 
407   std::string error_msg;
408   std::optional<std::string> maybe_package = ExtractCompiledString(*package_attr, &error_msg);
409   if (!maybe_package) {
410     diag->Error(android::DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
411                 << "invalid package name: " << error_msg);
412     return {};
413   }
414   app_info.package = maybe_package.value();
415 
416   if (const xml::Attribute* version_code_attr =
417           manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) {
418     std::optional<uint32_t> maybe_code = ExtractCompiledInt(*version_code_attr, &error_msg);
419     if (!maybe_code) {
420       diag->Error(android::DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
421                   << "invalid android:versionCode: " << error_msg);
422       return {};
423     }
424     app_info.version_code = maybe_code.value();
425   }
426 
427   if (const xml::Attribute* version_code_major_attr =
428       manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor")) {
429     std::optional<uint32_t> maybe_code = ExtractCompiledInt(*version_code_major_attr, &error_msg);
430     if (!maybe_code) {
431       diag->Error(android::DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
432                   << "invalid android:versionCodeMajor: " << error_msg);
433       return {};
434     }
435     app_info.version_code_major = maybe_code.value();
436   }
437 
438   if (const xml::Attribute* revision_code_attr =
439           manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) {
440     std::optional<uint32_t> maybe_code = ExtractCompiledInt(*revision_code_attr, &error_msg);
441     if (!maybe_code) {
442       diag->Error(android::DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
443                   << "invalid android:revisionCode: " << error_msg);
444       return {};
445     }
446     app_info.revision_code = maybe_code.value();
447   }
448 
449   if (const xml::Attribute* split_name_attr = manifest_el->FindAttribute({}, "split")) {
450     std::optional<std::string> maybe_split_name =
451         ExtractCompiledString(*split_name_attr, &error_msg);
452     if (!maybe_split_name) {
453       diag->Error(android::DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
454                   << "invalid split name: " << error_msg);
455       return {};
456     }
457     app_info.split_name = maybe_split_name.value();
458   }
459 
460   if (const xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) {
461     if (const xml::Attribute* min_sdk =
462             uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) {
463       std::optional<int> maybe_sdk = ExtractSdkVersion(*min_sdk, &error_msg);
464       if (!maybe_sdk) {
465         diag->Error(android::DiagMessage(xml_res.file.source.WithLine(uses_sdk_el->line_number))
466                     << "invalid android:minSdkVersion: " << error_msg);
467         return {};
468       }
469       app_info.min_sdk_version = maybe_sdk.value();
470     }
471   }
472   return app_info;
473 }
474 
SetLongVersionCode(xml::Element * manifest,uint64_t version)475 void SetLongVersionCode(xml::Element* manifest, uint64_t version) {
476   // Write the low bits of the version code to android:versionCode
477   auto version_code = manifest->FindOrCreateAttribute(xml::kSchemaAndroid, "versionCode");
478   version_code->value = StringPrintf("0x%08x", (uint32_t) (version & 0xffffffff));
479   version_code->compiled_value = ResourceUtils::TryParseInt(version_code->value);
480 
481   auto version_high = (uint32_t) (version >> 32);
482   if (version_high != 0) {
483     // Write the high bits of the version code to android:versionCodeMajor
484     auto version_major = manifest->FindOrCreateAttribute(xml::kSchemaAndroid, "versionCodeMajor");
485     version_major->value = StringPrintf("0x%08x", version_high);
486     version_major->compiled_value = ResourceUtils::TryParseInt(version_major->value);
487   } else {
488     manifest->RemoveAttribute(xml::kSchemaAndroid, "versionCodeMajor");
489   }
490 }
491 
GetRegularExpression(const std::string & input)492 std::regex GetRegularExpression(const std::string &input) {
493   // Standard ECMAScript grammar.
494   std::regex case_insensitive(
495       input, std::regex_constants::ECMAScript);
496   return case_insensitive;
497 }
498 
ParseResourceConfig(const std::string & content,IAaptContext * context,std::unordered_set<ResourceName> & out_resource_exclude_list,std::set<ResourceName> & out_name_collapse_exemptions,std::set<ResourceName> & out_path_shorten_exemptions)499 bool ParseResourceConfig(const std::string& content, IAaptContext* context,
500                          std::unordered_set<ResourceName>& out_resource_exclude_list,
501                          std::set<ResourceName>& out_name_collapse_exemptions,
502                          std::set<ResourceName>& out_path_shorten_exemptions) {
503   for (StringPiece line : util::Tokenize(content, '\n')) {
504     line = util::TrimWhitespace(line);
505     if (line.empty()) {
506       continue;
507     }
508 
509     auto split_line = util::Split(line, '#');
510     if (split_line.size() < 2) {
511       context->GetDiagnostics()->Error(android::DiagMessage(line) << "No # found in line");
512       return false;
513     }
514     StringPiece resource_string = split_line[0];
515     StringPiece directives = split_line[1];
516     ResourceNameRef resource_name;
517     if (!ResourceUtils::ParseResourceName(resource_string, &resource_name)) {
518       context->GetDiagnostics()->Error(android::DiagMessage(line) << "Malformed resource name");
519       return false;
520     }
521     if (!resource_name.package.empty()) {
522       context->GetDiagnostics()->Error(android::DiagMessage(line)
523                                        << "Package set for resource. Only use type/name");
524       return false;
525     }
526     for (StringPiece directive : util::Tokenize(directives, ',')) {
527       if (directive == "remove") {
528         out_resource_exclude_list.insert(resource_name.ToResourceName());
529       } else if (directive == "no_collapse" || directive == "no_obfuscate") {
530         out_name_collapse_exemptions.insert(resource_name.ToResourceName());
531       } else if (directive == "no_path_shorten") {
532         out_path_shorten_exemptions.insert(resource_name.ToResourceName());
533       }
534     }
535   }
536   return true;
537 }
538 
539 }  // namespace aapt
540