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