/* * 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 "process/SymbolTable.h" #include #include "android-base/logging.h" #include "android-base/stringprintf.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager2.h" #include "androidfw/ConfigDescription.h" #include "androidfw/ResourceTypes.h" #include "androidfw/ResourceUtils.h" #include "NameMangler.h" #include "Resource.h" #include "ResourceUtils.h" #include "ValueVisitor.h" #include "trace/TraceBuffer.h" #include "util/Util.h" using ::android::ApkAssets; using ::android::ConfigDescription; using ::android::StringPiece; using ::android::StringPiece16; namespace aapt { SymbolTable::SymbolTable(NameMangler* mangler) : mangler_(mangler), delegate_(util::make_unique()), cache_(200), id_cache_(200) { } void SymbolTable::SetDelegate(std::unique_ptr delegate) { CHECK(delegate != nullptr) << "can't set a nullptr delegate"; delegate_ = std::move(delegate); // Clear the cache in case this delegate changes the order of lookup. cache_.clear(); } void SymbolTable::AppendSource(std::unique_ptr source) { sources_.push_back(std::move(source)); // We do not clear the cache, because sources earlier in the list take // precedent. } void SymbolTable::PrependSource(std::unique_ptr source) { sources_.insert(sources_.begin(), std::move(source)); // We must clear the cache in case we did a lookup before adding this // resource. cache_.clear(); } const SymbolTable::Symbol* SymbolTable::FindByName(const ResourceName& name) { const ResourceName* name_with_package = &name; // Fill in the package name if necessary. // If there is no package in `name`, we will need to copy the ResourceName // and store it somewhere; we use the Maybe<> class to reserve storage. Maybe name_with_package_impl; if (name.package.empty()) { name_with_package_impl = ResourceName(mangler_->GetTargetPackageName(), name.type, name.entry); name_with_package = &name_with_package_impl.value(); } // We store the name unmangled in the cache, so look it up as-is. if (const std::shared_ptr& s = cache_.get(*name_with_package)) { return s.get(); } // The name was not found in the cache. Mangle it (if necessary) and find it in our sources. // Again, here we use a Maybe<> object to reserve storage if we need to mangle. const ResourceName* mangled_name = name_with_package; Maybe mangled_name_impl; if (mangler_->ShouldMangle(name_with_package->package)) { mangled_name_impl = mangler_->MangleName(*name_with_package); mangled_name = &mangled_name_impl.value(); } std::unique_ptr symbol = delegate_->FindByName(*mangled_name, sources_); if (symbol == nullptr) { return nullptr; } // Take ownership of the symbol into a shared_ptr. We do this because // LruCache doesn't support unique_ptr. std::shared_ptr shared_symbol(std::move(symbol)); // Since we look in the cache with the unmangled, but package prefixed // name, we must put the same name into the cache. cache_.put(*name_with_package, shared_symbol); if (shared_symbol->id) { // The symbol has an ID, so we can also cache this! id_cache_.put(shared_symbol->id.value(), shared_symbol); } // Returns the raw pointer. Callers are not expected to hold on to this // between calls to Find*. return shared_symbol.get(); } const SymbolTable::Symbol* SymbolTable::FindById(const ResourceId& id) { if (const std::shared_ptr& s = id_cache_.get(id)) { return s.get(); } // We did not find it in the cache, so look through the sources. std::unique_ptr symbol = delegate_->FindById(id, sources_); if (symbol == nullptr) { return nullptr; } // Take ownership of the symbol into a shared_ptr. We do this because LruCache // doesn't support unique_ptr. std::shared_ptr shared_symbol(std::move(symbol)); id_cache_.put(id, shared_symbol); // Returns the raw pointer. Callers are not expected to hold on to this // between calls to Find*. return shared_symbol.get(); } const SymbolTable::Symbol* SymbolTable::FindByReference(const Reference& ref) { // First try the ID. This is because when we lookup by ID, we only fill in the ID cache. // Looking up by name fills in the name and ID cache. So a cache miss will cause a failed // ID lookup, then a successful name lookup. Subsequent look ups will hit immediately // because the ID is cached too. // // If we looked up by name first, a cache miss would mean we failed to lookup by name, then // succeeded to lookup by ID. Subsequent lookups will miss then hit. const SymbolTable::Symbol* symbol = nullptr; if (ref.id) { symbol = FindById(ref.id.value()); } if (ref.name && !symbol) { symbol = FindByName(ref.name.value()); } return symbol; } std::unique_ptr DefaultSymbolTableDelegate::FindByName( const ResourceName& name, const std::vector>& sources) { for (auto& source : sources) { std::unique_ptr symbol = source->FindByName(name); if (symbol) { return symbol; } } return {}; } std::unique_ptr DefaultSymbolTableDelegate::FindById( ResourceId id, const std::vector>& sources) { for (auto& source : sources) { std::unique_ptr symbol = source->FindById(id); if (symbol) { return symbol; } } return {}; } std::unique_ptr ResourceTableSymbolSource::FindByName( const ResourceName& name) { Maybe result = table_->FindResource(name); if (!result) { if (name.type == ResourceType::kAttr) { // Recurse and try looking up a private attribute. return FindByName(ResourceName(name.package, ResourceType::kAttrPrivate, name.entry)); } return {}; } ResourceTable::SearchResult sr = result.value(); std::unique_ptr symbol = util::make_unique(); symbol->is_public = (sr.entry->visibility.level == Visibility::Level::kPublic); if (sr.package->id && sr.type->id && sr.entry->id) { symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value()); symbol->is_dynamic = (sr.package->id.value() == 0); } if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) { const ConfigDescription kDefaultConfig; ResourceConfigValue* config_value = sr.entry->FindValue(kDefaultConfig); if (config_value) { // This resource has an Attribute. if (Attribute* attr = ValueCast(config_value->value.get())) { symbol->attribute = std::make_shared(*attr); } else { return {}; } } } return symbol; } bool AssetManagerSymbolSource::AddAssetPath(const StringPiece& path) { TRACE_CALL(); if (std::unique_ptr apk = ApkAssets::Load(path.data())) { apk_assets_.push_back(std::move(apk)); std::vector apk_assets; for (const std::unique_ptr& apk_asset : apk_assets_) { apk_assets.push_back(apk_asset.get()); } asset_manager_.SetApkAssets(apk_assets, true /* invalidate_caches */, false /* filter_incompatible_configs */); return true; } return false; } std::map AssetManagerSymbolSource::GetAssignedPackageIds() const { TRACE_CALL(); std::map package_map; asset_manager_.ForEachPackage([&package_map](const std::string& name, uint8_t id) -> bool { package_map.insert(std::make_pair(id, name)); return true; }); return package_map; } bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId) const { if (packageId == 0) { return true; } for (const std::unique_ptr& assets : apk_assets_) { for (const std::unique_ptr& loaded_package : assets->GetLoadedArsc()->GetPackages()) { if (packageId == loaded_package->GetPackageId() && loaded_package->IsDynamic()) { return true; } } } return false; } static std::unique_ptr LookupAttributeInTable( android::AssetManager2& am, ResourceId id) { if (am.GetApkAssets().empty()) { return {}; } const android::ResolvedBag* bag = am.GetBag(id.id); if (bag == nullptr) { return nullptr; } // We found a resource. std::unique_ptr s = util::make_unique(id); const size_t count = bag->entry_count; for (uint32_t i = 0; i < count; i++) { if (bag->entries[i].key == android::ResTable_map::ATTR_TYPE) { s->attribute = std::make_shared(bag->entries[i].value.data); break; } } if (s->attribute) { for (size_t i = 0; i < count; i++) { const android::ResolvedBag::Entry& map_entry = bag->entries[i]; if (Res_INTERNALID(map_entry.key)) { switch (map_entry.key) { case android::ResTable_map::ATTR_MIN: s->attribute->min_int = static_cast(map_entry.value.data); break; case android::ResTable_map::ATTR_MAX: s->attribute->max_int = static_cast(map_entry.value.data); break; } continue; } android::AssetManager2::ResourceName name; if (!am.GetResourceName(map_entry.key, &name)) { return nullptr; } Maybe parsed_name = ResourceUtils::ToResourceName(name); if (!parsed_name) { return nullptr; } Attribute::Symbol symbol; symbol.symbol.name = parsed_name.value(); symbol.symbol.id = ResourceId(map_entry.key); symbol.value = map_entry.value.data; s->attribute->symbols.push_back(std::move(symbol)); } } return s; } std::unique_ptr AssetManagerSymbolSource::FindByName( const ResourceName& name) { const std::string mangled_entry = NameMangler::MangleEntry(name.package, name.entry); bool found = false; ResourceId res_id = 0; uint32_t type_spec_flags; // There can be mangled resources embedded within other packages. Here we will // look into each package and look-up the mangled name until we find the resource. asset_manager_.ForEachPackage([&](const std::string& package_name, uint8_t id) -> bool { ResourceName real_name(name.package, name.type, name.entry); if (package_name != name.package) { real_name.entry = mangled_entry; real_name.package = package_name; } res_id = asset_manager_.GetResourceId(real_name.to_string()); if (res_id.is_valid() && asset_manager_.GetResourceFlags(res_id.id, &type_spec_flags)) { found = true; return false; } return true; }); if (!found) { return {}; } std::unique_ptr s; if (name.type == ResourceType::kAttr) { s = LookupAttributeInTable(asset_manager_, res_id); } else { s = util::make_unique(); s->id = res_id; s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id()); } if (s) { s->is_public = (type_spec_flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0; return s; } return {}; } static Maybe GetResourceName(android::AssetManager2& am, ResourceId id) { android::AssetManager2::ResourceName name; if (!am.GetResourceName(id.id, &name)) { return {}; } return ResourceUtils::ToResourceName(name); } std::unique_ptr AssetManagerSymbolSource::FindById( ResourceId id) { if (!id.is_valid()) { // Exit early and avoid the error logs from AssetManager. return {}; } if (apk_assets_.empty()) { return {}; } Maybe maybe_name = GetResourceName(asset_manager_, id); if (!maybe_name) { return {}; } uint32_t type_spec_flags = 0; if (!asset_manager_.GetResourceFlags(id.id, &type_spec_flags)) { return {}; } ResourceName& name = maybe_name.value(); std::unique_ptr s; if (name.type == ResourceType::kAttr) { s = LookupAttributeInTable(asset_manager_, id); } else { s = util::make_unique(); s->id = id; s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id()); } if (s) { s->is_public = (type_spec_flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0; return s; } return {}; } std::unique_ptr AssetManagerSymbolSource::FindByReference( const Reference& ref) { // AssetManager always prefers IDs. if (ref.id) { return FindById(ref.id.value()); } else if (ref.name) { return FindByName(ref.name.value()); } return {}; } } // namespace aapt