|  | /* | 
|  | * Copyright (C) 2015 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include "link/ReferenceLinker.h" | 
|  |  | 
|  | #include "ResourceParser.h" | 
|  | #include "ResourceTable.h" | 
|  | #include "ResourceUtils.h" | 
|  | #include "ResourceValues.h" | 
|  | #include "ValueVisitor.h" | 
|  | #include "android-base/logging.h" | 
|  | #include "android-base/stringprintf.h" | 
|  | #include "androidfw/IDiagnostics.h" | 
|  | #include "androidfw/ResourceTypes.h" | 
|  | #include "link/Linkers.h" | 
|  | #include "process/IResourceTableConsumer.h" | 
|  | #include "process/SymbolTable.h" | 
|  | #include "trace/TraceBuffer.h" | 
|  | #include "util/Util.h" | 
|  | #include "xml/XmlUtil.h" | 
|  |  | 
|  | using ::aapt::ResourceUtils::StringBuilder; | 
|  | using ::android::StringPiece; | 
|  | using ::android::base::StringPrintf; | 
|  |  | 
|  | namespace aapt { | 
|  | namespace { | 
|  | struct LoggingResourceName { | 
|  | LoggingResourceName(const Reference& ref, const CallSite& callsite, | 
|  | const xml::IPackageDeclStack* decls) | 
|  | : ref_(ref), callsite_(callsite), decls_(decls) { | 
|  | } | 
|  |  | 
|  | const Reference& ref_; | 
|  | const CallSite& callsite_; | 
|  | const xml::IPackageDeclStack* decls_; | 
|  | }; | 
|  |  | 
|  | inline ::std::ostream& operator<<(::std::ostream& out, const LoggingResourceName& name) { | 
|  | if (!name.ref_.name) { | 
|  | out << name.ref_.id.value(); | 
|  | return out; | 
|  | } | 
|  |  | 
|  | out << name.ref_.name.value(); | 
|  |  | 
|  | Reference fully_qualified = name.ref_; | 
|  | xml::ResolvePackage(name.decls_, &fully_qualified); | 
|  |  | 
|  | ResourceName& full_name = fully_qualified.name.value(); | 
|  | if (full_name.package.empty()) { | 
|  | full_name.package = name.callsite_.package; | 
|  | } | 
|  |  | 
|  | if (full_name != name.ref_.name.value()) { | 
|  | out << " (aka " << full_name << ")"; | 
|  | } | 
|  | return out; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | std::unique_ptr<Reference> ReferenceLinkerTransformer::TransformDerived(const Reference* value) { | 
|  | auto linked_item = | 
|  | ReferenceLinker::LinkReference(callsite_, *value, context_, symbols_, table_, package_decls_); | 
|  | if (linked_item) { | 
|  | auto linked_item_ptr = linked_item.release(); | 
|  | if (auto ref = ValueCast<Reference>(linked_item_ptr)) { | 
|  | return std::unique_ptr<Reference>(ref); | 
|  | } | 
|  | context_->GetDiagnostics()->Error(android::DiagMessage(value->GetSource()) | 
|  | << "value of '" | 
|  | << LoggingResourceName(*value, callsite_, package_decls_) | 
|  | << "' must be a resource reference"); | 
|  | delete linked_item_ptr; | 
|  | } | 
|  |  | 
|  | error_ = true; | 
|  | return CloningValueTransformer::TransformDerived(value); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<Style> ReferenceLinkerTransformer::TransformDerived(const Style* style) { | 
|  | // We visit the Style specially because during this phase, values of attributes are either | 
|  | // RawString or Reference values. Now that we are expected to resolve all symbols, we can lookup | 
|  | // the attributes to find out which types are allowed for the attributes' values. | 
|  | auto new_style = CloningValueTransformer::TransformDerived(style); | 
|  | if (new_style->parent) { | 
|  | new_style->parent = *TransformDerived(&style->parent.value()); | 
|  | } | 
|  |  | 
|  | for (Style::Entry& entry : new_style->entries) { | 
|  | std::string err_str; | 
|  |  | 
|  | // Transform the attribute reference so that it is using the fully qualified package | 
|  | // name. This will also mark the reference as being able to see private resources if | 
|  | // there was a '*' in the reference or if the package came from the private namespace. | 
|  | Reference transformed_reference = entry.key; | 
|  | ResolvePackage(package_decls_, &transformed_reference); | 
|  |  | 
|  | // Find the attribute in the symbol table and check if it is visible from this callsite. | 
|  | const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility( | 
|  | transformed_reference, callsite_, context_, symbols_, &err_str); | 
|  | if (symbol) { | 
|  | // Assign our style key the correct ID. The ID may not exist. | 
|  | entry.key.id = symbol->id; | 
|  |  | 
|  | // Link/resolve the final value if it's a reference. | 
|  | entry.value = entry.value->Transform(*this); | 
|  |  | 
|  | // Try to convert the value to a more specific, typed value based on the attribute it is | 
|  | // set to. | 
|  | entry.value = ParseValueWithAttribute(std::move(entry.value), symbol->attribute.get()); | 
|  |  | 
|  | // Now verify that the type of this item is compatible with the | 
|  | // attribute it is defined for. We pass `nullptr` as the DiagMessage so that this | 
|  | // check is fast and we avoid creating a DiagMessage when the match is successful. | 
|  | if (!symbol->attribute->Matches(*entry.value, nullptr)) { | 
|  | // The actual type of this item is incompatible with the attribute. | 
|  | android::DiagMessage msg(entry.key.GetSource()); | 
|  |  | 
|  | // Call the matches method again, this time with a DiagMessage so we fill in the actual | 
|  | // error message. | 
|  | symbol->attribute->Matches(*entry.value, &msg); | 
|  | context_->GetDiagnostics()->Error(msg); | 
|  | error_ = true; | 
|  | } | 
|  | } else { | 
|  | context_->GetDiagnostics()->Error(android::DiagMessage(entry.key.GetSource()) | 
|  | << "style attribute '" | 
|  | << LoggingResourceName(entry.key, callsite_, package_decls_) | 
|  | << "' " << err_str); | 
|  |  | 
|  | error_ = true; | 
|  | } | 
|  | } | 
|  | return new_style; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<Item> ReferenceLinkerTransformer::TransformItem(const Reference* value) { | 
|  | auto linked_value = | 
|  | ReferenceLinker::LinkReference(callsite_, *value, context_, symbols_, table_, package_decls_); | 
|  | if (linked_value) { | 
|  | return linked_value; | 
|  | } | 
|  | error_ = true; | 
|  | return CloningValueTransformer::TransformDerived(value); | 
|  | } | 
|  |  | 
|  | // Transform a RawString value into a more specific, appropriate value, based on the | 
|  | // Attribute. If a non RawString value is passed in, this is an identity transform. | 
|  | std::unique_ptr<Item> ReferenceLinkerTransformer::ParseValueWithAttribute( | 
|  | std::unique_ptr<Item> value, const Attribute* attr) { | 
|  | if (RawString* raw_string = ValueCast<RawString>(value.get())) { | 
|  | std::unique_ptr<Item> transformed = | 
|  | ResourceUtils::TryParseItemForAttribute(*raw_string->value, attr); | 
|  |  | 
|  | // If we could not parse as any specific type, try a basic STRING. | 
|  | if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) { | 
|  | StringBuilder string_builder; | 
|  | string_builder.AppendText(*raw_string->value); | 
|  | if (string_builder) { | 
|  | transformed = util::make_unique<String>(pool_->MakeRef(string_builder.to_string())); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (transformed) { | 
|  | return transformed; | 
|  | } | 
|  | } | 
|  | return value; | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class EmptyDeclStack : public xml::IPackageDeclStack { | 
|  | public: | 
|  | EmptyDeclStack() = default; | 
|  |  | 
|  | std::optional<xml::ExtractedPackage> TransformPackageAlias(StringPiece alias) const override { | 
|  | if (alias.empty()) { | 
|  | return xml::ExtractedPackage{{}, true /*private*/}; | 
|  | } | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(EmptyDeclStack); | 
|  | }; | 
|  |  | 
|  | struct MacroDeclStack : public xml::IPackageDeclStack { | 
|  | explicit MacroDeclStack(std::vector<Macro::Namespace> namespaces) | 
|  | : alias_namespaces_(std::move(namespaces)) { | 
|  | } | 
|  |  | 
|  | std::optional<xml::ExtractedPackage> TransformPackageAlias(StringPiece alias) const override { | 
|  | if (alias.empty()) { | 
|  | return xml::ExtractedPackage{{}, true /*private*/}; | 
|  | } | 
|  | for (auto it = alias_namespaces_.rbegin(); it != alias_namespaces_.rend(); ++it) { | 
|  | if (alias == StringPiece(it->alias)) { | 
|  | return xml::ExtractedPackage{it->package_name, it->is_private}; | 
|  | } | 
|  | } | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | private: | 
|  | std::vector<Macro::Namespace> alias_namespaces_; | 
|  | }; | 
|  |  | 
|  | // The symbol is visible if it is public, or if the reference to it is requesting private access | 
|  | // or if the callsite comes from the same package. | 
|  | bool IsSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref, | 
|  | const CallSite& callsite) { | 
|  | if (symbol.is_public || ref.private_reference) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (ref.name) { | 
|  | const ResourceName& name = ref.name.value(); | 
|  | if (name.package.empty()) { | 
|  | // If the symbol was found, and the package is empty, that means it was found in the local | 
|  | // scope, which is always visible (private local). | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // The symbol is visible if the reference is local to the same package it is defined in. | 
|  | return callsite.package == name.package; | 
|  | } | 
|  |  | 
|  | if (ref.id && symbol.id) { | 
|  | return ref.id.value().package_id() == symbol.id.value().package_id(); | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& reference, | 
|  | const CallSite& callsite, | 
|  | IAaptContext* context, | 
|  | SymbolTable* symbols) { | 
|  | if (reference.name) { | 
|  | const ResourceName& name = reference.name.value(); | 
|  | if (name.package.empty()) { | 
|  | // Use the callsite's package name if no package name was defined. | 
|  | const SymbolTable::Symbol* symbol = symbols->FindByName( | 
|  | ResourceName(callsite.package, name.type, name.entry)); | 
|  | if (symbol) { | 
|  | return symbol; | 
|  | } | 
|  |  | 
|  | // If the callsite package is the same as the current compilation package, | 
|  | // check the feature split dependencies as well. Feature split resources | 
|  | // can be referenced without a namespace, just like the base package. | 
|  | if (callsite.package == context->GetCompilationPackage()) { | 
|  | const auto& split_name_dependencies = context->GetSplitNameDependencies(); | 
|  | for (const std::string& split_name : split_name_dependencies) { | 
|  | std::string split_package = | 
|  | StringPrintf("%s.%s", callsite.package.c_str(), split_name.c_str()); | 
|  | symbol = symbols->FindByName(ResourceName(split_package, name.type, name.entry)); | 
|  | if (symbol) { | 
|  | return symbol; | 
|  | } | 
|  | } | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  | return symbols->FindByName(name); | 
|  | } else if (reference.id) { | 
|  | return symbols->FindById(reference.id.value()); | 
|  | } else { | 
|  | return nullptr; | 
|  | } | 
|  | } | 
|  |  | 
|  | const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const Reference& reference, | 
|  | const CallSite& callsite, | 
|  | IAaptContext* context, | 
|  | SymbolTable* symbols, | 
|  | std::string* out_error) { | 
|  | const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, context, symbols); | 
|  | if (!symbol) { | 
|  | if (out_error) *out_error = "not found"; | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | if (!IsSymbolVisible(*symbol, reference, callsite)) { | 
|  | if (out_error) *out_error = "is private"; | 
|  | return nullptr; | 
|  | } | 
|  | return symbol; | 
|  | } | 
|  |  | 
|  | const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility( | 
|  | const Reference& reference, const CallSite& callsite, IAaptContext* context, | 
|  | SymbolTable* symbols, std::string* out_error) { | 
|  | const SymbolTable::Symbol* symbol = | 
|  | ResolveSymbolCheckVisibility(reference, callsite, context, symbols, out_error); | 
|  | if (!symbol) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | if (!symbol->attribute) { | 
|  | if (out_error) *out_error = "is not an attribute"; | 
|  | return nullptr; | 
|  | } | 
|  | return symbol; | 
|  | } | 
|  |  | 
|  | std::optional<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference, | 
|  | const CallSite& callsite, | 
|  | IAaptContext* context, | 
|  | SymbolTable* symbols, | 
|  | std::string* out_error) { | 
|  | const SymbolTable::Symbol* symbol = | 
|  | ResolveAttributeCheckVisibility(reference, callsite, context, symbols, out_error); | 
|  | if (!symbol) { | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | if (!symbol->attribute) { | 
|  | if (out_error) *out_error = "is not an attribute"; | 
|  | return {}; | 
|  | } | 
|  | return xml::AaptAttribute(*symbol->attribute, symbol->id); | 
|  | } | 
|  |  | 
|  | void ReferenceLinker::WriteAttributeName(const Reference& ref, const CallSite& callsite, | 
|  | const xml::IPackageDeclStack* decls, | 
|  | android::DiagMessage* out_msg) { | 
|  | CHECK(out_msg != nullptr); | 
|  | if (!ref.name) { | 
|  | *out_msg << ref.id.value(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | const ResourceName& ref_name = ref.name.value(); | 
|  | CHECK_EQ(ref_name.type.type, ResourceType::kAttr); | 
|  |  | 
|  | if (!ref_name.package.empty()) { | 
|  | *out_msg << ref_name.package << ":"; | 
|  | } | 
|  | *out_msg << ref_name.entry; | 
|  |  | 
|  | Reference fully_qualified = ref; | 
|  | xml::ResolvePackage(decls, &fully_qualified); | 
|  |  | 
|  | ResourceName& full_name = fully_qualified.name.value(); | 
|  | if (full_name.package.empty()) { | 
|  | full_name.package = callsite.package; | 
|  | } | 
|  |  | 
|  | if (full_name != ref.name.value()) { | 
|  | *out_msg << " (aka " << full_name.package << ":" << full_name.entry << ")"; | 
|  | } | 
|  | } | 
|  |  | 
|  | std::unique_ptr<Item> ReferenceLinker::LinkReference(const CallSite& callsite, | 
|  | const Reference& reference, | 
|  | IAaptContext* context, SymbolTable* symbols, | 
|  | ResourceTable* table, | 
|  | const xml::IPackageDeclStack* decls) { | 
|  | if (!reference.name && !reference.id) { | 
|  | // This is @null. | 
|  | return std::make_unique<Reference>(reference); | 
|  | } | 
|  |  | 
|  | Reference transformed_reference = reference; | 
|  | xml::ResolvePackage(decls, &transformed_reference); | 
|  |  | 
|  | if (transformed_reference.name.value().type.type == ResourceType::kMacro) { | 
|  | if (transformed_reference.name.value().package.empty()) { | 
|  | transformed_reference.name.value().package = callsite.package; | 
|  | } | 
|  |  | 
|  | auto result = table->FindResource(transformed_reference.name.value()); | 
|  | if (!result || result.value().entry->values.empty()) { | 
|  | context->GetDiagnostics()->Error( | 
|  | android::DiagMessage(reference.GetSource()) | 
|  | << "failed to find definition for " | 
|  | << LoggingResourceName(transformed_reference, callsite, decls)); | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | auto& macro_values = result.value().entry->values; | 
|  | CHECK(macro_values.size() == 1) << "Macros can only be defined in the default configuration."; | 
|  |  | 
|  | auto macro = ValueCast<Macro>(macro_values[0]->value.get()); | 
|  | CHECK(macro != nullptr) << "Value of macro resource is not a Macro (actual " | 
|  | << *macro_values[0]->value << ")"; | 
|  |  | 
|  | // Re-create the state used to parse the macro tag to compile the macro contents as if it was | 
|  | // defined inline | 
|  | uint32_t type_flags = 0; | 
|  | if (reference.type_flags.has_value()) { | 
|  | type_flags = reference.type_flags.value(); | 
|  | } | 
|  |  | 
|  | MacroDeclStack namespace_stack(macro->alias_namespaces); | 
|  | FlattenedXmlSubTree sub_tree{.raw_value = macro->raw_value, | 
|  | .style_string = macro->style_string, | 
|  | .untranslatable_sections = macro->untranslatable_sections, | 
|  | .namespace_resolver = &namespace_stack, | 
|  | .source = macro->GetSource()}; | 
|  |  | 
|  | auto new_value = ResourceParser::ParseXml(sub_tree, type_flags, reference.allow_raw, *table, | 
|  | macro_values[0]->config, *context->GetDiagnostics()); | 
|  | if (new_value == nullptr) { | 
|  | context->GetDiagnostics()->Error( | 
|  | android::DiagMessage(reference.GetSource()) | 
|  | << "failed to substitute macro " | 
|  | << LoggingResourceName(transformed_reference, callsite, decls) | 
|  | << ": failed to parse contents as one of type(s) " << Attribute::MaskString(type_flags)); | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | if (auto ref = ValueCast<Reference>(new_value.get())) { | 
|  | return LinkReference(callsite, *ref, context, symbols, table, decls); | 
|  | } | 
|  | return new_value; | 
|  | } | 
|  |  | 
|  | std::string err_str; | 
|  | const SymbolTable::Symbol* s = | 
|  | ResolveSymbolCheckVisibility(transformed_reference, callsite, context, symbols, &err_str); | 
|  | if (s) { | 
|  | // The ID may not exist. This is fine because of the possibility of building | 
|  | // against libraries without assigned IDs. | 
|  | // Ex: Linking against own resources when building a static library. | 
|  | auto new_ref = std::make_unique<Reference>(reference); | 
|  | new_ref->id = s->id; | 
|  | new_ref->is_dynamic = s->is_dynamic; | 
|  | return std::move(new_ref); | 
|  | } | 
|  |  | 
|  | context->GetDiagnostics()->Error(android::DiagMessage(reference.GetSource()) | 
|  | << "resource " | 
|  | << LoggingResourceName(transformed_reference, callsite, decls) | 
|  | << " " << err_str); | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) { | 
|  | TRACE_NAME("ReferenceLinker::Consume"); | 
|  | EmptyDeclStack decl_stack; | 
|  | bool error = false; | 
|  | for (auto& package : table->packages) { | 
|  | // Since we're linking, each package must have a name. | 
|  | CHECK(!package->name.empty()) << "all packages being linked must have a name"; | 
|  |  | 
|  | for (auto& type : package->types) { | 
|  | for (auto& entry : type->entries) { | 
|  | // First, unmangle the name if necessary. | 
|  | ResourceName name(package->name, type->named_type, entry->name); | 
|  | NameMangler::Unmangle(&name.entry, &name.package); | 
|  |  | 
|  | // Symbol state information may be lost if there is no value for the resource. | 
|  | if (entry->visibility.level != Visibility::Level::kUndefined && entry->values.empty()) { | 
|  | context->GetDiagnostics()->Error(android::DiagMessage(entry->visibility.source) | 
|  | << "no definition for declared symbol '" << name << "'"); | 
|  | error = true; | 
|  | } | 
|  |  | 
|  | // Ensure that definitions for values declared as overlayable exist | 
|  | if (entry->overlayable_item && entry->values.empty()) { | 
|  | context->GetDiagnostics()->Error( | 
|  | android::DiagMessage(entry->overlayable_item.value().source) | 
|  | << "no definition for overlayable symbol '" << name << "'"); | 
|  | error = true; | 
|  | } | 
|  |  | 
|  | // The context of this resource is the package in which it is defined. | 
|  | const CallSite callsite{name.package}; | 
|  | ReferenceLinkerTransformer reference_transformer(callsite, context, | 
|  | context->GetExternalSymbols(), | 
|  | &table->string_pool, table, &decl_stack); | 
|  |  | 
|  | for (auto& config_value : entry->values) { | 
|  | config_value->value = config_value->value->Transform(reference_transformer); | 
|  | } | 
|  |  | 
|  | if (reference_transformer.HasError()) { | 
|  | error = true; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return !error; | 
|  | } | 
|  |  | 
|  | }  // namespace aapt |