| /* |
| * 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 "Maybe.h" |
| #include "NameMangler.h" |
| #include "Resource.h" |
| #include "ResourceTable.h" |
| #include "ResourceTableResolver.h" |
| #include "ResourceValues.h" |
| #include "Util.h" |
| |
| #include <androidfw/AssetManager.h> |
| #include <androidfw/ResourceTypes.h> |
| #include <memory> |
| #include <vector> |
| |
| namespace aapt { |
| |
| ResourceTableResolver::ResourceTableResolver( |
| std::shared_ptr<const ResourceTable> table, |
| const std::vector<std::shared_ptr<const android::AssetManager>>& sources) : |
| mTable(table), mSources(sources) { |
| for (const auto& assetManager : mSources) { |
| const android::ResTable& resTable = assetManager->getResources(false); |
| const size_t packageCount = resTable.getBasePackageCount(); |
| for (size_t i = 0; i < packageCount; i++) { |
| std::u16string packageName = resTable.getBasePackageName(i).string(); |
| mIncludedPackages.insert(std::move(packageName)); |
| } |
| } |
| } |
| |
| Maybe<ResourceId> ResourceTableResolver::findId(const ResourceName& name) { |
| Maybe<Entry> result = findAttribute(name); |
| if (result) { |
| return result.value().id; |
| } |
| return {}; |
| } |
| |
| Maybe<IResolver::Entry> ResourceTableResolver::findAttribute(const ResourceName& name) { |
| auto cacheIter = mCache.find(name); |
| if (cacheIter != std::end(mCache)) { |
| return Entry{ cacheIter->second.id, cacheIter->second.attr.get() }; |
| } |
| |
| ResourceName mangledName; |
| const ResourceName* nameToSearch = &name; |
| if (name.package != mTable->getPackage()) { |
| // This may be a reference to an included resource or |
| // to a mangled resource. |
| if (mIncludedPackages.find(name.package) == mIncludedPackages.end()) { |
| // This is not in our included set, so mangle the name and |
| // check for that. |
| mangledName.entry = name.entry; |
| NameMangler::mangle(name.package, &mangledName.entry); |
| mangledName.package = mTable->getPackage(); |
| mangledName.type = name.type; |
| nameToSearch = &mangledName; |
| } else { |
| const CacheEntry* cacheEntry = buildCacheEntry(name); |
| if (cacheEntry) { |
| return Entry{ cacheEntry->id, cacheEntry->attr.get() }; |
| } |
| return {}; |
| } |
| } |
| |
| const ResourceTableType* type; |
| const ResourceEntry* entry; |
| std::tie(type, entry) = mTable->findResource(*nameToSearch); |
| if (type && entry) { |
| Entry result = {}; |
| if (mTable->getPackageId() != ResourceTable::kUnsetPackageId && |
| type->typeId != ResourceTableType::kUnsetTypeId && |
| entry->entryId != ResourceEntry::kUnsetEntryId) { |
| result.id = ResourceId(mTable->getPackageId(), type->typeId, entry->entryId); |
| } |
| |
| if (!entry->values.empty()) { |
| visitFunc<Attribute>(*entry->values.front().value, [&result](Attribute& attr) { |
| result.attr = &attr; |
| }); |
| } |
| return result; |
| } |
| return {}; |
| } |
| |
| Maybe<ResourceName> ResourceTableResolver::findName(ResourceId resId) { |
| for (const auto& assetManager : mSources) { |
| const android::ResTable& table = assetManager->getResources(false); |
| |
| android::ResTable::resource_name resourceName; |
| if (!table.getResourceName(resId.id, false, &resourceName)) { |
| continue; |
| } |
| |
| const ResourceType* type = parseResourceType(StringPiece16(resourceName.type, |
| resourceName.typeLen)); |
| assert(type); |
| return ResourceName{ |
| { resourceName.package, resourceName.packageLen }, |
| *type, |
| { resourceName.name, resourceName.nameLen } }; |
| } |
| return {}; |
| } |
| |
| /** |
| * This is called when we need to lookup a resource name in the AssetManager. |
| * Since the values in the AssetManager are not parsed like in a ResourceTable, |
| * we must create Attribute objects here if we find them. |
| */ |
| const ResourceTableResolver::CacheEntry* ResourceTableResolver::buildCacheEntry( |
| const ResourceName& name) { |
| for (const auto& assetManager : mSources) { |
| const android::ResTable& table = assetManager->getResources(false); |
| |
| const StringPiece16 type16 = toString(name.type); |
| ResourceId resId { |
| table.identifierForName( |
| name.entry.data(), name.entry.size(), |
| type16.data(), type16.size(), |
| name.package.data(), name.package.size()) |
| }; |
| |
| if (!resId.isValid()) { |
| continue; |
| } |
| |
| CacheEntry& entry = mCache[name]; |
| entry.id = resId; |
| |
| // |
| // Now check to see if this resource is an Attribute. |
| // |
| |
| const android::ResTable::bag_entry* bagBegin; |
| ssize_t bags = table.lockBag(resId.id, &bagBegin); |
| if (bags < 1) { |
| table.unlockBag(bagBegin); |
| return &entry; |
| } |
| |
| // Look for the ATTR_TYPE key in the bag and check the types it supports. |
| uint32_t attrTypeMask = 0; |
| for (ssize_t i = 0; i < bags; i++) { |
| if (bagBegin[i].map.name.ident == android::ResTable_map::ATTR_TYPE) { |
| attrTypeMask = bagBegin[i].map.value.data; |
| } |
| } |
| |
| entry.attr = util::make_unique<Attribute>(false); |
| |
| if (attrTypeMask & android::ResTable_map::TYPE_ENUM || |
| attrTypeMask & android::ResTable_map::TYPE_FLAGS) { |
| for (ssize_t i = 0; i < bags; i++) { |
| if (Res_INTERNALID(bagBegin[i].map.name.ident)) { |
| // Internal IDs are special keys, which are not enum/flag symbols, so skip. |
| continue; |
| } |
| |
| android::ResTable::resource_name symbolName; |
| bool result = table.getResourceName(bagBegin[i].map.name.ident, false, |
| &symbolName); |
| assert(result); |
| const ResourceType* type = parseResourceType( |
| StringPiece16(symbolName.type, symbolName.typeLen)); |
| assert(type); |
| |
| entry.attr->symbols.push_back(Attribute::Symbol{ |
| Reference(ResourceNameRef( |
| StringPiece16(symbolName.package, symbolName.packageLen), |
| *type, |
| StringPiece16(symbolName.name, symbolName.nameLen))), |
| bagBegin[i].map.value.data |
| }); |
| } |
| } |
| |
| entry.attr->typeMask |= attrTypeMask; |
| table.unlockBag(bagBegin); |
| return &entry; |
| } |
| return nullptr; |
| } |
| |
| } // namespace aapt |