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