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