• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "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, &fout);
1256     fout.Flush();
1257 
1258     if (fout.HadError()) {
1259       context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
1260                                                       << "': " << fout.GetError());
1261       return false;
1262     }
1263     return true;
1264   }
1265 
WriteProguardFile(const Maybe<std::string> & out,const proguard::KeepSet & keep_set)1266   bool WriteProguardFile(const Maybe<std::string>& out, const proguard::KeepSet& keep_set) {
1267     TRACE_CALL();
1268     if (!out) {
1269       return true;
1270     }
1271 
1272     const std::string& out_path = out.value();
1273     io::FileOutputStream fout(out_path);
1274     if (fout.HadError()) {
1275       context_->GetDiagnostics()->Error(DiagMessage() << "failed to open '" << out_path
1276                                                       << "': " << fout.GetError());
1277       return false;
1278     }
1279 
1280     proguard::WriteKeepSet(keep_set, &fout, options_.generate_minimal_proguard_rules);
1281     fout.Flush();
1282 
1283     if (fout.HadError()) {
1284       context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
1285                                                       << "': " << fout.GetError());
1286       return false;
1287     }
1288     return true;
1289   }
1290 
MergeStaticLibrary(const std::string & input,bool override)1291   bool MergeStaticLibrary(const std::string& input, bool override) {
1292     TRACE_CALL();
1293     if (context_->IsVerbose()) {
1294       context_->GetDiagnostics()->Note(DiagMessage() << "merging static library " << input);
1295     }
1296 
1297     std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(input, context_->GetDiagnostics());
1298     if (apk == nullptr) {
1299       context_->GetDiagnostics()->Error(DiagMessage(input) << "invalid static library");
1300       return false;
1301     }
1302 
1303     ResourceTable* table = apk->GetResourceTable();
1304     ResourceTablePackage* pkg = table->FindPackageById(kAppPackageId);
1305     if (!pkg) {
1306       context_->GetDiagnostics()->Error(DiagMessage(input) << "static library has no package");
1307       return false;
1308     }
1309 
1310     bool result;
1311     if (options_.no_static_lib_packages) {
1312       // Merge all resources as if they were in the compilation package. This is the old behavior
1313       // of aapt.
1314 
1315       // Add the package to the set of --extra-packages so we emit an R.java for each library
1316       // package.
1317       if (!pkg->name.empty()) {
1318         options_.extra_java_packages.insert(pkg->name);
1319       }
1320 
1321       // Clear the package name, so as to make the resources look like they are coming from the
1322       // local package.
1323       pkg->name = "";
1324       result = table_merger_->Merge(Source(input), table, override);
1325 
1326     } else {
1327       // This is the proper way to merge libraries, where the package name is
1328       // preserved and resource names are mangled.
1329       result = table_merger_->MergeAndMangle(Source(input), pkg->name, table);
1330     }
1331 
1332     if (!result) {
1333       return false;
1334     }
1335 
1336     // Make sure to move the collection into the set of IFileCollections.
1337     merged_apks_.push_back(std::move(apk));
1338     return true;
1339   }
1340 
MergeExportedSymbols(const Source & source,const std::vector<SourcedResourceName> & exported_symbols)1341   bool MergeExportedSymbols(const Source& source,
1342                             const std::vector<SourcedResourceName>& exported_symbols) {
1343     TRACE_CALL();
1344     // Add the exports of this file to the table.
1345     for (const SourcedResourceName& exported_symbol : exported_symbols) {
1346       ResourceName res_name = exported_symbol.name;
1347       if (res_name.package.empty()) {
1348         res_name.package = context_->GetCompilationPackage();
1349       }
1350 
1351       Maybe<ResourceName> mangled_name = context_->GetNameMangler()->MangleName(res_name);
1352       if (mangled_name) {
1353         res_name = mangled_name.value();
1354       }
1355 
1356       std::unique_ptr<Id> id = util::make_unique<Id>();
1357       id->SetSource(source.WithLine(exported_symbol.line));
1358       bool result =
1359           final_table_.AddResourceMangled(res_name, ConfigDescription::DefaultConfig(),
1360                                           std::string(), std::move(id), context_->GetDiagnostics());
1361       if (!result) {
1362         return false;
1363       }
1364     }
1365     return true;
1366   }
1367 
MergeCompiledFile(const ResourceFile & compiled_file,io::IFile * file,bool override)1368   bool MergeCompiledFile(const ResourceFile& compiled_file, io::IFile* file, bool override) {
1369     TRACE_CALL();
1370     if (context_->IsVerbose()) {
1371       context_->GetDiagnostics()->Note(DiagMessage()
1372                                        << "merging '" << compiled_file.name
1373                                        << "' from compiled file " << compiled_file.source);
1374     }
1375 
1376     if (!table_merger_->MergeFile(compiled_file, override, file)) {
1377       return false;
1378     }
1379     return MergeExportedSymbols(compiled_file.source, compiled_file.exported_symbols);
1380   }
1381 
1382   // Takes a path to load as a ZIP file and merges the files within into the master ResourceTable.
1383   // If override is true, conflicting resources are allowed to override each other, in order of last
1384   // seen.
1385   // An io::IFileCollection is created from the ZIP file and added to the set of
1386   // io::IFileCollections that are open.
MergeArchive(const std::string & input,bool override)1387   bool MergeArchive(const std::string& input, bool override) {
1388     TRACE_CALL();
1389     if (context_->IsVerbose()) {
1390       context_->GetDiagnostics()->Note(DiagMessage() << "merging archive " << input);
1391     }
1392 
1393     std::string error_str;
1394     std::unique_ptr<io::ZipFileCollection> collection =
1395         io::ZipFileCollection::Create(input, &error_str);
1396     if (!collection) {
1397       context_->GetDiagnostics()->Error(DiagMessage(input) << error_str);
1398       return false;
1399     }
1400 
1401     bool error = false;
1402     for (auto iter = collection->Iterator(); iter->HasNext();) {
1403       if (!MergeFile(iter->Next(), override)) {
1404         error = true;
1405       }
1406     }
1407 
1408     // Make sure to move the collection into the set of IFileCollections.
1409     collections_.push_back(std::move(collection));
1410     return !error;
1411   }
1412 
1413   // Takes a path to load and merge into the master ResourceTable. If override is true,
1414   // conflicting resources are allowed to override each other, in order of last seen.
1415   // If the file path ends with .flata, .jar, .jack, or .zip the file is treated
1416   // as ZIP archive and the files within are merged individually.
1417   // Otherwise the file is processed on its own.
MergePath(const std::string & path,bool override)1418   bool MergePath(const std::string& path, bool override) {
1419     if (util::EndsWith(path, ".flata") || util::EndsWith(path, ".jar") ||
1420         util::EndsWith(path, ".jack") || util::EndsWith(path, ".zip")) {
1421       return MergeArchive(path, override);
1422     } else if (util::EndsWith(path, ".apk")) {
1423       return MergeStaticLibrary(path, override);
1424     }
1425 
1426     io::IFile* file = file_collection_->InsertFile(path);
1427     return MergeFile(file, override);
1428   }
1429 
1430   // Takes an AAPT Container file (.apc/.flat) to load and merge into the master ResourceTable.
1431   // If override is true, conflicting resources are allowed to override each other, in order of last
1432   // seen.
1433   // All other file types are ignored. This is because these files could be coming from a zip,
1434   // where we could have other files like classes.dex.
MergeFile(io::IFile * file,bool override)1435   bool MergeFile(io::IFile* file, bool override) {
1436     TRACE_CALL();
1437     const Source& src = file->GetSource();
1438 
1439     if (util::EndsWith(src.path, ".xml") || util::EndsWith(src.path, ".png")) {
1440       // Since AAPT compiles these file types and appends .flat to them, seeing
1441       // their raw extensions is a sign that they weren't compiled.
1442       const StringPiece file_type = util::EndsWith(src.path, ".xml") ? "XML" : "PNG";
1443       context_->GetDiagnostics()->Error(DiagMessage(src) << "uncompiled " << file_type
1444                                                          << " file passed as argument. Must be "
1445                                                             "compiled first into .flat file.");
1446       return false;
1447     } else if (!util::EndsWith(src.path, ".apc") && !util::EndsWith(src.path, ".flat")) {
1448       if (context_->IsVerbose()) {
1449         context_->GetDiagnostics()->Warn(DiagMessage(src) << "ignoring unrecognized file");
1450         return true;
1451       }
1452     }
1453 
1454     std::unique_ptr<io::InputStream> input_stream = file->OpenInputStream();
1455     if (input_stream == nullptr) {
1456       context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to open file");
1457       return false;
1458     }
1459 
1460     if (input_stream->HadError()) {
1461       context_->GetDiagnostics()->Error(DiagMessage(src)
1462                                         << "failed to open file: " << input_stream->GetError());
1463       return false;
1464     }
1465 
1466     ContainerReaderEntry* entry;
1467     ContainerReader reader(input_stream.get());
1468 
1469     if (reader.HadError()) {
1470       context_->GetDiagnostics()->Error(DiagMessage(src)
1471                                         << "failed to read file: " << reader.GetError());
1472       return false;
1473     }
1474 
1475     while ((entry = reader.Next()) != nullptr) {
1476       if (entry->Type() == ContainerEntryType::kResTable) {
1477         TRACE_NAME(std::string("Process ResTable:") + file->GetSource().path);
1478         pb::ResourceTable pb_table;
1479         if (!entry->GetResTable(&pb_table)) {
1480           context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to read resource table: "
1481                                                              << entry->GetError());
1482           return false;
1483         }
1484 
1485         ResourceTable table;
1486         std::string error;
1487         if (!DeserializeTableFromPb(pb_table, nullptr /*files*/, &table, &error)) {
1488           context_->GetDiagnostics()->Error(DiagMessage(src)
1489                                             << "failed to deserialize resource table: " << error);
1490           return false;
1491         }
1492 
1493         if (!table_merger_->Merge(src, &table, override)) {
1494           context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to merge resource table");
1495           return false;
1496         }
1497       } else if (entry->Type() == ContainerEntryType::kResFile) {
1498         TRACE_NAME(std::string("Process ResFile") + file->GetSource().path);
1499         pb::internal::CompiledFile pb_compiled_file;
1500         off64_t offset;
1501         size_t len;
1502         if (!entry->GetResFileOffsets(&pb_compiled_file, &offset, &len)) {
1503           context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to get resource file: "
1504                                                              << entry->GetError());
1505           return false;
1506         }
1507 
1508         ResourceFile resource_file;
1509         std::string error;
1510         if (!DeserializeCompiledFileFromPb(pb_compiled_file, &resource_file, &error)) {
1511           context_->GetDiagnostics()->Error(DiagMessage(src)
1512                                             << "failed to read compiled header: " << error);
1513           return false;
1514         }
1515 
1516         if (!MergeCompiledFile(resource_file, file->CreateFileSegment(offset, len), override)) {
1517           return false;
1518         }
1519       }
1520     }
1521     return true;
1522   }
1523 
CopyAssetsDirsToApk(IArchiveWriter * writer)1524   bool CopyAssetsDirsToApk(IArchiveWriter* writer) {
1525     std::map<std::string, std::unique_ptr<io::RegularFile>> merged_assets;
1526     for (const std::string& assets_dir : options_.assets_dirs) {
1527       Maybe<std::vector<std::string>> files =
1528           file::FindFiles(assets_dir, context_->GetDiagnostics(), nullptr);
1529       if (!files) {
1530         return false;
1531       }
1532 
1533       for (const std::string& file : files.value()) {
1534         std::string full_key = "assets/" + file;
1535         std::string full_path = assets_dir;
1536         file::AppendPath(&full_path, file);
1537 
1538         auto iter = merged_assets.find(full_key);
1539         if (iter == merged_assets.end()) {
1540           merged_assets.emplace(std::move(full_key),
1541                                 util::make_unique<io::RegularFile>(Source(std::move(full_path))));
1542         } else if (context_->IsVerbose()) {
1543           context_->GetDiagnostics()->Warn(DiagMessage(iter->second->GetSource())
1544                                            << "asset file overrides '" << full_path << "'");
1545         }
1546       }
1547     }
1548 
1549     for (auto& entry : merged_assets) {
1550       uint32_t compression_flags = ArchiveEntry::kCompress;
1551       std::string extension = file::GetExtension(entry.first).to_string();
1552 
1553       if (options_.do_not_compress_anything
1554           || options_.extensions_to_not_compress.count(extension) > 0
1555           || (options_.regex_to_not_compress
1556               && std::regex_search(extension, options_.regex_to_not_compress.value()))) {
1557         compression_flags = 0u;
1558       }
1559 
1560       if (!io::CopyFileToArchive(context_, entry.second.get(), entry.first, compression_flags,
1561                                  writer)) {
1562         return false;
1563       }
1564     }
1565     return true;
1566   }
1567 
1568   // Writes the AndroidManifest, ResourceTable, and all XML files referenced by the ResourceTable
1569   // to the IArchiveWriter.
WriteApk(IArchiveWriter * writer,proguard::KeepSet * keep_set,xml::XmlResource * manifest,ResourceTable * table)1570   bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set, xml::XmlResource* manifest,
1571                 ResourceTable* table) {
1572     TRACE_CALL();
1573     const bool keep_raw_values = (context_->GetPackageType() == PackageType::kStaticLib)
1574                                  || options_.keep_raw_values;
1575     bool result = FlattenXml(context_, *manifest, kAndroidManifestPath, keep_raw_values,
1576                              true /*utf16*/, options_.output_format, writer);
1577     if (!result) {
1578       return false;
1579     }
1580 
1581     ResourceFileFlattenerOptions file_flattener_options;
1582     file_flattener_options.keep_raw_values = keep_raw_values;
1583     file_flattener_options.do_not_compress_anything = options_.do_not_compress_anything;
1584     file_flattener_options.extensions_to_not_compress = options_.extensions_to_not_compress;
1585     file_flattener_options.regex_to_not_compress = options_.regex_to_not_compress;
1586     file_flattener_options.no_auto_version = options_.no_auto_version;
1587     file_flattener_options.no_version_vectors = options_.no_version_vectors;
1588     file_flattener_options.no_version_transitions = options_.no_version_transitions;
1589     file_flattener_options.no_xml_namespaces = options_.no_xml_namespaces;
1590     file_flattener_options.update_proguard_spec =
1591         static_cast<bool>(options_.generate_proguard_rules_path);
1592     file_flattener_options.output_format = options_.output_format;
1593 
1594     ResourceFileFlattener file_flattener(file_flattener_options, context_, keep_set);
1595 
1596     if (!file_flattener.Flatten(table, writer)) {
1597       context_->GetDiagnostics()->Error(DiagMessage() << "failed linking file resources");
1598       return false;
1599     }
1600 
1601     // Hack to fix b/68820737.
1602     // We need to modify the ResourceTable's package name, but that should NOT affect
1603     // anything else being generated, which includes the Java classes.
1604     // If required, the package name is modifed before flattening, and then modified back
1605     // to its original name.
1606     ResourceTablePackage* package_to_rewrite = nullptr;
1607     // Pre-O, the platform treats negative resource IDs [those with a package ID of 0x80
1608     // or higher] as invalid. In order to work around this limitation, we allow the use
1609     // of traditionally reserved resource IDs [those between 0x02 and 0x7E]. Allow the
1610     // definition of what a valid "split" package ID is to account for this.
1611     const bool isSplitPackage = (options_.allow_reserved_package_id &&
1612           context_->GetPackageId() != kAppPackageId &&
1613           context_->GetPackageId() != kFrameworkPackageId)
1614         || (!options_.allow_reserved_package_id && context_->GetPackageId() > kAppPackageId);
1615     if (isSplitPackage &&
1616         included_feature_base_ == make_value(context_->GetCompilationPackage())) {
1617       // The base APK is included, and this is a feature split. If the base package is
1618       // the same as this package, then we are building an old style Android Instant Apps feature
1619       // split and must apply this workaround to avoid requiring namespaces support.
1620       package_to_rewrite = table->FindPackage(context_->GetCompilationPackage());
1621       if (package_to_rewrite != nullptr) {
1622         CHECK_EQ(1u, table->packages.size()) << "can't change name of package when > 1 package";
1623 
1624         std::string new_package_name =
1625             StringPrintf("%s.%s", package_to_rewrite->name.c_str(),
1626                          app_info_.split_name.value_or_default("feature").c_str());
1627 
1628         if (context_->IsVerbose()) {
1629           context_->GetDiagnostics()->Note(
1630               DiagMessage() << "rewriting resource package name for feature split to '"
1631                             << new_package_name << "'");
1632         }
1633         package_to_rewrite->name = new_package_name;
1634       }
1635     }
1636 
1637     bool success = FlattenTable(table, options_.output_format, writer);
1638 
1639     if (package_to_rewrite != nullptr) {
1640       // Change the name back.
1641       package_to_rewrite->name = context_->GetCompilationPackage();
1642       if (package_to_rewrite->id) {
1643         table->included_packages_.erase(package_to_rewrite->id.value());
1644       }
1645     }
1646 
1647     if (!success) {
1648       context_->GetDiagnostics()->Error(DiagMessage() << "failed to write resource table");
1649     }
1650     return success;
1651   }
1652 
Run(const std::vector<std::string> & input_files)1653   int Run(const std::vector<std::string>& input_files) {
1654     TRACE_CALL();
1655     // Load the AndroidManifest.xml
1656     std::unique_ptr<xml::XmlResource> manifest_xml =
1657         LoadXml(options_.manifest_path, context_->GetDiagnostics());
1658     if (!manifest_xml) {
1659       return 1;
1660     }
1661 
1662     // First extract the Package name without modifying it (via --rename-manifest-package).
1663     if (Maybe<AppInfo> maybe_app_info =
1664             ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics())) {
1665       const AppInfo& app_info = maybe_app_info.value();
1666       context_->SetCompilationPackage(app_info.package);
1667     }
1668 
1669     // Now that the compilation package is set, load the dependencies. This will also extract
1670     // the Android framework's versionCode and versionName, if they exist.
1671     if (!LoadSymbolsFromIncludePaths()) {
1672       return 1;
1673     }
1674 
1675     ManifestFixer manifest_fixer(options_.manifest_fixer_options);
1676     if (!manifest_fixer.Consume(context_, manifest_xml.get())) {
1677       return 1;
1678     }
1679 
1680     Maybe<AppInfo> maybe_app_info =
1681         ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics());
1682     if (!maybe_app_info) {
1683       return 1;
1684     }
1685 
1686     app_info_ = maybe_app_info.value();
1687     context_->SetMinSdkVersion(app_info_.min_sdk_version.value_or_default(0));
1688 
1689     context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()});
1690 
1691     // Override the package ID when it is "android".
1692     if (context_->GetCompilationPackage() == "android") {
1693       context_->SetPackageId(0x01);
1694 
1695       // Verify we're building a regular app.
1696       if (context_->GetPackageType() != PackageType::kApp) {
1697         context_->GetDiagnostics()->Error(
1698             DiagMessage() << "package 'android' can only be built as a regular app");
1699         return 1;
1700       }
1701     }
1702 
1703     TableMergerOptions table_merger_options;
1704     table_merger_options.auto_add_overlay = options_.auto_add_overlay;
1705     table_merger_options.strict_visibility = options_.strict_visibility;
1706     table_merger_ = util::make_unique<TableMerger>(context_, &final_table_, table_merger_options);
1707 
1708     if (context_->IsVerbose()) {
1709       context_->GetDiagnostics()->Note(DiagMessage()
1710                                        << StringPrintf("linking package '%s' using package ID %02x",
1711                                                        context_->GetCompilationPackage().data(),
1712                                                        context_->GetPackageId()));
1713     }
1714 
1715     // Extract symbols from AndroidManifest.xml, since this isn't merged like the other XML files
1716     // in res/**/*.
1717     {
1718       XmlIdCollector collector;
1719       if (!collector.Consume(context_, manifest_xml.get())) {
1720         return false;
1721       }
1722 
1723       if (!MergeExportedSymbols(manifest_xml->file.source, manifest_xml->file.exported_symbols)) {
1724         return false;
1725       }
1726     }
1727 
1728     for (const std::string& input : input_files) {
1729       if (!MergePath(input, false)) {
1730         context_->GetDiagnostics()->Error(DiagMessage() << "failed parsing input");
1731         return 1;
1732       }
1733     }
1734 
1735     for (const std::string& input : options_.overlay_files) {
1736       if (!MergePath(input, true)) {
1737         context_->GetDiagnostics()->Error(DiagMessage() << "failed parsing overlays");
1738         return 1;
1739       }
1740     }
1741 
1742     if (!VerifyNoExternalPackages()) {
1743       return 1;
1744     }
1745 
1746     if (context_->GetPackageType() != PackageType::kStaticLib) {
1747       PrivateAttributeMover mover;
1748       if (!mover.Consume(context_, &final_table_)) {
1749         context_->GetDiagnostics()->Error(DiagMessage() << "failed moving private attributes");
1750         return 1;
1751       }
1752 
1753       // Assign IDs if we are building a regular app.
1754       IdAssigner id_assigner(&options_.stable_id_map);
1755       if (!id_assigner.Consume(context_, &final_table_)) {
1756         context_->GetDiagnostics()->Error(DiagMessage() << "failed assigning IDs");
1757         return 1;
1758       }
1759 
1760       // Now grab each ID and emit it as a file.
1761       if (options_.resource_id_map_path) {
1762         for (auto& package : final_table_.packages) {
1763           for (auto& type : package->types) {
1764             for (auto& entry : type->entries) {
1765               ResourceName name(package->name, type->type, entry->name);
1766               // The IDs are guaranteed to exist.
1767               options_.stable_id_map[std::move(name)] =
1768                   ResourceId(package->id.value(), type->id.value(), entry->id.value());
1769             }
1770           }
1771         }
1772 
1773         if (!WriteStableIdMapToPath(context_->GetDiagnostics(), options_.stable_id_map,
1774                                     options_.resource_id_map_path.value())) {
1775           return 1;
1776         }
1777       }
1778     } else {
1779       // Static libs are merged with other apps, and ID collisions are bad, so
1780       // verify that
1781       // no IDs have been set.
1782       if (!VerifyNoIdsSet()) {
1783         return 1;
1784       }
1785     }
1786 
1787     // Add the names to mangle based on our source merge earlier.
1788     context_->SetNameManglerPolicy(
1789         NameManglerPolicy{context_->GetCompilationPackage(), table_merger_->merged_packages()});
1790 
1791     // Add our table to the symbol table.
1792     context_->GetExternalSymbols()->PrependSource(
1793         util::make_unique<ResourceTableSymbolSource>(&final_table_));
1794 
1795     // Workaround for pre-O runtime that would treat negative resource IDs
1796     // (any ID with a package ID > 7f) as invalid. Intercept any ID (PPTTEEEE) with PP > 0x7f
1797     // and type == 'id', and return the ID 0x7fPPEEEE. IDs don't need to be real resources, they
1798     // are just identifiers.
1799     if (context_->GetMinSdkVersion() < SDK_O && context_->GetPackageType() == PackageType::kApp) {
1800       if (context_->IsVerbose()) {
1801         context_->GetDiagnostics()->Note(DiagMessage()
1802                                          << "enabling pre-O feature split ID rewriting");
1803       }
1804       context_->GetExternalSymbols()->SetDelegate(
1805           util::make_unique<FeatureSplitSymbolTableDelegate>(context_));
1806     }
1807 
1808     // Before we process anything, remove the resources whose default values don't exist.
1809     // We want to force any references to these to fail the build.
1810     if (!options_.no_resource_removal) {
1811       if (!NoDefaultResourceRemover{}.Consume(context_, &final_table_)) {
1812         context_->GetDiagnostics()->Error(DiagMessage()
1813                                           << "failed removing resources with no defaults");
1814         return 1;
1815       }
1816     }
1817 
1818     ReferenceLinker linker;
1819     if (!linker.Consume(context_, &final_table_)) {
1820       context_->GetDiagnostics()->Error(DiagMessage() << "failed linking references");
1821       return 1;
1822     }
1823 
1824     if (context_->GetPackageType() == PackageType::kStaticLib) {
1825       if (!options_.products.empty()) {
1826         context_->GetDiagnostics()->Warn(DiagMessage()
1827                                          << "can't select products when building static library");
1828       }
1829     } else {
1830       ProductFilter product_filter(options_.products);
1831       if (!product_filter.Consume(context_, &final_table_)) {
1832         context_->GetDiagnostics()->Error(DiagMessage() << "failed stripping products");
1833         return 1;
1834       }
1835     }
1836 
1837     if (!options_.no_auto_version) {
1838       AutoVersioner versioner;
1839       if (!versioner.Consume(context_, &final_table_)) {
1840         context_->GetDiagnostics()->Error(DiagMessage() << "failed versioning styles");
1841         return 1;
1842       }
1843     }
1844 
1845     if (context_->GetPackageType() != PackageType::kStaticLib && context_->GetMinSdkVersion() > 0) {
1846       if (context_->IsVerbose()) {
1847         context_->GetDiagnostics()->Note(DiagMessage()
1848                                          << "collapsing resource versions for minimum SDK "
1849                                          << context_->GetMinSdkVersion());
1850       }
1851 
1852       VersionCollapser collapser;
1853       if (!collapser.Consume(context_, &final_table_)) {
1854         return 1;
1855       }
1856     }
1857 
1858     if (!options_.exclude_configs_.empty()) {
1859       std::vector<ConfigDescription> excluded_configs;
1860 
1861       for (auto& config_string : options_.exclude_configs_) {
1862         TRACE_NAME("ConfigDescription::Parse");
1863         ConfigDescription config_description;
1864 
1865         if (!ConfigDescription::Parse(config_string, &config_description)) {
1866           context_->GetDiagnostics()->Error(DiagMessage()
1867                                                 << "failed to parse --excluded-configs "
1868                                                 << config_string);
1869           return 1;
1870         }
1871 
1872         excluded_configs.push_back(config_description);
1873       }
1874 
1875       ResourceExcluder excluder(excluded_configs);
1876       if (!excluder.Consume(context_, &final_table_)) {
1877         context_->GetDiagnostics()->Error(DiagMessage() << "failed excluding configurations");
1878         return 1;
1879       }
1880     }
1881 
1882     if (!options_.no_resource_deduping) {
1883       ResourceDeduper deduper;
1884       if (!deduper.Consume(context_, &final_table_)) {
1885         context_->GetDiagnostics()->Error(DiagMessage() << "failed deduping resources");
1886         return 1;
1887       }
1888     }
1889 
1890     proguard::KeepSet proguard_keep_set =
1891         proguard::KeepSet(options_.generate_conditional_proguard_rules);
1892     proguard::KeepSet proguard_main_dex_keep_set;
1893 
1894     if (context_->GetPackageType() == PackageType::kStaticLib) {
1895       if (options_.table_splitter_options.config_filter != nullptr ||
1896           !options_.table_splitter_options.preferred_densities.empty()) {
1897         context_->GetDiagnostics()->Warn(DiagMessage()
1898                                          << "can't strip resources when building static library");
1899       }
1900     } else {
1901       // Adjust the SplitConstraints so that their SDK version is stripped if it is less than or
1902       // equal to the minSdk.
1903       const size_t origConstraintSize = options_.split_constraints.size();
1904       options_.split_constraints =
1905           AdjustSplitConstraintsForMinSdk(context_->GetMinSdkVersion(), options_.split_constraints);
1906 
1907       if (origConstraintSize != options_.split_constraints.size()) {
1908         context_->GetDiagnostics()->Warn(DiagMessage()
1909                                          << "requested to split resources prior to min sdk of "
1910                                          << context_->GetMinSdkVersion());
1911       }
1912       TableSplitter table_splitter(options_.split_constraints, options_.table_splitter_options);
1913       if (!table_splitter.VerifySplitConstraints(context_)) {
1914         return 1;
1915       }
1916       table_splitter.SplitTable(&final_table_);
1917 
1918       // Now we need to write out the Split APKs.
1919       auto path_iter = options_.split_paths.begin();
1920       auto split_constraints_iter = options_.split_constraints.begin();
1921       for (std::unique_ptr<ResourceTable>& split_table : table_splitter.splits()) {
1922         if (context_->IsVerbose()) {
1923           context_->GetDiagnostics()->Note(DiagMessage(*path_iter)
1924                                            << "generating split with configurations '"
1925                                            << util::Joiner(split_constraints_iter->configs, ", ")
1926                                            << "'");
1927         }
1928 
1929         std::unique_ptr<IArchiveWriter> archive_writer = MakeArchiveWriter(*path_iter);
1930         if (!archive_writer) {
1931           context_->GetDiagnostics()->Error(DiagMessage() << "failed to create archive");
1932           return 1;
1933         }
1934 
1935         // Generate an AndroidManifest.xml for each split.
1936         std::unique_ptr<xml::XmlResource> split_manifest =
1937             GenerateSplitManifest(app_info_, *split_constraints_iter);
1938 
1939         XmlReferenceLinker linker;
1940         if (!linker.Consume(context_, split_manifest.get())) {
1941           context_->GetDiagnostics()->Error(DiagMessage()
1942                                             << "failed to create Split AndroidManifest.xml");
1943           return 1;
1944         }
1945 
1946         if (!WriteApk(archive_writer.get(), &proguard_keep_set, split_manifest.get(),
1947                       split_table.get())) {
1948           return 1;
1949         }
1950 
1951         ++path_iter;
1952         ++split_constraints_iter;
1953       }
1954     }
1955 
1956     // Start writing the base APK.
1957     std::unique_ptr<IArchiveWriter> archive_writer = MakeArchiveWriter(options_.output_path);
1958     if (!archive_writer) {
1959       context_->GetDiagnostics()->Error(DiagMessage() << "failed to create archive");
1960       return 1;
1961     }
1962 
1963     bool error = false;
1964     {
1965       // AndroidManifest.xml has no resource name, but the CallSite is built from the name
1966       // (aka, which package the AndroidManifest.xml is coming from).
1967       // So we give it a package name so it can see local resources.
1968       manifest_xml->file.name.package = context_->GetCompilationPackage();
1969 
1970       XmlReferenceLinker manifest_linker;
1971       if (manifest_linker.Consume(context_, manifest_xml.get())) {
1972         if (options_.generate_proguard_rules_path &&
1973             !proguard::CollectProguardRulesForManifest(manifest_xml.get(), &proguard_keep_set)) {
1974           error = true;
1975         }
1976 
1977         if (options_.generate_main_dex_proguard_rules_path &&
1978             !proguard::CollectProguardRulesForManifest(manifest_xml.get(),
1979                                                        &proguard_main_dex_keep_set, true)) {
1980           error = true;
1981         }
1982 
1983         if (options_.generate_java_class_path) {
1984           if (!WriteManifestJavaFile(manifest_xml.get())) {
1985             error = true;
1986           }
1987         }
1988 
1989         if (options_.no_xml_namespaces) {
1990           // PackageParser will fail if URIs are removed from
1991           // AndroidManifest.xml.
1992           XmlNamespaceRemover namespace_remover(true /* keepUris */);
1993           if (!namespace_remover.Consume(context_, manifest_xml.get())) {
1994             error = true;
1995           }
1996         }
1997       } else {
1998         error = true;
1999       }
2000     }
2001 
2002     if (error) {
2003       context_->GetDiagnostics()->Error(DiagMessage() << "failed processing manifest");
2004       return 1;
2005     }
2006 
2007     if (!WriteApk(archive_writer.get(), &proguard_keep_set, manifest_xml.get(), &final_table_)) {
2008       return 1;
2009     }
2010 
2011     if (!CopyAssetsDirsToApk(archive_writer.get())) {
2012       return 1;
2013     }
2014 
2015     if (options_.generate_java_class_path || options_.generate_text_symbols_path) {
2016       if (!GenerateJavaClasses()) {
2017         return 1;
2018       }
2019     }
2020 
2021     if (!WriteProguardFile(options_.generate_proguard_rules_path, proguard_keep_set)) {
2022       return 1;
2023     }
2024 
2025     if (!WriteProguardFile(options_.generate_main_dex_proguard_rules_path,
2026                            proguard_main_dex_keep_set)) {
2027       return 1;
2028     }
2029     return 0;
2030   }
2031 
2032  private:
2033   LinkOptions options_;
2034   LinkContext* context_;
2035   ResourceTable final_table_;
2036 
2037   AppInfo app_info_;
2038 
2039   std::unique_ptr<TableMerger> table_merger_;
2040 
2041   // A pointer to the FileCollection representing the filesystem (not archives).
2042   std::unique_ptr<io::FileCollection> file_collection_;
2043 
2044   // A vector of IFileCollections. This is mainly here to retain ownership of the
2045   // collections.
2046   std::vector<std::unique_ptr<io::IFileCollection>> collections_;
2047 
2048   // The set of merged APKs. This is mainly here to retain ownership of the APKs.
2049   std::vector<std::unique_ptr<LoadedApk>> merged_apks_;
2050 
2051   // The set of included APKs (not merged). This is mainly here to retain ownership of the APKs.
2052   std::vector<std::unique_ptr<LoadedApk>> static_library_includes_;
2053 
2054   // The set of shared libraries being used, mapping their assigned package ID to package name.
2055   std::map<size_t, std::string> shared_libs_;
2056 
2057   // The package name of the base application, if it is included.
2058   Maybe<std::string> included_feature_base_;
2059 };
2060 
Action(const std::vector<std::string> & args)2061 int LinkCommand::Action(const std::vector<std::string>& args) {
2062   TRACE_FLUSH(trace_folder_ ? trace_folder_.value() : "", "LinkCommand::Action");
2063   LinkContext context(diag_);
2064 
2065   // Expand all argument-files passed into the command line. These start with '@'.
2066   std::vector<std::string> arg_list;
2067   for (const std::string& arg : args) {
2068     if (util::StartsWith(arg, "@")) {
2069       const std::string path = arg.substr(1, arg.size() - 1);
2070       std::string error;
2071       if (!file::AppendArgsFromFile(path, &arg_list, &error)) {
2072         context.GetDiagnostics()->Error(DiagMessage(path) << error);
2073         return 1;
2074       }
2075     } else {
2076       arg_list.push_back(arg);
2077     }
2078   }
2079 
2080   // Expand all argument-files passed to -R.
2081   for (const std::string& arg : overlay_arg_list_) {
2082     if (util::StartsWith(arg, "@")) {
2083       const std::string path = arg.substr(1, arg.size() - 1);
2084       std::string error;
2085       if (!file::AppendArgsFromFile(path, &options_.overlay_files, &error)) {
2086         context.GetDiagnostics()->Error(DiagMessage(path) << error);
2087         return 1;
2088       }
2089     } else {
2090       options_.overlay_files.push_back(arg);
2091     }
2092   }
2093 
2094   if (verbose_) {
2095     context.SetVerbose(verbose_);
2096   }
2097 
2098   if (int{shared_lib_} + int{static_lib_} + int{proto_format_} > 1) {
2099     context.GetDiagnostics()->Error(
2100         DiagMessage()
2101             << "only one of --shared-lib, --static-lib, or --proto_format can be defined");
2102     return 1;
2103   }
2104 
2105   // The default build type.
2106   context.SetPackageType(PackageType::kApp);
2107   context.SetPackageId(kAppPackageId);
2108 
2109   if (shared_lib_) {
2110     context.SetPackageType(PackageType::kSharedLib);
2111     context.SetPackageId(0x00);
2112   } else if (static_lib_) {
2113     context.SetPackageType(PackageType::kStaticLib);
2114     options_.output_format = OutputFormat::kProto;
2115   } else if (proto_format_) {
2116     options_.output_format = OutputFormat::kProto;
2117   }
2118 
2119   if (package_id_) {
2120     if (context.GetPackageType() != PackageType::kApp) {
2121       context.GetDiagnostics()->Error(
2122           DiagMessage() << "can't specify --package-id when not building a regular app");
2123       return 1;
2124     }
2125 
2126     const Maybe<uint32_t> maybe_package_id_int = ResourceUtils::ParseInt(package_id_.value());
2127     if (!maybe_package_id_int) {
2128       context.GetDiagnostics()->Error(DiagMessage() << "package ID '" << package_id_.value()
2129                                                     << "' is not a valid integer");
2130       return 1;
2131     }
2132 
2133     const uint32_t package_id_int = maybe_package_id_int.value();
2134     if (package_id_int > std::numeric_limits<uint8_t>::max()
2135         || package_id_int == kFrameworkPackageId
2136         || (!options_.allow_reserved_package_id && package_id_int < kAppPackageId)) {
2137       context.GetDiagnostics()->Error(
2138           DiagMessage() << StringPrintf(
2139               "invalid package ID 0x%02x. Must be in the range 0x7f-0xff.", package_id_int));
2140       return 1;
2141     }
2142     context.SetPackageId(static_cast<uint8_t>(package_id_int));
2143   }
2144 
2145   // Populate the set of extra packages for which to generate R.java.
2146   for (std::string& extra_package : extra_java_packages_) {
2147     // A given package can actually be a colon separated list of packages.
2148     for (StringPiece package : util::Split(extra_package, ':')) {
2149       options_.extra_java_packages.insert(package.to_string());
2150     }
2151   }
2152 
2153   if (product_list_) {
2154     for (StringPiece product : util::Tokenize(product_list_.value(), ',')) {
2155       if (product != "" && product != "default") {
2156         options_.products.insert(product.to_string());
2157       }
2158     }
2159   }
2160 
2161   std::unique_ptr<IConfigFilter> filter;
2162   if (!configs_.empty()) {
2163     filter = ParseConfigFilterParameters(configs_, context.GetDiagnostics());
2164     if (filter == nullptr) {
2165       return 1;
2166     }
2167     options_.table_splitter_options.config_filter = filter.get();
2168   }
2169 
2170   if (preferred_density_) {
2171     Maybe<uint16_t> density =
2172         ParseTargetDensityParameter(preferred_density_.value(), context.GetDiagnostics());
2173     if (!density) {
2174       return 1;
2175     }
2176     options_.table_splitter_options.preferred_densities.push_back(density.value());
2177   }
2178 
2179   // Parse the split parameters.
2180   for (const std::string& split_arg : split_args_) {
2181     options_.split_paths.push_back({});
2182     options_.split_constraints.push_back({});
2183     if (!ParseSplitParameter(split_arg, context.GetDiagnostics(), &options_.split_paths.back(),
2184         &options_.split_constraints.back())) {
2185       return 1;
2186     }
2187   }
2188 
2189   if (context.GetPackageType() != PackageType::kStaticLib && stable_id_file_path_) {
2190     if (!LoadStableIdMap(context.GetDiagnostics(), stable_id_file_path_.value(),
2191         &options_.stable_id_map)) {
2192       return 1;
2193     }
2194   }
2195 
2196   if (no_compress_regex) {
2197     std::string regex = no_compress_regex.value();
2198     if (util::StartsWith(regex, "@")) {
2199       const std::string path = regex.substr(1, regex.size() -1);
2200       std::string error;
2201       if (!file::AppendSetArgsFromFile(path, &options_.extensions_to_not_compress, &error)) {
2202         context.GetDiagnostics()->Error(DiagMessage(path) << error);
2203         return 1;
2204       }
2205     } else {
2206       options_.regex_to_not_compress = GetRegularExpression(no_compress_regex.value());
2207     }
2208   }
2209 
2210   // Populate some default no-compress extensions that are already compressed.
2211   options_.extensions_to_not_compress.insert(
2212       {".jpg",   ".jpeg", ".png",  ".gif", ".wav",  ".mp2",  ".mp3",  ".ogg",
2213           ".aac",   ".mpg",  ".mpeg", ".mid", ".midi", ".smf",  ".jet",  ".rtttl",
2214           ".imy",   ".xmf",  ".mp4",  ".m4a", ".m4v",  ".3gp",  ".3gpp", ".3g2",
2215           ".3gpp2", ".amr",  ".awb",  ".wma", ".wmv",  ".webm", ".mkv"});
2216 
2217   // Turn off auto versioning for static-libs.
2218   if (context.GetPackageType() == PackageType::kStaticLib) {
2219     options_.no_auto_version = true;
2220     options_.no_version_vectors = true;
2221     options_.no_version_transitions = true;
2222   }
2223 
2224   Linker cmd(&context, options_);
2225   return cmd.Run(arg_list);
2226 }
2227 
2228 }  // namespace aapt
2229