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