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 "Link.h"
18 
19 #include <sys/stat.h>
20 
21 #include <algorithm>
22 #include <cinttypes>
23 #include <queue>
24 #include <unordered_map>
25 #include <vector>
26 
27 #include "AppInfo.h"
28 #include "Debug.h"
29 #include "LoadedApk.h"
30 #include "NameMangler.h"
31 #include "ResourceUtils.h"
32 #include "ResourceValues.h"
33 #include "ValueVisitor.h"
34 #include "android-base/errors.h"
35 #include "android-base/expected.h"
36 #include "android-base/file.h"
37 #include "android-base/stringprintf.h"
38 #include "androidfw/IDiagnostics.h"
39 #include "androidfw/Locale.h"
40 #include "androidfw/StringPiece.h"
41 #include "cmd/Util.h"
42 #include "compile/IdAssigner.h"
43 #include "compile/XmlIdCollector.h"
44 #include "filter/ConfigFilter.h"
45 #include "format/Archive.h"
46 #include "format/Container.h"
47 #include "format/binary/TableFlattener.h"
48 #include "format/binary/XmlFlattener.h"
49 #include "format/proto/ProtoDeserialize.h"
50 #include "format/proto/ProtoSerialize.h"
51 #include "io/BigBufferStream.h"
52 #include "io/FileStream.h"
53 #include "io/FileSystem.h"
54 #include "io/Util.h"
55 #include "io/ZipArchive.h"
56 #include "java/JavaClassGenerator.h"
57 #include "java/ManifestClassGenerator.h"
58 #include "java/ProguardRules.h"
59 #include "link/Linkers.h"
60 #include "link/ManifestFixer.h"
61 #include "link/NoDefaultResourceRemover.h"
62 #include "link/ReferenceLinker.h"
63 #include "link/ResourceExcluder.h"
64 #include "link/TableMerger.h"
65 #include "link/XmlCompatVersioner.h"
66 #include "optimize/ResourceDeduper.h"
67 #include "optimize/VersionCollapser.h"
68 #include "process/IResourceTableConsumer.h"
69 #include "process/SymbolTable.h"
70 #include "split/TableSplitter.h"
71 #include "trace/TraceBuffer.h"
72 #include "util/Files.h"
73 #include "xml/XmlDom.h"
74 
75 using ::aapt::io::FileInputStream;
76 using ::android::ConfigDescription;
77 using ::android::StringPiece;
78 using ::android::base::expected;
79 using ::android::base::StringPrintf;
80 using ::android::base::unexpected;
81 
82 namespace aapt {
83 
84 namespace {
85 
GetStaticLibraryPackage(ResourceTable * table)86 expected<ResourceTablePackage*, const char*> GetStaticLibraryPackage(ResourceTable* table) {
87   // Resource tables built by aapt2 always contain one package. This is a post condition of
88   // VerifyNoExternalPackages.
89   if (table->packages.size() != 1u) {
90     return unexpected("static library contains more than one package");
91   }
92   return table->packages.back().get();
93 }
94 
95 }  // namespace
96 
97 constexpr uint8_t kAndroidPackageId = 0x01;
98 
99 class LinkContext : public IAaptContext {
100  public:
LinkContext(android::IDiagnostics * diagnostics)101   explicit LinkContext(android::IDiagnostics* diagnostics)
102       : diagnostics_(diagnostics), name_mangler_({}), symbols_(&name_mangler_) {
103   }
104 
GetPackageType()105   PackageType GetPackageType() override {
106     return package_type_;
107   }
108 
SetPackageType(PackageType type)109   void SetPackageType(PackageType type) {
110     package_type_ = type;
111   }
112 
GetDiagnostics()113   android::IDiagnostics* GetDiagnostics() override {
114     return diagnostics_;
115   }
116 
GetNameMangler()117   NameMangler* GetNameMangler() override {
118     return &name_mangler_;
119   }
120 
SetNameManglerPolicy(const NameManglerPolicy & policy)121   void SetNameManglerPolicy(const NameManglerPolicy& policy) {
122     name_mangler_ = NameMangler(policy);
123   }
124 
GetCompilationPackage()125   const std::string& GetCompilationPackage() override {
126     return compilation_package_;
127   }
128 
SetCompilationPackage(StringPiece package_name)129   void SetCompilationPackage(StringPiece package_name) {
130     compilation_package_ = std::string(package_name);
131   }
132 
GetPackageId()133   uint8_t GetPackageId() override {
134     return package_id_;
135   }
136 
SetPackageId(uint8_t id)137   void SetPackageId(uint8_t id) {
138     package_id_ = id;
139   }
140 
GetExternalSymbols()141   SymbolTable* GetExternalSymbols() override {
142     return &symbols_;
143   }
144 
IsVerbose()145   bool IsVerbose() override {
146     return verbose_;
147   }
148 
SetVerbose(bool val)149   void SetVerbose(bool val) {
150     verbose_ = val;
151   }
152 
GetMinSdkVersion()153   int GetMinSdkVersion() override {
154     return min_sdk_version_;
155   }
156 
SetMinSdkVersion(int minSdk)157   void SetMinSdkVersion(int minSdk) {
158     min_sdk_version_ = minSdk;
159   }
160 
GetSplitNameDependencies()161   const std::set<std::string>& GetSplitNameDependencies() override {
162     return split_name_dependencies_;
163   }
164 
SetSplitNameDependencies(const std::set<std::string> & split_name_dependencies)165   void SetSplitNameDependencies(const std::set<std::string>& split_name_dependencies) {
166     split_name_dependencies_ = split_name_dependencies;
167   }
168 
169  private:
170   DISALLOW_COPY_AND_ASSIGN(LinkContext);
171 
172   PackageType package_type_ = PackageType::kApp;
173   android::IDiagnostics* diagnostics_;
174   NameMangler name_mangler_;
175   std::string compilation_package_;
176   uint8_t package_id_ = 0x0;
177   SymbolTable symbols_;
178   bool verbose_ = false;
179   int min_sdk_version_ = 0;
180   std::set<std::string> split_name_dependencies_;
181 };
182 
183 // A custom delegate that generates compatible pre-O IDs for use with feature splits.
184 // Feature splits use package IDs > 7f, which in Java (since Java doesn't have unsigned ints)
185 // is interpreted as a negative number. Some verification was wrongly assuming negative values
186 // were invalid.
187 //
188 // This delegate will attempt to masquerade any '@id/' references with ID 0xPPTTEEEE,
189 // where PP > 7f, as 0x7fPPEEEE. Any potential overlapping is verified and an error occurs if such
190 // an overlap exists.
191 //
192 // See b/37498913.
193 class FeatureSplitSymbolTableDelegate : public DefaultSymbolTableDelegate {
194  public:
FeatureSplitSymbolTableDelegate(IAaptContext * context)195   explicit FeatureSplitSymbolTableDelegate(IAaptContext* context) : context_(context) {
196   }
197 
198   virtual ~FeatureSplitSymbolTableDelegate() = default;
199 
FindByName(const ResourceName & name,const std::vector<std::unique_ptr<ISymbolSource>> & sources)200   virtual std::unique_ptr<SymbolTable::Symbol> FindByName(
201       const ResourceName& name,
202       const std::vector<std::unique_ptr<ISymbolSource>>& sources) override {
203     std::unique_ptr<SymbolTable::Symbol> symbol =
204         DefaultSymbolTableDelegate::FindByName(name, sources);
205     if (symbol == nullptr) {
206       return {};
207     }
208 
209     // Check to see if this is an 'id' with the target package.
210     if (name.type.type == ResourceType::kId && symbol->id) {
211       ResourceId* id = &symbol->id.value();
212       if (id->package_id() > kAppPackageId) {
213         // Rewrite the resource ID to be compatible pre-O.
214         ResourceId rewritten_id(kAppPackageId, id->package_id(), id->entry_id());
215 
216         // Check that this doesn't overlap another resource.
217         if (DefaultSymbolTableDelegate::FindById(rewritten_id, sources) != nullptr) {
218           // The ID overlaps, so log a message (since this is a weird failure) and fail.
219           context_->GetDiagnostics()->Error(android::DiagMessage()
220                                             << "Failed to rewrite " << name
221                                             << " for pre-O feature split support");
222           return {};
223         }
224 
225         if (context_->IsVerbose()) {
226           context_->GetDiagnostics()->Note(android::DiagMessage()
227                                            << "rewriting " << name << " (" << *id << ") -> ("
228                                            << rewritten_id << ")");
229         }
230 
231         *id = rewritten_id;
232       }
233     }
234     return symbol;
235   }
236 
237  private:
238   DISALLOW_COPY_AND_ASSIGN(FeatureSplitSymbolTableDelegate);
239 
240   IAaptContext* context_;
241 };
242 
FlattenXml(IAaptContext * context,const xml::XmlResource & xml_res,StringPiece path,bool keep_raw_values,bool utf16,OutputFormat format,IArchiveWriter * writer)243 static bool FlattenXml(IAaptContext* context, const xml::XmlResource& xml_res, StringPiece path,
244                        bool keep_raw_values, bool utf16, OutputFormat format,
245                        IArchiveWriter* writer) {
246   TRACE_CALL();
247   if (context->IsVerbose()) {
248     context->GetDiagnostics()->Note(android::DiagMessage(path)
249                                     << "writing to archive (keep_raw_values="
250                                     << (keep_raw_values ? "true" : "false") << ")");
251   }
252 
253   switch (format) {
254     case OutputFormat::kApk: {
255       android::BigBuffer buffer(1024);
256       XmlFlattenerOptions options = {};
257       options.keep_raw_values = keep_raw_values;
258       options.use_utf16 = utf16;
259       XmlFlattener flattener(&buffer, options);
260       if (!flattener.Consume(context, &xml_res)) {
261         return false;
262       }
263 
264       io::BigBufferInputStream input_stream(&buffer);
265       return io::CopyInputStreamToArchive(context, &input_stream, path, ArchiveEntry::kCompress,
266                                           writer);
267     } break;
268 
269     case OutputFormat::kProto: {
270       pb::XmlNode pb_node;
271       // Strip whitespace text nodes from tha AndroidManifest.xml
272       SerializeXmlOptions options;
273       options.remove_empty_text_nodes = (path == kAndroidManifestPath);
274       SerializeXmlResourceToPb(xml_res, &pb_node);
275       return io::CopyProtoToArchive(context, &pb_node, path, ArchiveEntry::kCompress, writer);
276     } break;
277   }
278   return false;
279 }
280 
281 // Inflates an XML file from the source path.
LoadXml(const std::string & path,android::IDiagnostics * diag)282 static std::unique_ptr<xml::XmlResource> LoadXml(const std::string& path,
283                                                  android::IDiagnostics* diag) {
284   TRACE_CALL();
285   FileInputStream fin(path);
286   if (fin.HadError()) {
287     diag->Error(android::DiagMessage(path) << "failed to load XML file: " << fin.GetError());
288     return {};
289   }
290   return xml::Inflate(&fin, diag, android::Source(path));
291 }
292 
293 struct ResourceFileFlattenerOptions {
294   bool no_auto_version = false;
295   bool no_version_vectors = false;
296   bool no_version_transitions = false;
297   bool no_xml_namespaces = false;
298   bool keep_raw_values = false;
299   bool do_not_compress_anything = false;
300   bool update_proguard_spec = false;
301   bool do_not_fail_on_missing_resources = false;
302   OutputFormat output_format = OutputFormat::kApk;
303   std::unordered_set<std::string> extensions_to_not_compress;
304   std::optional<std::regex> regex_to_not_compress;
305 };
306 
307 // A sampling of public framework resource IDs.
308 struct R {
309   struct attr {
310     enum : uint32_t {
311       paddingLeft = 0x010100d6u,
312       paddingRight = 0x010100d8u,
313       paddingHorizontal = 0x0101053du,
314 
315       paddingTop = 0x010100d7u,
316       paddingBottom = 0x010100d9u,
317       paddingVertical = 0x0101053eu,
318 
319       layout_marginLeft = 0x010100f7u,
320       layout_marginRight = 0x010100f9u,
321       layout_marginHorizontal = 0x0101053bu,
322 
323       layout_marginTop = 0x010100f8u,
324       layout_marginBottom = 0x010100fau,
325       layout_marginVertical = 0x0101053cu,
326     };
327   };
328 };
329 
330 template <typename T>
GetCompressionFlags(StringPiece str,T options)331 uint32_t GetCompressionFlags(StringPiece str, T options) {
332   if (options.do_not_compress_anything) {
333     return 0;
334   }
335 
336   if (options.regex_to_not_compress &&
337       std::regex_search(str.begin(), str.end(), options.regex_to_not_compress.value())) {
338     return 0;
339   }
340 
341   for (const std::string& extension : options.extensions_to_not_compress) {
342     if (util::EndsWith(str, extension)) {
343       return 0;
344     }
345   }
346   return ArchiveEntry::kCompress;
347 }
348 
349 class ResourceFileFlattener {
350  public:
351   ResourceFileFlattener(const ResourceFileFlattenerOptions& options, IAaptContext* context,
352                         proguard::KeepSet* keep_set);
353 
354   bool Flatten(ResourceTable* table, IArchiveWriter* archive_writer);
355 
356  private:
357   struct FileOperation {
358     ConfigDescription config;
359 
360     // The entry this file came from.
361     ResourceEntry* entry;
362 
363     // The file to copy as-is.
364     io::IFile* file_to_copy;
365 
366     // The XML to process and flatten.
367     std::unique_ptr<xml::XmlResource> xml_to_flatten;
368 
369     // The destination to write this file to.
370     std::string dst_path;
371   };
372 
373   std::vector<std::unique_ptr<xml::XmlResource>> LinkAndVersionXmlFile(ResourceTable* table,
374                                                                        FileOperation* file_op);
375 
376   ResourceFileFlattenerOptions options_;
377   IAaptContext* context_;
378   proguard::KeepSet* keep_set_;
379   XmlCompatVersioner::Rules rules_;
380 };
381 
ResourceFileFlattener(const ResourceFileFlattenerOptions & options,IAaptContext * context,proguard::KeepSet * keep_set)382 ResourceFileFlattener::ResourceFileFlattener(const ResourceFileFlattenerOptions& options,
383                                              IAaptContext* context, proguard::KeepSet* keep_set)
384     : options_(options), context_(context), keep_set_(keep_set) {
385   SymbolTable* symm = context_->GetExternalSymbols();
386 
387   // Build up the rules for degrading newer attributes to older ones.
388   // NOTE(adamlesinski): These rules are hardcoded right now, but they should be
389   // generated from the attribute definitions themselves (b/62028956).
390   if (const SymbolTable::Symbol* s = symm->FindById(R::attr::paddingHorizontal)) {
391     std::vector<ReplacementAttr> replacements{
392         {"paddingLeft", R::attr::paddingLeft, Attribute(android::ResTable_map::TYPE_DIMENSION)},
393         {"paddingRight", R::attr::paddingRight, Attribute(android::ResTable_map::TYPE_DIMENSION)},
394     };
395     rules_[R::attr::paddingHorizontal] =
396         util::make_unique<DegradeToManyRule>(std::move(replacements));
397   }
398 
399   if (const SymbolTable::Symbol* s = symm->FindById(R::attr::paddingVertical)) {
400     std::vector<ReplacementAttr> replacements{
401         {"paddingTop", R::attr::paddingTop, Attribute(android::ResTable_map::TYPE_DIMENSION)},
402         {"paddingBottom", R::attr::paddingBottom, Attribute(android::ResTable_map::TYPE_DIMENSION)},
403     };
404     rules_[R::attr::paddingVertical] =
405         util::make_unique<DegradeToManyRule>(std::move(replacements));
406   }
407 
408   if (const SymbolTable::Symbol* s = symm->FindById(R::attr::layout_marginHorizontal)) {
409     std::vector<ReplacementAttr> replacements{
410         {"layout_marginLeft", R::attr::layout_marginLeft,
411          Attribute(android::ResTable_map::TYPE_DIMENSION)},
412         {"layout_marginRight", R::attr::layout_marginRight,
413          Attribute(android::ResTable_map::TYPE_DIMENSION)},
414     };
415     rules_[R::attr::layout_marginHorizontal] =
416         util::make_unique<DegradeToManyRule>(std::move(replacements));
417   }
418 
419   if (const SymbolTable::Symbol* s = symm->FindById(R::attr::layout_marginVertical)) {
420     std::vector<ReplacementAttr> replacements{
421         {"layout_marginTop", R::attr::layout_marginTop,
422          Attribute(android::ResTable_map::TYPE_DIMENSION)},
423         {"layout_marginBottom", R::attr::layout_marginBottom,
424          Attribute(android::ResTable_map::TYPE_DIMENSION)},
425     };
426     rules_[R::attr::layout_marginVertical] =
427         util::make_unique<DegradeToManyRule>(std::move(replacements));
428   }
429 }
430 
IsTransitionElement(const std::string & name)431 static bool IsTransitionElement(const std::string& name) {
432   return name == "fade" || name == "changeBounds" || name == "slide" || name == "explode" ||
433          name == "changeImageTransform" || name == "changeTransform" ||
434          name == "changeClipBounds" || name == "autoTransition" || name == "recolor" ||
435          name == "changeScroll" || name == "transitionSet" || name == "transition" ||
436          name == "transitionManager";
437 }
438 
IsVectorElement(const std::string & name)439 static bool IsVectorElement(const std::string& name) {
440   return name == "vector" || name == "animated-vector" || name == "pathInterpolator" ||
441          name == "objectAnimator" || name == "gradient" || name == "animated-selector" ||
442          name == "set";
443 }
444 
445 template <typename T>
make_singleton_vec(T && val)446 std::vector<T> make_singleton_vec(T&& val) {
447   std::vector<T> vec;
448   vec.emplace_back(std::forward<T>(val));
449   return vec;
450 }
451 
LinkAndVersionXmlFile(ResourceTable * table,FileOperation * file_op)452 std::vector<std::unique_ptr<xml::XmlResource>> ResourceFileFlattener::LinkAndVersionXmlFile(
453     ResourceTable* table, FileOperation* file_op) {
454   TRACE_CALL();
455   xml::XmlResource* doc = file_op->xml_to_flatten.get();
456   const android::Source& src = doc->file.source;
457 
458   if (context_->IsVerbose()) {
459     context_->GetDiagnostics()->Note(android::DiagMessage()
460                                      << "linking " << src.path << " (" << doc->file.name << ")");
461   }
462 
463   // First, strip out any tools namespace attributes. AAPT stripped them out early, which means
464   // that existing projects have out-of-date references which pass compilation.
465   xml::StripAndroidStudioAttributes(doc->root.get());
466 
467   XmlReferenceLinker xml_linker(table);
468   if (!options_.do_not_fail_on_missing_resources && !xml_linker.Consume(context_, doc)) {
469     return {};
470   }
471 
472   if (options_.update_proguard_spec && !proguard::CollectProguardRules(context_, doc, keep_set_)) {
473     return {};
474   }
475 
476   if (options_.no_xml_namespaces) {
477     XmlNamespaceRemover namespace_remover;
478     if (!namespace_remover.Consume(context_, doc)) {
479       return {};
480     }
481   }
482 
483   if (options_.no_auto_version) {
484     return make_singleton_vec(std::move(file_op->xml_to_flatten));
485   }
486 
487   if (options_.no_version_vectors || options_.no_version_transitions) {
488     // Skip this if it is a vector or animated-vector.
489     xml::Element* el = doc->root.get();
490     if (el && el->namespace_uri.empty()) {
491       if ((options_.no_version_vectors && IsVectorElement(el->name)) ||
492           (options_.no_version_transitions && IsTransitionElement(el->name))) {
493         return make_singleton_vec(std::move(file_op->xml_to_flatten));
494       }
495     }
496   }
497 
498   const ConfigDescription& config = file_op->config;
499   ResourceEntry* entry = file_op->entry;
500 
501   XmlCompatVersioner xml_compat_versioner(&rules_);
502   const util::Range<ApiVersion> api_range{config.sdkVersion,
503                                           FindNextApiVersionForConfig(entry, config)};
504   return xml_compat_versioner.Process(context_, doc, api_range);
505 }
506 
XmlFileTypeForOutputFormat(OutputFormat format)507 ResourceFile::Type XmlFileTypeForOutputFormat(OutputFormat format) {
508   switch (format) {
509     case OutputFormat::kApk:
510       return ResourceFile::Type::kBinaryXml;
511     case OutputFormat::kProto:
512       return ResourceFile::Type::kProtoXml;
513   }
514   LOG_ALWAYS_FATAL("unreachable");
515   return ResourceFile::Type::kUnknown;
516 }
517 
518 static auto kDrawableVersions = std::map<std::string, ApiVersion>{
519     { "adaptive-icon" , SDK_O },
520 };
521 
Flatten(ResourceTable * table,IArchiveWriter * archive_writer)522 bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archive_writer) {
523   TRACE_CALL();
524   bool error = false;
525   std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> config_sorted_files;
526 
527   proguard::CollectResourceReferences(context_, table, keep_set_);
528 
529   for (auto& pkg : table->packages) {
530     CHECK(!pkg->name.empty()) << "Packages must have names when being linked";
531 
532     for (auto& type : pkg->types) {
533       // Sort by config and name, so that we get better locality in the zip file.
534       config_sorted_files.clear();
535       std::queue<FileOperation> file_operations;
536 
537       // Populate the queue with all files in the ResourceTable.
538       for (auto& entry : type->entries) {
539         for (auto& config_value : entry->values) {
540           // WARNING! Do not insert or remove any resources while executing in this scope. It will
541           // corrupt the iteration order.
542 
543           FileReference* file_ref = ValueCast<FileReference>(config_value->value.get());
544           if (!file_ref) {
545             continue;
546           }
547 
548           io::IFile* file = file_ref->file;
549           if (!file) {
550             context_->GetDiagnostics()->Error(android::DiagMessage(file_ref->GetSource())
551                                               << "file not found");
552             return false;
553           }
554 
555           FileOperation file_op;
556           file_op.entry = entry.get();
557           file_op.dst_path = *file_ref->path;
558           file_op.config = config_value->config;
559           file_op.file_to_copy = file;
560 
561           if (type->named_type.type != ResourceType::kRaw &&
562               (file_ref->type == ResourceFile::Type::kBinaryXml ||
563                file_ref->type == ResourceFile::Type::kProtoXml)) {
564             std::unique_ptr<io::IData> data = file->OpenAsData();
565             if (!data) {
566               context_->GetDiagnostics()->Error(android::DiagMessage(file->GetSource())
567                                                 << "failed to open file");
568               return false;
569             }
570 
571             if (file_ref->type == ResourceFile::Type::kProtoXml) {
572               pb::XmlNode pb_xml_node;
573               if (!pb_xml_node.ParseFromArray(data->data(), static_cast<int>(data->size()))) {
574                 context_->GetDiagnostics()->Error(android::DiagMessage(file->GetSource())
575                                                   << "failed to parse proto XML");
576                 return false;
577               }
578 
579               std::string error;
580               file_op.xml_to_flatten = DeserializeXmlResourceFromPb(pb_xml_node, &error);
581               if (file_op.xml_to_flatten == nullptr) {
582                 context_->GetDiagnostics()->Error(android::DiagMessage(file->GetSource())
583                                                   << "failed to deserialize proto XML: " << error);
584                 return false;
585               }
586             } else {
587               std::string error_str;
588               file_op.xml_to_flatten = xml::Inflate(data->data(), data->size(), &error_str);
589               if (file_op.xml_to_flatten == nullptr) {
590                 context_->GetDiagnostics()->Error(android::DiagMessage(file->GetSource())
591                                                   << "failed to parse binary XML: " << error_str);
592                 return false;
593               }
594             }
595 
596             // Update the type that this file will be written as.
597             file_ref->type = XmlFileTypeForOutputFormat(options_.output_format);
598 
599             file_op.xml_to_flatten->file.config = config_value->config;
600             file_op.xml_to_flatten->file.source = file_ref->GetSource();
601             file_op.xml_to_flatten->file.name =
602                 ResourceName(pkg->name, type->named_type, entry->name);
603           }
604 
605           // NOTE(adamlesinski): Explicitly construct a StringPiece here, or
606           // else we end up copying the string in the std::make_pair() method,
607           // then creating a StringPiece from the copy, which would cause us
608           // to end up referencing garbage in the map.
609           const StringPiece entry_name(entry->name);
610           config_sorted_files[std::make_pair(config_value->config, entry_name)] =
611               std::move(file_op);
612         }
613       }
614 
615       // Now flatten the sorted values.
616       for (auto& map_entry : config_sorted_files) {
617         const ConfigDescription& config = map_entry.first.first;
618         FileOperation& file_op = map_entry.second;
619 
620         if (file_op.xml_to_flatten) {
621           // Check minimum sdk versions supported for drawables
622           auto drawable_entry = kDrawableVersions.find(file_op.xml_to_flatten->root->name);
623           if (drawable_entry != kDrawableVersions.end()) {
624             if (drawable_entry->second > context_->GetMinSdkVersion()
625                 && drawable_entry->second > config.sdkVersion) {
626               context_->GetDiagnostics()->Error(
627                   android::DiagMessage(file_op.xml_to_flatten->file.source)
628                   << "<" << drawable_entry->first << "> elements "
629                   << "require a sdk version of at least " << (int16_t)drawable_entry->second);
630               error = true;
631               continue;
632             }
633           }
634 
635           std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
636               LinkAndVersionXmlFile(table, &file_op);
637           if (versioned_docs.empty()) {
638             error = true;
639             continue;
640           }
641 
642           for (std::unique_ptr<xml::XmlResource>& doc : versioned_docs) {
643             std::string dst_path = file_op.dst_path;
644             if (doc->file.config != file_op.config) {
645               // Only add the new versioned configurations.
646               if (context_->IsVerbose()) {
647                 context_->GetDiagnostics()->Note(android::DiagMessage(doc->file.source)
648                                                  << "auto-versioning resource from config '"
649                                                  << config << "' -> '" << doc->file.config << "'");
650               }
651 
652               const ResourceFile& file = doc->file;
653               dst_path = ResourceUtils::BuildResourceFileName(file, context_->GetNameMangler());
654 
655               auto file_ref =
656                   util::make_unique<FileReference>(table->string_pool.MakeRef(dst_path));
657               file_ref->SetSource(doc->file.source);
658 
659               // Update the output format of this XML file.
660               file_ref->type = XmlFileTypeForOutputFormat(options_.output_format);
661               bool result = table->AddResource(NewResourceBuilder(file.name)
662                                                    .SetValue(std::move(file_ref), file.config)
663                                                    .SetAllowMangled(true)
664                                                    .Build(),
665                                                context_->GetDiagnostics());
666               if (!result) {
667                 return false;
668               }
669             }
670 
671             error |= !FlattenXml(context_, *doc, dst_path, options_.keep_raw_values,
672                                  false /*utf16*/, options_.output_format, archive_writer);
673           }
674         } else {
675           error |= !io::CopyFileToArchive(context_, file_op.file_to_copy, file_op.dst_path,
676                                           GetCompressionFlags(file_op.dst_path, options_),
677                                           archive_writer);
678         }
679       }
680     }
681   }
682   return !error;
683 }
684 
WriteStableIdMapToPath(android::IDiagnostics * diag,const std::unordered_map<ResourceName,ResourceId> & id_map,const std::string & id_map_path)685 static bool WriteStableIdMapToPath(android::IDiagnostics* diag,
686                                    const std::unordered_map<ResourceName, ResourceId>& id_map,
687                                    const std::string& id_map_path) {
688   io::FileOutputStream fout(id_map_path);
689   if (fout.HadError()) {
690     diag->Error(android::DiagMessage(id_map_path) << "failed to open: " << fout.GetError());
691     return false;
692   }
693 
694   text::Printer printer(&fout);
695   for (const auto& entry : id_map) {
696     const ResourceName& name = entry.first;
697     const ResourceId& id = entry.second;
698     printer.Print(name.to_string());
699     printer.Print(" = ");
700     printer.Println(id.to_string());
701   }
702   fout.Flush();
703 
704   if (fout.HadError()) {
705     diag->Error(android::DiagMessage(id_map_path) << "failed writing to file: " << fout.GetError());
706     return false;
707   }
708   return true;
709 }
710 
LoadStableIdMap(android::IDiagnostics * diag,const std::string & path,std::unordered_map<ResourceName,ResourceId> * out_id_map)711 static bool LoadStableIdMap(android::IDiagnostics* diag, const std::string& path,
712                             std::unordered_map<ResourceName, ResourceId>* out_id_map) {
713   std::string content;
714   if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) {
715     diag->Error(android::DiagMessage(path) << "failed reading stable ID file");
716     return false;
717   }
718 
719   out_id_map->clear();
720   size_t line_no = 0;
721   for (StringPiece line : util::Tokenize(content, '\n')) {
722     line_no++;
723     line = util::TrimWhitespace(line);
724     if (line.empty()) {
725       continue;
726     }
727 
728     auto iter = std::find(line.begin(), line.end(), '=');
729     if (iter == line.end()) {
730       diag->Error(android::DiagMessage(android::Source(path, line_no)) << "missing '='");
731       return false;
732     }
733 
734     ResourceNameRef name;
735     StringPiece res_name_str =
736         util::TrimWhitespace(line.substr(0, std::distance(line.begin(), iter)));
737     if (!ResourceUtils::ParseResourceName(res_name_str, &name)) {
738       diag->Error(android::DiagMessage(android::Source(path, line_no))
739                   << "invalid resource name '" << res_name_str << "'");
740       return false;
741     }
742 
743     const size_t res_id_start_idx = std::distance(line.begin(), iter) + 1;
744     const size_t res_id_str_len = line.size() - res_id_start_idx;
745     StringPiece res_id_str = util::TrimWhitespace(line.substr(res_id_start_idx, res_id_str_len));
746 
747     std::optional<ResourceId> maybe_id = ResourceUtils::ParseResourceId(res_id_str);
748     if (!maybe_id) {
749       diag->Error(android::DiagMessage(android::Source(path, line_no))
750                   << "invalid resource ID '" << res_id_str << "'");
751       return false;
752     }
753 
754     (*out_id_map)[name.ToResourceName()] = maybe_id.value();
755   }
756   return true;
757 }
758 
759 class Linker {
760  public:
Linker(LinkContext * context,const LinkOptions & options)761   Linker(LinkContext* context, const LinkOptions& options)
762       : options_(options),
763         context_(context),
764         final_table_(),
765         file_collection_(util::make_unique<io::FileCollection>()) {
766   }
767 
ExtractCompileSdkVersions(android::AssetManager2 * assets)768   void ExtractCompileSdkVersions(android::AssetManager2* assets) {
769     using namespace android;
770 
771     // Find the system package (0x01). AAPT always generates attributes with the type 0x01, so
772     // we're looking for the first attribute resource in the system package.
773     android::ApkAssetsCookie cookie;
774     if (auto value = assets->GetResource(0x01010000, true /** may_be_bag */); value.has_value()) {
775       cookie = value->cookie;
776     } else {
777       // No Framework assets loaded. Not a failure.
778       return;
779     }
780 
781     std::unique_ptr<Asset> manifest(
782         assets->OpenNonAsset(kAndroidManifestPath, cookie, Asset::AccessMode::ACCESS_BUFFER));
783     if (manifest == nullptr) {
784       // No errors.
785       return;
786     }
787 
788     std::string error;
789     std::unique_ptr<xml::XmlResource> manifest_xml =
790         xml::Inflate(manifest->getBuffer(true /*wordAligned*/), manifest->getLength(), &error);
791     if (manifest_xml == nullptr) {
792       // No errors.
793       return;
794     }
795 
796     if (!options_.manifest_fixer_options.compile_sdk_version) {
797       xml::Attribute* attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionCode");
798       if (attr != nullptr) {
799         auto& compile_sdk_version = options_.manifest_fixer_options.compile_sdk_version;
800         if (BinaryPrimitive* prim = ValueCast<BinaryPrimitive>(attr->compiled_value.get())) {
801           switch (prim->value.dataType) {
802             case Res_value::TYPE_INT_DEC:
803               compile_sdk_version = StringPrintf("%" PRId32, static_cast<int32_t>(prim->value.data));
804               break;
805             case Res_value::TYPE_INT_HEX:
806               compile_sdk_version = StringPrintf("%" PRIx32, prim->value.data);
807               break;
808             default:
809               break;
810           }
811         } else if (String* str = ValueCast<String>(attr->compiled_value.get())) {
812           compile_sdk_version = *str->value;
813         } else {
814           compile_sdk_version = attr->value;
815         }
816       }
817     }
818 
819     if (!options_.manifest_fixer_options.compile_sdk_version_codename) {
820       xml::Attribute* attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionName");
821       if (attr != nullptr) {
822         std::optional<std::string>& compile_sdk_version_codename =
823             options_.manifest_fixer_options.compile_sdk_version_codename;
824         if (String* str = ValueCast<String>(attr->compiled_value.get())) {
825           compile_sdk_version_codename = *str->value;
826         } else {
827           compile_sdk_version_codename = attr->value;
828         }
829       }
830     }
831   }
832 
833   // Creates a SymbolTable that loads symbols from the various APKs.
834   // Pre-condition: context_->GetCompilationPackage() needs to be set.
LoadSymbolsFromIncludePaths()835   bool LoadSymbolsFromIncludePaths() {
836     TRACE_NAME("LoadSymbolsFromIncludePaths: #" + std::to_string(options_.include_paths.size()));
837     auto asset_source = util::make_unique<AssetManagerSymbolSource>();
838     for (const std::string& path : options_.include_paths) {
839       if (context_->IsVerbose()) {
840         context_->GetDiagnostics()->Note(android::DiagMessage() << "including " << path);
841       }
842 
843       std::string error;
844       auto zip_collection = io::ZipFileCollection::Create(path, &error);
845       if (zip_collection == nullptr) {
846         context_->GetDiagnostics()->Error(android::DiagMessage()
847                                           << "failed to open APK: " << error);
848         return false;
849       }
850 
851       if (zip_collection->FindFile(kProtoResourceTablePath) != nullptr) {
852         // Load this as a static library include.
853         std::unique_ptr<LoadedApk> static_apk = LoadedApk::LoadProtoApkFromFileCollection(
854             android::Source(path), std::move(zip_collection), context_->GetDiagnostics());
855         if (static_apk == nullptr) {
856           return false;
857         }
858 
859         if (context_->GetPackageType() != PackageType::kStaticLib) {
860           // Can't include static libraries when not building a static library (they have no IDs
861           // assigned).
862           context_->GetDiagnostics()->Error(
863               android::DiagMessage(path)
864               << "can't include static library when not building a static lib");
865           return false;
866         }
867 
868         ResourceTable* table = static_apk->GetResourceTable();
869 
870         // If we are using --no-static-lib-packages, we need to rename the package of this table to
871         // our compilation package so the symbol package name does not get mangled into the entry
872         // name.
873         if (options_.no_static_lib_packages && !table->packages.empty()) {
874           auto lib_package_result = GetStaticLibraryPackage(table);
875           if (!lib_package_result.has_value()) {
876             context_->GetDiagnostics()->Error(android::DiagMessage(path)
877                                               << lib_package_result.error());
878             return false;
879           }
880           lib_package_result.value()->name = context_->GetCompilationPackage();
881         }
882 
883         context_->GetExternalSymbols()->AppendSource(
884             util::make_unique<ResourceTableSymbolSource>(table));
885         static_library_includes_.push_back(std::move(static_apk));
886       } else {
887         if (!asset_source->AddAssetPath(path)) {
888           context_->GetDiagnostics()->Error(android::DiagMessage()
889                                             << "failed to load include path " << path);
890           return false;
891         }
892       }
893     }
894 
895     // Capture the shared libraries so that the final resource table can be properly flattened
896     // with support for shared libraries.
897     for (auto& entry : asset_source->GetAssignedPackageIds()) {
898       if (entry.first == kAppPackageId) {
899         // Capture the included base feature package.
900         included_feature_base_ = entry.second;
901       } else if (entry.first == kFrameworkPackageId) {
902         // Try to embed which version of the framework we're compiling against.
903         // First check if we should use compileSdkVersion at all. Otherwise compilation may fail
904         // when linking our synthesized 'android:compileSdkVersion' attribute.
905         std::unique_ptr<SymbolTable::Symbol> symbol = asset_source->FindByName(
906             ResourceName("android", ResourceType::kAttr, "compileSdkVersion"));
907         if (symbol != nullptr && symbol->is_public) {
908           // The symbol is present and public, extract the android:versionName and
909           // android:versionCode from the framework AndroidManifest.xml.
910           ExtractCompileSdkVersions(asset_source->GetAssetManager());
911         }
912       } else if (asset_source->IsPackageDynamic(entry.first, entry.second)) {
913         final_table_.included_packages_[entry.first] = entry.second;
914       }
915     }
916 
917     context_->GetExternalSymbols()->AppendSource(std::move(asset_source));
918     return true;
919   }
920 
ExtractAppInfoFromManifest(xml::XmlResource * xml_res,android::IDiagnostics * diag)921   std::optional<AppInfo> ExtractAppInfoFromManifest(xml::XmlResource* xml_res,
922                                                     android::IDiagnostics* diag) {
923     TRACE_CALL();
924     // Make sure the first element is <manifest> with package attribute.
925     xml::Element* manifest_el = xml::FindRootElement(xml_res->root.get());
926     if (manifest_el == nullptr) {
927       return {};
928     }
929 
930     AppInfo app_info;
931 
932     if (!manifest_el->namespace_uri.empty() || manifest_el->name != "manifest") {
933       diag->Error(android::DiagMessage(xml_res->file.source) << "root tag must be <manifest>");
934       return {};
935     }
936 
937     xml::Attribute* package_attr = manifest_el->FindAttribute({}, "package");
938     if (!package_attr) {
939       diag->Error(android::DiagMessage(xml_res->file.source)
940                   << "<manifest> must have a 'package' attribute");
941       return {};
942     }
943     app_info.package = package_attr->value;
944 
945     if (xml::Attribute* version_code_attr =
946             manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) {
947       std::optional<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_attr->value);
948       if (!maybe_code) {
949         diag->Error(android::DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
950                     << "invalid android:versionCode '" << version_code_attr->value << "'");
951         return {};
952       }
953       app_info.version_code = maybe_code.value();
954     }
955 
956     if (xml::Attribute* version_code_major_attr =
957         manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor")) {
958       std::optional<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_major_attr->value);
959       if (!maybe_code) {
960         diag->Error(android::DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
961                     << "invalid android:versionCodeMajor '" << version_code_major_attr->value
962                     << "'");
963         return {};
964       }
965       app_info.version_code_major = maybe_code.value();
966     }
967 
968     if (xml::Attribute* revision_code_attr =
969             manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) {
970       std::optional<uint32_t> maybe_code = ResourceUtils::ParseInt(revision_code_attr->value);
971       if (!maybe_code) {
972         diag->Error(android::DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
973                     << "invalid android:revisionCode '" << revision_code_attr->value << "'");
974         return {};
975       }
976       app_info.revision_code = maybe_code.value();
977     }
978 
979     if (xml::Attribute* split_name_attr = manifest_el->FindAttribute({}, "split")) {
980       if (!split_name_attr->value.empty()) {
981         app_info.split_name = split_name_attr->value;
982       }
983     }
984 
985     if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) {
986       if (xml::Attribute* min_sdk =
987               uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) {
988         app_info.min_sdk_version = ResourceUtils::ParseSdkVersion(min_sdk->value);
989       }
990     }
991 
992     for (const xml::Element* child_el : manifest_el->GetChildElements()) {
993       if (child_el->namespace_uri.empty() && child_el->name == "uses-split") {
994         if (const xml::Attribute* split_name =
995             child_el->FindAttribute(xml::kSchemaAndroid, "name")) {
996           if (!split_name->value.empty()) {
997             app_info.split_name_dependencies.insert(split_name->value);
998           }
999         }
1000       }
1001     }
1002     return app_info;
1003   }
1004 
1005   // Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
1006   // Postcondition: ResourceTable has only one package left. All others are
1007   // stripped, or there is an error and false is returned.
VerifyNoExternalPackages()1008   bool VerifyNoExternalPackages() {
1009     auto is_ext_package_func = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
1010       return context_->GetCompilationPackage() != pkg->name;
1011     };
1012 
1013     bool error = false;
1014     for (const auto& package : final_table_.packages) {
1015       if (is_ext_package_func(package)) {
1016         // We have a package that is not related to the one we're building!
1017         for (const auto& type : package->types) {
1018           for (const auto& entry : type->entries) {
1019             ResourceNameRef res_name(package->name, type->named_type, entry->name);
1020 
1021             for (const auto& config_value : entry->values) {
1022               // Special case the occurrence of an ID that is being generated
1023               // for the 'android' package. This is due to legacy reasons.
1024               if (ValueCast<Id>(config_value->value.get()) && package->name == "android") {
1025                 context_->GetDiagnostics()->Warn(
1026                     android::DiagMessage(config_value->value->GetSource())
1027                     << "generated id '" << res_name << "' for external package '" << package->name
1028                     << "'");
1029               } else {
1030                 context_->GetDiagnostics()->Error(
1031                     android::DiagMessage(config_value->value->GetSource())
1032                     << "defined resource '" << res_name << "' for external package '"
1033                     << package->name << "'");
1034                 error = true;
1035               }
1036             }
1037           }
1038         }
1039       }
1040     }
1041 
1042     auto new_end_iter = std::remove_if(final_table_.packages.begin(), final_table_.packages.end(),
1043                                        is_ext_package_func);
1044     final_table_.packages.erase(new_end_iter, final_table_.packages.end());
1045     return !error;
1046   }
1047 
1048   /**
1049    * Returns true if no IDs have been set, false otherwise.
1050    */
VerifyNoIdsSet()1051   bool VerifyNoIdsSet() {
1052     for (const auto& package : final_table_.packages) {
1053       for (const auto& type : package->types) {
1054         for (const auto& entry : type->entries) {
1055           if (entry->id) {
1056             ResourceNameRef res_name(package->name, type->named_type, entry->name);
1057             context_->GetDiagnostics()->Error(android::DiagMessage()
1058                                               << "resource " << res_name << " has ID "
1059                                               << entry->id.value() << " assigned");
1060             return false;
1061           }
1062         }
1063       }
1064     }
1065     return true;
1066   }
1067 
VerifyLocaleFormat(xml::XmlResource * manifest,android::IDiagnostics * diag)1068   bool VerifyLocaleFormat(xml::XmlResource* manifest, android::IDiagnostics* diag) {
1069     // Skip it if the Manifest doesn't declare the localeConfig attribute within the <application>
1070     // element.
1071     const xml::Element* application = manifest->root->FindChild("", "application");
1072     if (!application) {
1073       return true;
1074     }
1075     const xml::Attribute* localeConfig =
1076         application->FindAttribute(xml::kSchemaAndroid, "localeConfig");
1077     if (!localeConfig) {
1078       return true;
1079     }
1080 
1081     // Deserialize XML from the compiled file
1082     if (localeConfig->compiled_value) {
1083       const auto localeconfig_reference = ValueCast<Reference>(localeConfig->compiled_value.get());
1084       const auto localeconfig_entry =
1085           ResolveTableEntry(context_, &final_table_, localeconfig_reference);
1086       if (!localeconfig_entry) {
1087         // If locale config is resolved from external symbols - skip validation.
1088         if (context_->GetExternalSymbols()->FindByReference(*localeconfig_reference)) {
1089           return true;
1090         }
1091         context_->GetDiagnostics()->Error(
1092             android::DiagMessage(localeConfig->compiled_value->GetSource())
1093             << "no localeConfig entry");
1094         return false;
1095       }
1096       for (const auto& value : localeconfig_entry->values) {
1097         const FileReference* file_ref = ValueCast<FileReference>(value->value.get());
1098         if (!file_ref) {
1099           context_->GetDiagnostics()->Error(
1100               android::DiagMessage(localeConfig->compiled_value->GetSource())
1101               << "no file reference");
1102           return false;
1103         }
1104         io::IFile* file = file_ref->file;
1105         if (!file) {
1106           context_->GetDiagnostics()->Error(android::DiagMessage(file_ref->GetSource())
1107                                             << "file not found");
1108           return false;
1109         }
1110         std::unique_ptr<io::IData> data = file->OpenAsData();
1111         if (!data) {
1112           context_->GetDiagnostics()->Error(android::DiagMessage(file->GetSource())
1113                                             << "failed to open file");
1114           return false;
1115         }
1116         pb::XmlNode pb_xml_node;
1117         if (!pb_xml_node.ParseFromArray(data->data(), static_cast<int>(data->size()))) {
1118           context_->GetDiagnostics()->Error(android::DiagMessage(file->GetSource())
1119                                             << "failed to parse proto XML");
1120           return false;
1121         }
1122 
1123         std::string error;
1124         std::unique_ptr<xml::XmlResource> localeConfig_xml =
1125             DeserializeXmlResourceFromPb(pb_xml_node, &error);
1126         if (!localeConfig_xml) {
1127           context_->GetDiagnostics()->Error(android::DiagMessage(file->GetSource())
1128                                             << "failed to deserialize proto XML: " << error);
1129           return false;
1130         }
1131         xml::Element* localeConfig_el = xml::FindRootElement(localeConfig_xml->root.get());
1132         if (!localeConfig_el) {
1133           diag->Error(android::DiagMessage(file->GetSource()) << "no root tag defined");
1134           return false;
1135         }
1136         if (localeConfig_el->name != "locale-config") {
1137           diag->Error(android::DiagMessage(file->GetSource())
1138                       << "invalid element name: " << localeConfig_el->name
1139                       << ", expected: locale-config");
1140           return false;
1141         }
1142         for (const xml::Element* child_el : localeConfig_el->GetChildElements()) {
1143           if (child_el->name == "locale") {
1144             if (const xml::Attribute* locale_name_attr =
1145                     child_el->FindAttribute(xml::kSchemaAndroid, "name")) {
1146               const std::string& locale_name = locale_name_attr->value;
1147               const std::string valid_name = ConvertToBCP47Tag(locale_name);
1148               // Start to verify the locale format
1149               ConfigDescription config;
1150               if (!ConfigDescription::Parse(valid_name, &config)) {
1151                 diag->Error(android::DiagMessage(file->GetSource())
1152                             << "invalid configuration: " << locale_name);
1153                 return false;
1154               }
1155             } else {
1156               diag->Error(android::DiagMessage(file->GetSource())
1157                           << "the attribute android:name is not found");
1158               return false;
1159             }
1160           } else {
1161             diag->Error(android::DiagMessage(file->GetSource())
1162                         << "invalid element name: " << child_el->name << ", expected: locale");
1163             return false;
1164           }
1165         }
1166       }
1167     }
1168     return true;
1169   }
1170 
ConvertToBCP47Tag(const std::string & locale)1171   std::string ConvertToBCP47Tag(const std::string& locale) {
1172     std::string bcp47tag = "b+";
1173     bcp47tag += locale;
1174     std::replace(bcp47tag.begin(), bcp47tag.end(), '-', '+');
1175     return bcp47tag;
1176   }
1177 
MakeArchiveWriter(StringPiece out)1178   std::unique_ptr<IArchiveWriter> MakeArchiveWriter(StringPiece out) {
1179     if (options_.output_to_directory) {
1180       return CreateDirectoryArchiveWriter(context_->GetDiagnostics(), out);
1181     } else {
1182       return CreateZipFileArchiveWriter(context_->GetDiagnostics(), out);
1183     }
1184   }
1185 
FlattenTable(ResourceTable * table,OutputFormat format,IArchiveWriter * writer)1186   bool FlattenTable(ResourceTable* table, OutputFormat format, IArchiveWriter* writer) {
1187     TRACE_CALL();
1188     switch (format) {
1189       case OutputFormat::kApk: {
1190         android::BigBuffer buffer(1024);
1191         TableFlattener flattener(options_.table_flattener_options, &buffer);
1192         if (!flattener.Consume(context_, table)) {
1193           context_->GetDiagnostics()->Error(android::DiagMessage()
1194                                             << "failed to flatten resource table");
1195           return false;
1196         }
1197 
1198         io::BigBufferInputStream input_stream(&buffer);
1199         return io::CopyInputStreamToArchive(context_, &input_stream, kApkResourceTablePath,
1200                                             ArchiveEntry::kAlign, writer);
1201       } break;
1202 
1203       case OutputFormat::kProto: {
1204         pb::ResourceTable pb_table;
1205         SerializeTableToPb(*table, &pb_table, context_->GetDiagnostics(),
1206                            options_.proto_table_flattener_options);
1207         return io::CopyProtoToArchive(context_, &pb_table, kProtoResourceTablePath,
1208                                       ArchiveEntry::kCompress, writer);
1209       } break;
1210     }
1211     return false;
1212   }
1213 
WriteJavaFile(ResourceTable * table,StringPiece package_name_to_generate,StringPiece out_package,const JavaClassGeneratorOptions & java_options,const std::optional<std::string> & out_text_symbols_path={})1214   bool WriteJavaFile(ResourceTable* table, StringPiece package_name_to_generate,
1215                      StringPiece out_package, const JavaClassGeneratorOptions& java_options,
1216                      const std::optional<std::string>& out_text_symbols_path = {}) {
1217     if (!options_.generate_java_class_path && !out_text_symbols_path) {
1218       return true;
1219     }
1220 
1221     std::string out_path;
1222     std::unique_ptr<io::FileOutputStream> fout;
1223     if (options_.generate_java_class_path) {
1224       out_path = options_.generate_java_class_path.value();
1225       file::AppendPath(&out_path, file::PackageToPath(out_package));
1226       if (!file::mkdirs(out_path)) {
1227         context_->GetDiagnostics()->Error(android::DiagMessage()
1228                                           << "failed to create directory '" << out_path << "'");
1229         return false;
1230       }
1231 
1232       file::AppendPath(&out_path, "R.java");
1233 
1234       fout = util::make_unique<io::FileOutputStream>(out_path);
1235       if (fout->HadError()) {
1236         context_->GetDiagnostics()->Error(android::DiagMessage()
1237                                           << "failed writing to '" << out_path
1238                                           << "': " << fout->GetError());
1239         return false;
1240       }
1241     }
1242 
1243     std::unique_ptr<io::FileOutputStream> fout_text;
1244     if (out_text_symbols_path) {
1245       fout_text = util::make_unique<io::FileOutputStream>(out_text_symbols_path.value());
1246       if (fout_text->HadError()) {
1247         context_->GetDiagnostics()->Error(android::DiagMessage()
1248                                           << "failed writing to '" << out_text_symbols_path.value()
1249                                           << "': " << fout_text->GetError());
1250         return false;
1251       }
1252     }
1253 
1254     JavaClassGenerator generator(context_, table, java_options);
1255     if (!generator.Generate(package_name_to_generate, out_package, fout.get(), fout_text.get())) {
1256       context_->GetDiagnostics()->Error(android::DiagMessage(out_path) << generator.GetError());
1257       return false;
1258     }
1259 
1260     return true;
1261   }
1262 
GenerateJavaClasses()1263   bool GenerateJavaClasses() {
1264     TRACE_CALL();
1265     // The set of packages whose R class to call in the main classes onResourcesLoaded callback.
1266     std::vector<std::string> packages_to_callback;
1267 
1268     JavaClassGeneratorOptions template_options;
1269     template_options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
1270     template_options.javadoc_annotations = options_.javadoc_annotations;
1271 
1272     if (context_->GetPackageType() == PackageType::kStaticLib || options_.generate_non_final_ids) {
1273       template_options.use_final = false;
1274     }
1275 
1276     if (context_->GetPackageType() == PackageType::kSharedLib) {
1277       template_options.use_final = false;
1278       template_options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{};
1279     }
1280 
1281     const StringPiece actual_package = context_->GetCompilationPackage();
1282     StringPiece output_package = context_->GetCompilationPackage();
1283     if (options_.custom_java_package) {
1284       // Override the output java package to the custom one.
1285       output_package = options_.custom_java_package.value();
1286     }
1287 
1288     // Generate the private symbols if required.
1289     if (options_.private_symbols) {
1290       packages_to_callback.push_back(options_.private_symbols.value());
1291 
1292       // If we defined a private symbols package, we only emit Public symbols
1293       // to the original package, and private and public symbols to the private package.
1294       JavaClassGeneratorOptions options = template_options;
1295       options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
1296       if (!WriteJavaFile(&final_table_, actual_package, options_.private_symbols.value(),
1297                          options)) {
1298         return false;
1299       }
1300     }
1301 
1302     // Generate copies of the original package R class but with different package names.
1303     // This is to support non-namespaced builds.
1304     for (const std::string& extra_package : options_.extra_java_packages) {
1305       packages_to_callback.push_back(extra_package);
1306 
1307       JavaClassGeneratorOptions options = template_options;
1308       options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
1309       if (!WriteJavaFile(&final_table_, actual_package, extra_package, options)) {
1310         return false;
1311       }
1312     }
1313 
1314     // Generate R classes for each package that was merged (static library).
1315     // Use the actual package's resources only.
1316     for (const std::string& package : table_merger_->merged_packages()) {
1317       packages_to_callback.push_back(package);
1318 
1319       JavaClassGeneratorOptions options = template_options;
1320       options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
1321       if (!WriteJavaFile(&final_table_, package, package, options)) {
1322         return false;
1323       }
1324     }
1325 
1326     // Generate the main public R class.
1327     JavaClassGeneratorOptions options = template_options;
1328 
1329     // Only generate public symbols if we have a private package.
1330     if (options_.private_symbols) {
1331       options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
1332     }
1333 
1334     if (options.rewrite_callback_options) {
1335       options.rewrite_callback_options.value().packages_to_callback =
1336           std::move(packages_to_callback);
1337     }
1338 
1339     if (!WriteJavaFile(&final_table_, actual_package, output_package, options,
1340                        options_.generate_text_symbols_path)) {
1341       return false;
1342     }
1343 
1344     return true;
1345   }
1346 
WriteManifestJavaFile(xml::XmlResource * manifest_xml)1347   bool WriteManifestJavaFile(xml::XmlResource* manifest_xml) {
1348     TRACE_CALL();
1349     if (!options_.generate_java_class_path) {
1350       return true;
1351     }
1352 
1353     std::unique_ptr<ClassDefinition> manifest_class =
1354         GenerateManifestClass(context_->GetDiagnostics(), manifest_xml);
1355 
1356     if (!manifest_class) {
1357       // Something bad happened, but we already logged it, so exit.
1358       return false;
1359     }
1360 
1361     if (manifest_class->empty()) {
1362       // Empty Manifest class, no need to generate it.
1363       return true;
1364     }
1365 
1366     // Add any JavaDoc annotations to the generated class.
1367     for (const std::string& annotation : options_.javadoc_annotations) {
1368       std::string proper_annotation = "@";
1369       proper_annotation += annotation;
1370       manifest_class->GetCommentBuilder()->AppendComment(proper_annotation);
1371     }
1372 
1373     const std::string package_utf8 =
1374         options_.custom_java_package.value_or(context_->GetCompilationPackage());
1375 
1376     std::string out_path = options_.generate_java_class_path.value();
1377     file::AppendPath(&out_path, file::PackageToPath(package_utf8));
1378 
1379     if (!file::mkdirs(out_path)) {
1380       context_->GetDiagnostics()->Error(android::DiagMessage()
1381                                         << "failed to create directory '" << out_path << "'");
1382       return false;
1383     }
1384 
1385     file::AppendPath(&out_path, "Manifest.java");
1386 
1387     io::FileOutputStream fout(out_path);
1388     if (fout.HadError()) {
1389       context_->GetDiagnostics()->Error(android::DiagMessage() << "failed to open '" << out_path
1390                                                                << "': " << fout.GetError());
1391       return false;
1392     }
1393 
1394     ClassDefinition::WriteJavaFile(manifest_class.get(), package_utf8, true,
1395                                    false /* strip_api_annotations */, &fout);
1396     fout.Flush();
1397 
1398     if (fout.HadError()) {
1399       context_->GetDiagnostics()->Error(android::DiagMessage() << "failed writing to '" << out_path
1400                                                                << "': " << fout.GetError());
1401       return false;
1402     }
1403     return true;
1404   }
1405 
WriteProguardFile(const std::optional<std::string> & out,const proguard::KeepSet & keep_set)1406   bool WriteProguardFile(const std::optional<std::string>& out, const proguard::KeepSet& keep_set) {
1407     TRACE_CALL();
1408     if (!out) {
1409       return true;
1410     }
1411 
1412     const std::string& out_path = out.value();
1413     io::FileOutputStream fout(out_path);
1414     if (fout.HadError()) {
1415       context_->GetDiagnostics()->Error(android::DiagMessage() << "failed to open '" << out_path
1416                                                                << "': " << fout.GetError());
1417       return false;
1418     }
1419 
1420     proguard::WriteKeepSet(keep_set, &fout, options_.generate_minimal_proguard_rules,
1421                            options_.no_proguard_location_reference);
1422     fout.Flush();
1423 
1424     if (fout.HadError()) {
1425       context_->GetDiagnostics()->Error(android::DiagMessage() << "failed writing to '" << out_path
1426                                                                << "': " << fout.GetError());
1427       return false;
1428     }
1429     return true;
1430   }
1431 
MergeStaticLibrary(const std::string & input,bool override)1432   bool MergeStaticLibrary(const std::string& input, bool override) {
1433     TRACE_CALL();
1434     if (context_->IsVerbose()) {
1435       context_->GetDiagnostics()->Note(android::DiagMessage()
1436                                        << "merging static library " << input);
1437     }
1438 
1439     std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(input, context_->GetDiagnostics());
1440     if (apk == nullptr) {
1441       context_->GetDiagnostics()->Error(android::DiagMessage(input) << "invalid static library");
1442       return false;
1443     }
1444 
1445     ResourceTable* table = apk->GetResourceTable();
1446     if (table->packages.empty()) {
1447       return true;
1448     }
1449 
1450     auto lib_package_result = GetStaticLibraryPackage(table);
1451     if (!lib_package_result.has_value()) {
1452       context_->GetDiagnostics()->Error(android::DiagMessage(input) << lib_package_result.error());
1453       return false;
1454     }
1455 
1456     ResourceTablePackage* pkg = lib_package_result.value();
1457     bool result;
1458     if (options_.no_static_lib_packages) {
1459       // Merge all resources as if they were in the compilation package. This is the old behavior
1460       // of aapt.
1461 
1462       // Add the package to the set of --extra-packages so we emit an R.java for each library
1463       // package.
1464       if (!pkg->name.empty()) {
1465         options_.extra_java_packages.insert(pkg->name);
1466       }
1467 
1468       // Clear the package name, so as to make the resources look like they are coming from the
1469       // local package.
1470       pkg->name = "";
1471       result = table_merger_->Merge(android::Source(input), table, override);
1472 
1473     } else {
1474       // This is the proper way to merge libraries, where the package name is
1475       // preserved and resource names are mangled.
1476       result = table_merger_->MergeAndMangle(android::Source(input), pkg->name, table);
1477     }
1478 
1479     if (!result) {
1480       return false;
1481     }
1482 
1483     // Make sure to move the collection into the set of IFileCollections.
1484     merged_apks_.push_back(std::move(apk));
1485     return true;
1486   }
1487 
MergeExportedSymbols(const android::Source & source,const std::vector<SourcedResourceName> & exported_symbols)1488   bool MergeExportedSymbols(const android::Source& source,
1489                             const std::vector<SourcedResourceName>& exported_symbols) {
1490     TRACE_CALL();
1491     // Add the exports of this file to the table.
1492     for (const SourcedResourceName& exported_symbol : exported_symbols) {
1493       ResourceName res_name = exported_symbol.name;
1494       if (res_name.package.empty()) {
1495         res_name.package = context_->GetCompilationPackage();
1496       }
1497 
1498       std::optional<ResourceName> mangled_name = context_->GetNameMangler()->MangleName(res_name);
1499       if (mangled_name) {
1500         res_name = mangled_name.value();
1501       }
1502 
1503       auto id = util::make_unique<Id>();
1504       id->SetSource(source.WithLine(exported_symbol.line));
1505       bool result = final_table_.AddResource(
1506           NewResourceBuilder(res_name).SetValue(std::move(id)).SetAllowMangled(true).Build(),
1507           context_->GetDiagnostics());
1508       if (!result) {
1509         return false;
1510       }
1511     }
1512     return true;
1513   }
1514 
MergeCompiledFile(const ResourceFile & compiled_file,io::IFile * file,bool override)1515   bool MergeCompiledFile(const ResourceFile& compiled_file, io::IFile* file, bool override) {
1516     TRACE_CALL();
1517     if (context_->IsVerbose()) {
1518       context_->GetDiagnostics()->Note(android::DiagMessage()
1519                                        << "merging '" << compiled_file.name
1520                                        << "' from compiled file " << compiled_file.source);
1521     }
1522 
1523     if (!table_merger_->MergeFile(compiled_file, override, file)) {
1524       return false;
1525     }
1526     return MergeExportedSymbols(compiled_file.source, compiled_file.exported_symbols);
1527   }
1528 
1529   // Takes a path to load as a ZIP file and merges the files within into the main ResourceTable.
1530   // If override is true, conflicting resources are allowed to override each other, in order of last
1531   // seen.
1532   // An io::IFileCollection is created from the ZIP file and added to the set of
1533   // io::IFileCollections that are open.
MergeArchive(const std::string & input,bool override)1534   bool MergeArchive(const std::string& input, bool override) {
1535     TRACE_CALL();
1536     if (context_->IsVerbose()) {
1537       context_->GetDiagnostics()->Note(android::DiagMessage() << "merging archive " << input);
1538     }
1539 
1540     std::string error_str;
1541     std::unique_ptr<io::ZipFileCollection> collection =
1542         io::ZipFileCollection::Create(input, &error_str);
1543     if (!collection) {
1544       context_->GetDiagnostics()->Error(android::DiagMessage(input) << error_str);
1545       return false;
1546     }
1547 
1548     bool error = false;
1549     for (auto iter = collection->Iterator(); iter->HasNext();) {
1550       if (!MergeFile(iter->Next(), override)) {
1551         error = true;
1552       }
1553     }
1554 
1555     // Make sure to move the collection into the set of IFileCollections.
1556     collections_.push_back(std::move(collection));
1557     return !error;
1558   }
1559 
1560   // Takes a path to load and merge into the main ResourceTable. If override is true,
1561   // conflicting resources are allowed to override each other, in order of last seen.
1562   // If the file path ends with .flata, .jar, .jack, or .zip the file is treated
1563   // as ZIP archive and the files within are merged individually.
1564   // Otherwise the file is processed on its own.
MergePath(const std::string & path,bool override)1565   bool MergePath(const std::string& path, bool override) {
1566     if (util::EndsWith(path, ".flata") || util::EndsWith(path, ".jar") ||
1567         util::EndsWith(path, ".jack") || util::EndsWith(path, ".zip")) {
1568       return MergeArchive(path, override);
1569     } else if (util::EndsWith(path, ".apk")) {
1570       return MergeStaticLibrary(path, override);
1571     }
1572 
1573     io::IFile* file = file_collection_->InsertFile(path);
1574     return MergeFile(file, override);
1575   }
1576 
1577   // Takes an AAPT Container file (.apc/.flat) to load and merge into the main ResourceTable.
1578   // If override is true, conflicting resources are allowed to override each other, in order of last
1579   // seen.
1580   // All other file types are ignored. This is because these files could be coming from a zip,
1581   // where we could have other files like classes.dex.
MergeFile(io::IFile * file,bool override)1582   bool MergeFile(io::IFile* file, bool override) {
1583     TRACE_CALL();
1584     const android::Source& src = file->GetSource();
1585 
1586     if (util::EndsWith(src.path, ".xml") || util::EndsWith(src.path, ".png")) {
1587       // Since AAPT compiles these file types and appends .flat to them, seeing
1588       // their raw extensions is a sign that they weren't compiled.
1589       const StringPiece file_type = util::EndsWith(src.path, ".xml") ? "XML" : "PNG";
1590       context_->GetDiagnostics()->Error(android::DiagMessage(src)
1591                                         << "uncompiled " << file_type
1592                                         << " file passed as argument. Must be "
1593                                            "compiled first into .flat file.");
1594       return false;
1595     } else if (!util::EndsWith(src.path, ".apc") && !util::EndsWith(src.path, ".flat")) {
1596       if (context_->IsVerbose()) {
1597         context_->GetDiagnostics()->Warn(android::DiagMessage(src) << "ignoring unrecognized file");
1598         return true;
1599       }
1600     }
1601 
1602     std::unique_ptr<io::InputStream> input_stream = file->OpenInputStream();
1603     if (input_stream == nullptr) {
1604       context_->GetDiagnostics()->Error(android::DiagMessage(src) << "failed to open file");
1605       return false;
1606     }
1607 
1608     if (input_stream->HadError()) {
1609       context_->GetDiagnostics()->Error(android::DiagMessage(src)
1610                                         << "failed to open file: " << input_stream->GetError());
1611       return false;
1612     }
1613 
1614     ContainerReaderEntry* entry;
1615     ContainerReader reader(input_stream.get());
1616 
1617     if (reader.HadError()) {
1618       context_->GetDiagnostics()->Error(android::DiagMessage(src)
1619                                         << "failed to read file: " << reader.GetError());
1620       return false;
1621     }
1622 
1623     while ((entry = reader.Next()) != nullptr) {
1624       if (entry->Type() == ContainerEntryType::kResTable) {
1625         TRACE_NAME(std::string("Process ResTable:") + file->GetSource().path);
1626         pb::ResourceTable pb_table;
1627         if (!entry->GetResTable(&pb_table)) {
1628           context_->GetDiagnostics()->Error(
1629               android::DiagMessage(src) << "failed to read resource table: " << entry->GetError());
1630           return false;
1631         }
1632 
1633         ResourceTable table;
1634         std::string error;
1635         if (!DeserializeTableFromPb(pb_table, nullptr /*files*/, &table, &error)) {
1636           context_->GetDiagnostics()->Error(android::DiagMessage(src)
1637                                             << "failed to deserialize resource table: " << error);
1638           return false;
1639         }
1640 
1641         if (!table_merger_->Merge(src, &table, override)) {
1642           context_->GetDiagnostics()->Error(android::DiagMessage(src)
1643                                             << "failed to merge resource table");
1644           return false;
1645         }
1646       } else if (entry->Type() == ContainerEntryType::kResFile) {
1647         TRACE_NAME(std::string("Process ResFile") + file->GetSource().path);
1648         pb::internal::CompiledFile pb_compiled_file;
1649         off64_t offset;
1650         size_t len;
1651         if (!entry->GetResFileOffsets(&pb_compiled_file, &offset, &len)) {
1652           context_->GetDiagnostics()->Error(
1653               android::DiagMessage(src) << "failed to get resource file: " << entry->GetError());
1654           return false;
1655         }
1656 
1657         ResourceFile resource_file;
1658         std::string error;
1659         if (!DeserializeCompiledFileFromPb(pb_compiled_file, &resource_file, &error)) {
1660           context_->GetDiagnostics()->Error(android::DiagMessage(src)
1661                                             << "failed to read compiled header: " << error);
1662           return false;
1663         }
1664 
1665         if (!MergeCompiledFile(resource_file, file->CreateFileSegment(offset, len), override)) {
1666           return false;
1667         }
1668       }
1669     }
1670     return true;
1671   }
1672 
CopyAssetsDirsToApk(IArchiveWriter * writer)1673   bool CopyAssetsDirsToApk(IArchiveWriter* writer) {
1674     std::map<std::string, std::unique_ptr<io::RegularFile>> merged_assets;
1675     for (const std::string& assets_dir : options_.assets_dirs) {
1676       std::optional<std::vector<std::string>> files =
1677           file::FindFiles(assets_dir, context_->GetDiagnostics(), nullptr);
1678       if (!files) {
1679         return false;
1680       }
1681 
1682       for (const std::string& file : files.value()) {
1683         std::string full_key = "assets/" + file;
1684         std::string full_path = assets_dir;
1685         file::AppendPath(&full_path, file);
1686 
1687         auto iter = merged_assets.find(full_key);
1688         if (iter == merged_assets.end()) {
1689           merged_assets.emplace(std::move(full_key), util::make_unique<io::RegularFile>(
1690                                                          android::Source(std::move(full_path))));
1691         } else if (context_->IsVerbose()) {
1692           context_->GetDiagnostics()->Warn(android::DiagMessage(iter->second->GetSource())
1693                                            << "asset file overrides '" << full_path << "'");
1694         }
1695       }
1696     }
1697 
1698     for (auto& entry : merged_assets) {
1699       uint32_t compression_flags = GetCompressionFlags(entry.first, options_);
1700       if (!io::CopyFileToArchive(context_, entry.second.get(), entry.first, compression_flags,
1701                                  writer)) {
1702         return false;
1703       }
1704     }
1705     return true;
1706   }
1707 
ResolveTableEntry(LinkContext * context,ResourceTable * table,Reference * reference)1708   ResourceEntry* ResolveTableEntry(LinkContext* context, ResourceTable* table,
1709                                    Reference* reference) {
1710     if (!reference || !reference->name) {
1711       return nullptr;
1712     }
1713     auto name_ref = ResourceNameRef(reference->name.value());
1714     if (name_ref.package.empty()) {
1715       name_ref.package = context->GetCompilationPackage();
1716     }
1717     const auto search_result = table->FindResource(name_ref);
1718     if (!search_result) {
1719       return nullptr;
1720     }
1721     return search_result.value().entry;
1722   }
1723 
AliasAdaptiveIcon(xml::XmlResource * manifest,ResourceTable * table)1724   void AliasAdaptiveIcon(xml::XmlResource* manifest, ResourceTable* table) {
1725     const xml::Element* application = manifest->root->FindChild("", "application");
1726     if (!application) {
1727       return;
1728     }
1729 
1730     const xml::Attribute* icon = application->FindAttribute(xml::kSchemaAndroid, "icon");
1731     const xml::Attribute* round_icon = application->FindAttribute(xml::kSchemaAndroid, "roundIcon");
1732     if (!icon || !round_icon) {
1733       return;
1734     }
1735 
1736     // Find the icon resource defined within the application.
1737     const auto icon_reference = ValueCast<Reference>(icon->compiled_value.get());
1738     const auto icon_entry = ResolveTableEntry(context_, table, icon_reference);
1739     if (!icon_entry) {
1740       return;
1741     }
1742 
1743     int icon_max_sdk = 0;
1744     for (auto& config_value : icon_entry->values) {
1745       icon_max_sdk = (icon_max_sdk < config_value->config.sdkVersion)
1746           ? config_value->config.sdkVersion : icon_max_sdk;
1747     }
1748     if (icon_max_sdk < SDK_O) {
1749       // Adaptive icons must be versioned with v26 qualifiers, so this is not an adaptive icon.
1750       return;
1751     }
1752 
1753     // Find the roundIcon resource defined within the application.
1754     const auto round_icon_reference = ValueCast<Reference>(round_icon->compiled_value.get());
1755     const auto round_icon_entry = ResolveTableEntry(context_, table, round_icon_reference);
1756     if (!round_icon_entry) {
1757       return;
1758     }
1759 
1760     int round_icon_max_sdk = 0;
1761     for (auto& config_value : round_icon_entry->values) {
1762       round_icon_max_sdk = (round_icon_max_sdk < config_value->config.sdkVersion)
1763                      ? config_value->config.sdkVersion : round_icon_max_sdk;
1764     }
1765     if (round_icon_max_sdk >= SDK_O) {
1766       // The developer explicitly used a v26 compatible drawable as the roundIcon, meaning we should
1767       // not generate an alias to the icon drawable.
1768       return;
1769     }
1770 
1771     // Add an equivalent v26 entry to the roundIcon for each v26 variant of the regular icon.
1772     for (auto& config_value : icon_entry->values) {
1773       if (config_value->config.sdkVersion < SDK_O) {
1774         continue;
1775       }
1776 
1777       context_->GetDiagnostics()->Note(android::DiagMessage()
1778                                        << "generating " << round_icon_reference->name.value()
1779                                        << " with config \"" << config_value->config
1780                                        << "\" for round icon compatibility");
1781 
1782       CloningValueTransformer cloner(&table->string_pool);
1783       auto value = icon_reference->Transform(cloner);
1784       auto round_config_value =
1785           round_icon_entry->FindOrCreateValue(config_value->config, config_value->product);
1786       round_config_value->value = std::move(value);
1787     }
1788   }
1789 
VerifySharedUserId(xml::XmlResource * manifest,ResourceTable * table)1790   bool VerifySharedUserId(xml::XmlResource* manifest, ResourceTable* table) {
1791     const xml::Element* manifest_el = xml::FindRootElement(manifest->root.get());
1792     if (manifest_el == nullptr) {
1793       return true;
1794     }
1795     if (!manifest_el->namespace_uri.empty() || manifest_el->name != "manifest") {
1796       return true;
1797     }
1798     const xml::Attribute* attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "sharedUserId");
1799     if (!attr) {
1800       return true;
1801     }
1802     const auto validate = [&](const std::string& shared_user_id) -> bool {
1803       if (util::IsAndroidSharedUserId(context_->GetCompilationPackage(), shared_user_id)) {
1804         return true;
1805       }
1806       android::DiagMessage error_msg(manifest_el->line_number);
1807       error_msg << "attribute 'sharedUserId' in <manifest> tag is not a valid shared user id: '"
1808                 << shared_user_id << "'";
1809       if (options_.manifest_fixer_options.warn_validation) {
1810         // Treat the error only as a warning.
1811         context_->GetDiagnostics()->Warn(error_msg);
1812         return true;
1813       }
1814       context_->GetDiagnostics()->Error(error_msg);
1815       return false;
1816     };
1817     // If attr->compiled_value is not null, check if it is a ref
1818     if (attr->compiled_value) {
1819       const auto ref = ValueCast<Reference>(attr->compiled_value.get());
1820       if (ref == nullptr) {
1821         return true;
1822       }
1823       const auto shared_user_id_entry = ResolveTableEntry(context_, table, ref);
1824       if (!shared_user_id_entry) {
1825         return true;
1826       }
1827       for (const auto& value : shared_user_id_entry->values) {
1828         const auto str_value = ValueCast<String>(value->value.get());
1829         if (str_value != nullptr && !validate(*str_value->value)) {
1830           return false;
1831         }
1832       }
1833       return true;
1834     }
1835 
1836     // Fallback to checking the raw value
1837     return validate(attr->value);
1838   }
1839 
1840   // Writes the AndroidManifest, ResourceTable, and all XML files referenced by the ResourceTable
1841   // to the IArchiveWriter.
WriteApk(IArchiveWriter * writer,proguard::KeepSet * keep_set,xml::XmlResource * manifest,ResourceTable * table)1842   bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set, xml::XmlResource* manifest,
1843                 ResourceTable* table) {
1844     TRACE_CALL();
1845     const bool keep_raw_values = (context_->GetPackageType() == PackageType::kStaticLib)
1846                                  || options_.keep_raw_values;
1847     bool result = FlattenXml(context_, *manifest, kAndroidManifestPath, keep_raw_values,
1848                              true /*utf16*/, options_.output_format, writer);
1849     if (!result) {
1850       return false;
1851     }
1852 
1853     // When a developer specifies an adaptive application icon, and a non-adaptive round application
1854     // icon, create an alias from the round icon to the regular icon for v26 APIs and up. We do this
1855     // because certain devices prefer android:roundIcon over android:icon regardless of the API
1856     // levels of the drawables set for either. This auto-aliasing behaviour allows an app to prefer
1857     // the android:roundIcon on API 25 devices, and prefer the adaptive icon on API 26 devices.
1858     // See (b/34829129)
1859     AliasAdaptiveIcon(manifest, table);
1860 
1861     // Verify the shared user id here to handle the case of reference value.
1862     if (!VerifySharedUserId(manifest, table)) {
1863       return false;
1864     }
1865 
1866     ResourceFileFlattenerOptions file_flattener_options;
1867     file_flattener_options.keep_raw_values = keep_raw_values;
1868     file_flattener_options.do_not_compress_anything = options_.do_not_compress_anything;
1869     file_flattener_options.extensions_to_not_compress = options_.extensions_to_not_compress;
1870     file_flattener_options.regex_to_not_compress = options_.regex_to_not_compress;
1871     file_flattener_options.no_auto_version = options_.no_auto_version;
1872     file_flattener_options.no_version_vectors = options_.no_version_vectors;
1873     file_flattener_options.no_version_transitions = options_.no_version_transitions;
1874     file_flattener_options.no_xml_namespaces = options_.no_xml_namespaces;
1875     file_flattener_options.update_proguard_spec =
1876         static_cast<bool>(options_.generate_proguard_rules_path);
1877     file_flattener_options.output_format = options_.output_format;
1878     file_flattener_options.do_not_fail_on_missing_resources = options_.merge_only;
1879 
1880     ResourceFileFlattener file_flattener(file_flattener_options, context_, keep_set);
1881     if (!file_flattener.Flatten(table, writer)) {
1882       context_->GetDiagnostics()->Error(android::DiagMessage() << "failed linking file resources");
1883       return false;
1884     }
1885 
1886     // Hack to fix b/68820737.
1887     // We need to modify the ResourceTable's package name, but that should NOT affect
1888     // anything else being generated, which includes the Java classes.
1889     // If required, the package name is modifed before flattening, and then modified back
1890     // to its original name.
1891     ResourceTablePackage* package_to_rewrite = nullptr;
1892     // Pre-O, the platform treats negative resource IDs [those with a package ID of 0x80
1893     // or higher] as invalid. In order to work around this limitation, we allow the use
1894     // of traditionally reserved resource IDs [those between 0x02 and 0x7E]. Allow the
1895     // definition of what a valid "split" package ID is to account for this.
1896     const bool isSplitPackage = (options_.allow_reserved_package_id &&
1897           context_->GetPackageId() != kAppPackageId &&
1898           context_->GetPackageId() != kFrameworkPackageId)
1899         || (!options_.allow_reserved_package_id && context_->GetPackageId() > kAppPackageId);
1900     if (isSplitPackage && included_feature_base_ == context_->GetCompilationPackage()) {
1901       // The base APK is included, and this is a feature split. If the base package is
1902       // the same as this package, then we are building an old style Android Instant Apps feature
1903       // split and must apply this workaround to avoid requiring namespaces support.
1904       if (!table->packages.empty() &&
1905           table->packages.back()->name == context_->GetCompilationPackage()) {
1906         package_to_rewrite = table->packages.back().get();
1907         std::string new_package_name =
1908             StringPrintf("%s.%s", package_to_rewrite->name.c_str(),
1909                          app_info_.split_name.value_or("feature").c_str());
1910 
1911         if (context_->IsVerbose()) {
1912           context_->GetDiagnostics()->Note(
1913               android::DiagMessage() << "rewriting resource package name for feature split to '"
1914                                      << new_package_name << "'");
1915         }
1916         package_to_rewrite->name = new_package_name;
1917       }
1918     }
1919 
1920     bool success = FlattenTable(table, options_.output_format, writer);
1921 
1922     if (package_to_rewrite != nullptr) {
1923       // Change the name back.
1924       package_to_rewrite->name = context_->GetCompilationPackage();
1925 
1926       // TableFlattener creates an `included_packages_` mapping entry for each package with a
1927       // non-standard package id (not 0x01 or 0x7f). Since this is a feature split and not a shared
1928       // library, do not include a mapping from the feature package name to the feature package id
1929       // in the feature's dynamic reference table.
1930       table->included_packages_.erase(context_->GetPackageId());
1931     }
1932 
1933     if (!success) {
1934       context_->GetDiagnostics()->Error(android::DiagMessage() << "failed to write resource table");
1935     }
1936     return success;
1937   }
1938 
Run(const std::vector<std::string> & input_files)1939   int Run(const std::vector<std::string>& input_files) {
1940     TRACE_CALL();
1941     // Load the AndroidManifest.xml
1942     std::unique_ptr<xml::XmlResource> manifest_xml =
1943         LoadXml(options_.manifest_path, context_->GetDiagnostics());
1944     if (!manifest_xml) {
1945       return 1;
1946     }
1947 
1948     // First extract the Package name without modifying it (via --rename-manifest-package).
1949     if (std::optional<AppInfo> maybe_app_info =
1950             ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics())) {
1951       const AppInfo& app_info = maybe_app_info.value();
1952       context_->SetCompilationPackage(app_info.package);
1953     }
1954 
1955     // Determine the package name under which to merge resources.
1956     if (options_.rename_resources_package) {
1957       if (!options_.custom_java_package) {
1958         // Generate the R.java under the original package name instead of the package name specified
1959         // through --rename-resources-package.
1960         options_.custom_java_package = context_->GetCompilationPackage();
1961       }
1962       context_->SetCompilationPackage(options_.rename_resources_package.value());
1963     }
1964 
1965     // Now that the compilation package is set, load the dependencies. This will also extract
1966     // the Android framework's versionCode and versionName, if they exist.
1967     if (!LoadSymbolsFromIncludePaths()) {
1968       return 1;
1969     }
1970 
1971     ManifestFixer manifest_fixer(options_.manifest_fixer_options);
1972     if (!manifest_fixer.Consume(context_, manifest_xml.get())) {
1973       return 1;
1974     }
1975 
1976     std::optional<AppInfo> maybe_app_info =
1977         ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics());
1978     if (!maybe_app_info) {
1979       return 1;
1980     }
1981 
1982     app_info_ = maybe_app_info.value();
1983     context_->SetMinSdkVersion(app_info_.min_sdk_version.value_or(0));
1984 
1985     context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()});
1986     context_->SetSplitNameDependencies(app_info_.split_name_dependencies);
1987 
1988     // Override the package ID when it is "android".
1989     if (context_->GetCompilationPackage() == "android") {
1990       context_->SetPackageId(kAndroidPackageId);
1991 
1992       // Verify we're building a regular app.
1993       if (context_->GetPackageType() != PackageType::kApp) {
1994         context_->GetDiagnostics()->Error(
1995             android::DiagMessage() << "package 'android' can only be built as a regular app");
1996         return 1;
1997       }
1998     }
1999 
2000     TableMergerOptions table_merger_options;
2001     table_merger_options.auto_add_overlay = options_.auto_add_overlay;
2002     table_merger_options.override_styles_instead_of_overlaying =
2003         options_.override_styles_instead_of_overlaying;
2004     table_merger_options.strict_visibility = options_.strict_visibility;
2005     table_merger_ = util::make_unique<TableMerger>(context_, &final_table_, table_merger_options);
2006 
2007     if (context_->IsVerbose()) {
2008       context_->GetDiagnostics()->Note(android::DiagMessage()
2009                                        << StringPrintf("linking package '%s' using package ID %02x",
2010                                                        context_->GetCompilationPackage().data(),
2011                                                        context_->GetPackageId()));
2012     }
2013 
2014     // Extract symbols from AndroidManifest.xml, since this isn't merged like the other XML files
2015     // in res/**/*.
2016     {
2017       XmlIdCollector collector;
2018       if (!collector.Consume(context_, manifest_xml.get())) {
2019         return false;
2020       }
2021 
2022       if (!MergeExportedSymbols(manifest_xml->file.source, manifest_xml->file.exported_symbols)) {
2023         return false;
2024       }
2025     }
2026 
2027     for (const std::string& input : input_files) {
2028       if (!MergePath(input, false)) {
2029         context_->GetDiagnostics()->Error(android::DiagMessage() << "failed parsing input");
2030         return 1;
2031       }
2032     }
2033 
2034     for (const std::string& input : options_.overlay_files) {
2035       if (!MergePath(input, true)) {
2036         context_->GetDiagnostics()->Error(android::DiagMessage() << "failed parsing overlays");
2037         return 1;
2038       }
2039     }
2040 
2041     if (!VerifyNoExternalPackages()) {
2042       return 1;
2043     }
2044 
2045     if (context_->GetPackageType() != PackageType::kStaticLib) {
2046       PrivateAttributeMover mover;
2047       if (context_->GetPackageId() == kAndroidPackageId &&
2048           !mover.Consume(context_, &final_table_)) {
2049         context_->GetDiagnostics()->Error(android::DiagMessage()
2050                                           << "failed moving private attributes");
2051         return 1;
2052       }
2053 
2054       // Assign IDs if we are building a regular app.
2055       IdAssigner id_assigner(&options_.stable_id_map);
2056       if (!id_assigner.Consume(context_, &final_table_)) {
2057         context_->GetDiagnostics()->Error(android::DiagMessage() << "failed assigning IDs");
2058         return 1;
2059       }
2060 
2061       // Now grab each ID and emit it as a file.
2062       if (options_.resource_id_map_path) {
2063         for (auto& package : final_table_.packages) {
2064           for (auto& type : package->types) {
2065             for (auto& entry : type->entries) {
2066               ResourceName name(package->name, type->named_type, entry->name);
2067               // The IDs are guaranteed to exist.
2068               options_.stable_id_map[std::move(name)] = entry->id.value();
2069             }
2070           }
2071         }
2072 
2073         if (!WriteStableIdMapToPath(context_->GetDiagnostics(), options_.stable_id_map,
2074                                     options_.resource_id_map_path.value())) {
2075           return 1;
2076         }
2077       }
2078     } else {
2079       // Static libs are merged with other apps, and ID collisions are bad, so
2080       // verify that
2081       // no IDs have been set.
2082       if (!VerifyNoIdsSet()) {
2083         return 1;
2084       }
2085     }
2086 
2087     // Add the names to mangle based on our source merge earlier.
2088     context_->SetNameManglerPolicy(
2089         NameManglerPolicy{context_->GetCompilationPackage(), table_merger_->merged_packages()});
2090 
2091     // Add our table to the symbol table.
2092     context_->GetExternalSymbols()->PrependSource(
2093         util::make_unique<ResourceTableSymbolSource>(&final_table_));
2094 
2095     // Workaround for pre-O runtime that would treat negative resource IDs
2096     // (any ID with a package ID > 7f) as invalid. Intercept any ID (PPTTEEEE) with PP > 0x7f
2097     // and type == 'id', and return the ID 0x7fPPEEEE. IDs don't need to be real resources, they
2098     // are just identifiers.
2099     if (context_->GetMinSdkVersion() < SDK_O && context_->GetPackageType() == PackageType::kApp) {
2100       if (context_->IsVerbose()) {
2101         context_->GetDiagnostics()->Note(android::DiagMessage()
2102                                          << "enabling pre-O feature split ID rewriting");
2103       }
2104       context_->GetExternalSymbols()->SetDelegate(
2105           util::make_unique<FeatureSplitSymbolTableDelegate>(context_));
2106     }
2107 
2108     // Before we process anything, remove the resources whose default values don't exist.
2109     // We want to force any references to these to fail the build.
2110     if (!options_.no_resource_removal) {
2111       if (!NoDefaultResourceRemover{}.Consume(context_, &final_table_)) {
2112         context_->GetDiagnostics()->Error(android::DiagMessage()
2113                                           << "failed removing resources with no defaults");
2114         return 1;
2115       }
2116     }
2117 
2118     ReferenceLinker linker;
2119     if (!options_.merge_only && !linker.Consume(context_, &final_table_)) {
2120       context_->GetDiagnostics()->Error(android::DiagMessage() << "failed linking references");
2121       return 1;
2122     }
2123 
2124     if (context_->GetPackageType() == PackageType::kStaticLib) {
2125       if (!options_.products.empty()) {
2126         context_->GetDiagnostics()->Warn(android::DiagMessage()
2127                                          << "can't select products when building static library");
2128       }
2129     } else {
2130       ProductFilter product_filter(options_.products);
2131       if (!product_filter.Consume(context_, &final_table_)) {
2132         context_->GetDiagnostics()->Error(android::DiagMessage() << "failed stripping products");
2133         return 1;
2134       }
2135     }
2136 
2137     if (!options_.no_auto_version) {
2138       AutoVersioner versioner;
2139       if (!versioner.Consume(context_, &final_table_)) {
2140         context_->GetDiagnostics()->Error(android::DiagMessage() << "failed versioning styles");
2141         return 1;
2142       }
2143     }
2144 
2145     if (context_->GetPackageType() != PackageType::kStaticLib && context_->GetMinSdkVersion() > 0) {
2146       if (context_->IsVerbose()) {
2147         context_->GetDiagnostics()->Note(android::DiagMessage()
2148                                          << "collapsing resource versions for minimum SDK "
2149                                          << context_->GetMinSdkVersion());
2150       }
2151 
2152       VersionCollapser collapser;
2153       if (!collapser.Consume(context_, &final_table_)) {
2154         return 1;
2155       }
2156     }
2157 
2158     if (!options_.exclude_configs_.empty()) {
2159       std::vector<ConfigDescription> excluded_configs;
2160 
2161       for (auto& config_string : options_.exclude_configs_) {
2162         TRACE_NAME("ConfigDescription::Parse");
2163         ConfigDescription config_description;
2164 
2165         if (!ConfigDescription::Parse(config_string, &config_description)) {
2166           context_->GetDiagnostics()->Error(
2167               android::DiagMessage() << "failed to parse --excluded-configs " << config_string);
2168           return 1;
2169         }
2170 
2171         excluded_configs.push_back(config_description);
2172       }
2173 
2174       ResourceExcluder excluder(excluded_configs);
2175       if (!excluder.Consume(context_, &final_table_)) {
2176         context_->GetDiagnostics()->Error(android::DiagMessage()
2177                                           << "failed excluding configurations");
2178         return 1;
2179       }
2180     }
2181 
2182     if (!options_.no_resource_deduping) {
2183       ResourceDeduper deduper;
2184       if (!deduper.Consume(context_, &final_table_)) {
2185         context_->GetDiagnostics()->Error(android::DiagMessage() << "failed deduping resources");
2186         return 1;
2187       }
2188     }
2189 
2190     proguard::KeepSet proguard_keep_set =
2191         proguard::KeepSet(options_.generate_conditional_proguard_rules);
2192     proguard::KeepSet proguard_main_dex_keep_set;
2193 
2194     if (context_->GetPackageType() == PackageType::kStaticLib) {
2195       if (options_.table_splitter_options.config_filter != nullptr ||
2196           !options_.table_splitter_options.preferred_densities.empty()) {
2197         context_->GetDiagnostics()->Warn(android::DiagMessage()
2198                                          << "can't strip resources when building static library");
2199       }
2200     } else {
2201       // Adjust the SplitConstraints so that their SDK version is stripped if it is less than or
2202       // equal to the minSdk.
2203       const size_t origConstraintSize = options_.split_constraints.size();
2204       options_.split_constraints =
2205           AdjustSplitConstraintsForMinSdk(context_->GetMinSdkVersion(), options_.split_constraints);
2206 
2207       if (origConstraintSize != options_.split_constraints.size()) {
2208         context_->GetDiagnostics()->Warn(android::DiagMessage()
2209                                          << "requested to split resources prior to min sdk of "
2210                                          << context_->GetMinSdkVersion());
2211       }
2212       TableSplitter table_splitter(options_.split_constraints, options_.table_splitter_options);
2213       if (!table_splitter.VerifySplitConstraints(context_)) {
2214         return 1;
2215       }
2216       table_splitter.SplitTable(&final_table_);
2217 
2218       // Now we need to write out the Split APKs.
2219       auto path_iter = options_.split_paths.begin();
2220       auto split_constraints_iter = options_.split_constraints.begin();
2221       for (std::unique_ptr<ResourceTable>& split_table : table_splitter.splits()) {
2222         if (context_->IsVerbose()) {
2223           context_->GetDiagnostics()->Note(android::DiagMessage(*path_iter)
2224                                            << "generating split with configurations '"
2225                                            << util::Joiner(split_constraints_iter->configs, ", ")
2226                                            << "'");
2227         }
2228 
2229         std::unique_ptr<IArchiveWriter> archive_writer = MakeArchiveWriter(*path_iter);
2230         if (!archive_writer) {
2231           context_->GetDiagnostics()->Error(android::DiagMessage() << "failed to create archive");
2232           return 1;
2233         }
2234 
2235         // Generate an AndroidManifest.xml for each split.
2236         std::unique_ptr<xml::XmlResource> split_manifest =
2237             GenerateSplitManifest(app_info_, *split_constraints_iter);
2238 
2239         XmlReferenceLinker linker(&final_table_);
2240         if (!linker.Consume(context_, split_manifest.get())) {
2241           context_->GetDiagnostics()->Error(android::DiagMessage()
2242                                             << "failed to create Split AndroidManifest.xml");
2243           return 1;
2244         }
2245 
2246         if (!WriteApk(archive_writer.get(), &proguard_keep_set, split_manifest.get(),
2247                       split_table.get())) {
2248           return 1;
2249         }
2250 
2251         ++path_iter;
2252         ++split_constraints_iter;
2253       }
2254     }
2255 
2256     // Start writing the base APK.
2257     std::unique_ptr<IArchiveWriter> archive_writer = MakeArchiveWriter(options_.output_path);
2258     if (!archive_writer) {
2259       context_->GetDiagnostics()->Error(android::DiagMessage() << "failed to create archive");
2260       return 1;
2261     }
2262 
2263     bool error = false;
2264     {
2265       // AndroidManifest.xml has no resource name, but the CallSite is built from the name
2266       // (aka, which package the AndroidManifest.xml is coming from).
2267       // So we give it a package name so it can see local resources.
2268       manifest_xml->file.name.package = context_->GetCompilationPackage();
2269 
2270       XmlReferenceLinker manifest_linker(&final_table_);
2271       if (options_.merge_only || manifest_linker.Consume(context_, manifest_xml.get())) {
2272         if (options_.generate_proguard_rules_path &&
2273             !proguard::CollectProguardRulesForManifest(manifest_xml.get(), &proguard_keep_set)) {
2274           error = true;
2275         }
2276 
2277         if (options_.generate_main_dex_proguard_rules_path &&
2278             !proguard::CollectProguardRulesForManifest(manifest_xml.get(),
2279                                                        &proguard_main_dex_keep_set, true)) {
2280           error = true;
2281         }
2282 
2283         if (options_.generate_java_class_path) {
2284           if (!WriteManifestJavaFile(manifest_xml.get())) {
2285             error = true;
2286           }
2287         }
2288 
2289         if (options_.no_xml_namespaces) {
2290           // PackageParser will fail if URIs are removed from
2291           // AndroidManifest.xml.
2292           XmlNamespaceRemover namespace_remover(true /* keepUris */);
2293           if (!namespace_remover.Consume(context_, manifest_xml.get())) {
2294             error = true;
2295           }
2296         }
2297       } else {
2298         error = true;
2299       }
2300     }
2301 
2302     if (error) {
2303       context_->GetDiagnostics()->Error(android::DiagMessage() << "failed processing manifest");
2304       return 1;
2305     }
2306 
2307     if (!VerifyLocaleFormat(manifest_xml.get(), context_->GetDiagnostics())) {
2308       return 1;
2309     };
2310 
2311     if (!WriteApk(archive_writer.get(), &proguard_keep_set, manifest_xml.get(), &final_table_)) {
2312       return 1;
2313     }
2314 
2315     if (!CopyAssetsDirsToApk(archive_writer.get())) {
2316       return 1;
2317     }
2318 
2319     if (options_.generate_java_class_path || options_.generate_text_symbols_path) {
2320       if (!GenerateJavaClasses()) {
2321         return 1;
2322       }
2323     }
2324 
2325     if (!WriteProguardFile(options_.generate_proguard_rules_path, proguard_keep_set)) {
2326       return 1;
2327     }
2328 
2329     if (!WriteProguardFile(options_.generate_main_dex_proguard_rules_path,
2330                            proguard_main_dex_keep_set)) {
2331       return 1;
2332     }
2333     return 0;
2334   }
2335 
2336  private:
2337   LinkOptions options_;
2338   LinkContext* context_;
2339   ResourceTable final_table_;
2340 
2341   AppInfo app_info_;
2342 
2343   std::unique_ptr<TableMerger> table_merger_;
2344 
2345   // A pointer to the FileCollection representing the filesystem (not archives).
2346   std::unique_ptr<io::FileCollection> file_collection_;
2347 
2348   // A vector of IFileCollections. This is mainly here to retain ownership of the
2349   // collections.
2350   std::vector<std::unique_ptr<io::IFileCollection>> collections_;
2351 
2352   // The set of merged APKs. This is mainly here to retain ownership of the APKs.
2353   std::vector<std::unique_ptr<LoadedApk>> merged_apks_;
2354 
2355   // The set of included APKs (not merged). This is mainly here to retain ownership of the APKs.
2356   std::vector<std::unique_ptr<LoadedApk>> static_library_includes_;
2357 
2358   // The set of shared libraries being used, mapping their assigned package ID to package name.
2359   std::map<size_t, std::string> shared_libs_;
2360 
2361   // The package name of the base application, if it is included.
2362   std::optional<std::string> included_feature_base_;
2363 };
2364 
Action(const std::vector<std::string> & args)2365 int LinkCommand::Action(const std::vector<std::string>& args) {
2366   TRACE_FLUSH(trace_folder_ ? trace_folder_.value() : "", "LinkCommand::Action");
2367   LinkContext context(diag_);
2368 
2369   // Expand all argument-files passed into the command line. These start with '@'.
2370   std::vector<std::string> arg_list;
2371   for (const std::string& arg : args) {
2372     if (util::StartsWith(arg, "@")) {
2373       const std::string path = arg.substr(1, arg.size() - 1);
2374       std::string error;
2375       if (!file::AppendArgsFromFile(path, &arg_list, &error)) {
2376         context.GetDiagnostics()->Error(android::DiagMessage(path) << error);
2377         return 1;
2378       }
2379     } else {
2380       arg_list.push_back(arg);
2381     }
2382   }
2383 
2384   // Expand all argument-files passed to -R.
2385   for (const std::string& arg : overlay_arg_list_) {
2386     if (util::StartsWith(arg, "@")) {
2387       const std::string path = arg.substr(1, arg.size() - 1);
2388       std::string error;
2389       if (!file::AppendArgsFromFile(path, &options_.overlay_files, &error)) {
2390         context.GetDiagnostics()->Error(android::DiagMessage(path) << error);
2391         return 1;
2392       }
2393     } else {
2394       options_.overlay_files.push_back(arg);
2395     }
2396   }
2397 
2398   if (verbose_) {
2399     context.SetVerbose(verbose_);
2400   }
2401 
2402   if (int{shared_lib_} + int{static_lib_} + int{proto_format_} > 1) {
2403     context.GetDiagnostics()
2404         ->Error(android::DiagMessage()
2405                 << "only one of --shared-lib, --static-lib, or --proto_format can be defined");
2406     return 1;
2407   }
2408 
2409   if (shared_lib_ && options_.private_symbols) {
2410     // If a shared library styleable in a public R.java uses a private attribute, attempting to
2411     // reference the private attribute within the styleable array will cause a link error because
2412     // the private attribute will not be emitted in the public R.java.
2413     context.GetDiagnostics()->Error(android::DiagMessage()
2414                                     << "--shared-lib cannot currently be used in combination with"
2415                                     << " --private-symbols");
2416     return 1;
2417   }
2418 
2419   if (options_.merge_only && !static_lib_) {
2420     context.GetDiagnostics()
2421         ->Error(android::DiagMessage()
2422                 << "the --merge-only flag can be only used when building a static library");
2423     return 1;
2424   }
2425   if (options_.use_sparse_encoding) {
2426     options_.table_flattener_options.sparse_entries = SparseEntriesMode::Enabled;
2427   }
2428 
2429   // The default build type.
2430   context.SetPackageType(PackageType::kApp);
2431   context.SetPackageId(kAppPackageId);
2432 
2433   if (shared_lib_) {
2434     context.SetPackageType(PackageType::kSharedLib);
2435     context.SetPackageId(0x00);
2436   } else if (static_lib_) {
2437     context.SetPackageType(PackageType::kStaticLib);
2438     options_.output_format = OutputFormat::kProto;
2439   } else if (proto_format_) {
2440     options_.output_format = OutputFormat::kProto;
2441   }
2442 
2443   if (package_id_) {
2444     if (context.GetPackageType() != PackageType::kApp) {
2445       context.GetDiagnostics()->Error(
2446           android::DiagMessage() << "can't specify --package-id when not building a regular app");
2447       return 1;
2448     }
2449 
2450     const std::optional<uint32_t> maybe_package_id_int =
2451         ResourceUtils::ParseInt(package_id_.value());
2452     if (!maybe_package_id_int) {
2453       context.GetDiagnostics()->Error(android::DiagMessage()
2454                                       << "package ID '" << package_id_.value()
2455                                       << "' is not a valid integer");
2456       return 1;
2457     }
2458 
2459     const uint32_t package_id_int = maybe_package_id_int.value();
2460     if (package_id_int > std::numeric_limits<uint8_t>::max()
2461         || package_id_int == kFrameworkPackageId
2462         || (!options_.allow_reserved_package_id && package_id_int < kAppPackageId)) {
2463       context.GetDiagnostics()->Error(
2464           android::DiagMessage() << StringPrintf(
2465               "invalid package ID 0x%02x. Must be in the range 0x7f-0xff.", package_id_int));
2466       return 1;
2467     }
2468     context.SetPackageId(static_cast<uint8_t>(package_id_int));
2469   }
2470 
2471   // Populate the set of extra packages for which to generate R.java.
2472   for (std::string& extra_package : extra_java_packages_) {
2473     // A given package can actually be a colon separated list of packages.
2474     for (StringPiece package : util::Split(extra_package, ':')) {
2475       options_.extra_java_packages.emplace(package);
2476     }
2477   }
2478 
2479   if (product_list_) {
2480     for (StringPiece product : util::Tokenize(product_list_.value(), ',')) {
2481       if (product != "" && product != "default") {
2482         options_.products.emplace(product);
2483       }
2484     }
2485   }
2486 
2487   std::unique_ptr<IConfigFilter> filter;
2488   if (!configs_.empty()) {
2489     filter = ParseConfigFilterParameters(configs_, context.GetDiagnostics());
2490     if (filter == nullptr) {
2491       return 1;
2492     }
2493     options_.table_splitter_options.config_filter = filter.get();
2494   }
2495 
2496   if (preferred_density_) {
2497     std::optional<uint16_t> density =
2498         ParseTargetDensityParameter(preferred_density_.value(), context.GetDiagnostics());
2499     if (!density) {
2500       return 1;
2501     }
2502     options_.table_splitter_options.preferred_densities.push_back(density.value());
2503   }
2504 
2505   // Parse the split parameters.
2506   for (const std::string& split_arg : split_args_) {
2507     options_.split_paths.push_back({});
2508     options_.split_constraints.push_back({});
2509     if (!ParseSplitParameter(split_arg, context.GetDiagnostics(), &options_.split_paths.back(),
2510         &options_.split_constraints.back())) {
2511       return 1;
2512     }
2513   }
2514 
2515   if (context.GetPackageType() != PackageType::kStaticLib && stable_id_file_path_) {
2516     if (!LoadStableIdMap(context.GetDiagnostics(), stable_id_file_path_.value(),
2517         &options_.stable_id_map)) {
2518       return 1;
2519     }
2520   }
2521 
2522   if (no_compress_regex) {
2523     std::string regex = no_compress_regex.value();
2524     if (util::StartsWith(regex, "@")) {
2525       const std::string path = regex.substr(1, regex.size() -1);
2526       std::string error;
2527       if (!file::AppendSetArgsFromFile(path, &options_.extensions_to_not_compress, &error)) {
2528         context.GetDiagnostics()->Error(android::DiagMessage(path) << error);
2529         return 1;
2530       }
2531     } else {
2532       options_.regex_to_not_compress = GetRegularExpression(no_compress_regex.value());
2533     }
2534   }
2535 
2536   // Populate some default no-compress extensions that are already compressed.
2537   options_.extensions_to_not_compress.insert({
2538       // Image extensions
2539       ".jpg", ".jpeg", ".png", ".gif", ".webp",
2540       // Audio extensions
2541       ".wav", ".mp2", ".mp3", ".ogg", ".aac", ".mid", ".midi", ".smf", ".jet", ".rtttl", ".imy",
2542       ".xmf", ".amr", ".awb",
2543       // Audio/video extensions
2544       ".mpg", ".mpeg", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", ".wma", ".wmv",
2545       ".webm", ".mkv"});
2546 
2547   // Turn off auto versioning for static-libs.
2548   if (context.GetPackageType() == PackageType::kStaticLib) {
2549     options_.no_auto_version = true;
2550     options_.no_version_vectors = true;
2551     options_.no_version_transitions = true;
2552   }
2553 
2554   Linker cmd(&context, options_);
2555   return cmd.Run(arg_list);
2556 }
2557 
2558 }  // namespace aapt
2559