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