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