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 "Maybe.h" 18#include "NameMangler.h" 19#include "Resource.h" 20#include "ResourceTable.h" 21#include "ResourceTableResolver.h" 22#include "ResourceValues.h" 23#include "Util.h" 24 25#include <androidfw/AssetManager.h> 26#include <androidfw/ResourceTypes.h> 27#include <memory> 28#include <vector> 29 30namespace aapt { 31 32ResourceTableResolver::ResourceTableResolver( 33 std::shared_ptr<const ResourceTable> table, 34 const std::vector<std::shared_ptr<const android::AssetManager>>& sources) : 35 mTable(table), mSources(sources) { 36 for (const auto& assetManager : mSources) { 37 const android::ResTable& resTable = assetManager->getResources(false); 38 const size_t packageCount = resTable.getBasePackageCount(); 39 for (size_t i = 0; i < packageCount; i++) { 40 std::u16string packageName = resTable.getBasePackageName(i).string(); 41 mIncludedPackages.insert(std::move(packageName)); 42 } 43 } 44} 45 46Maybe<ResourceId> ResourceTableResolver::findId(const ResourceName& name) { 47 Maybe<Entry> result = findAttribute(name); 48 if (result) { 49 return result.value().id; 50 } 51 return {}; 52} 53 54Maybe<IResolver::Entry> ResourceTableResolver::findAttribute(const ResourceName& name) { 55 auto cacheIter = mCache.find(name); 56 if (cacheIter != std::end(mCache)) { 57 return Entry{ cacheIter->second.id, cacheIter->second.attr.get() }; 58 } 59 60 ResourceName mangledName; 61 const ResourceName* nameToSearch = &name; 62 if (name.package != mTable->getPackage()) { 63 // This may be a reference to an included resource or 64 // to a mangled resource. 65 if (mIncludedPackages.find(name.package) == mIncludedPackages.end()) { 66 // This is not in our included set, so mangle the name and 67 // check for that. 68 mangledName.entry = name.entry; 69 NameMangler::mangle(name.package, &mangledName.entry); 70 mangledName.package = mTable->getPackage(); 71 mangledName.type = name.type; 72 nameToSearch = &mangledName; 73 } else { 74 const CacheEntry* cacheEntry = buildCacheEntry(name); 75 if (cacheEntry) { 76 return Entry{ cacheEntry->id, cacheEntry->attr.get() }; 77 } 78 return {}; 79 } 80 } 81 82 const ResourceTableType* type; 83 const ResourceEntry* entry; 84 std::tie(type, entry) = mTable->findResource(*nameToSearch); 85 if (type && entry) { 86 Entry result = {}; 87 if (mTable->getPackageId() != ResourceTable::kUnsetPackageId && 88 type->typeId != ResourceTableType::kUnsetTypeId && 89 entry->entryId != ResourceEntry::kUnsetEntryId) { 90 result.id = ResourceId(mTable->getPackageId(), type->typeId, entry->entryId); 91 } 92 93 if (!entry->values.empty()) { 94 visitFunc<Attribute>(*entry->values.front().value, [&result](Attribute& attr) { 95 result.attr = &attr; 96 }); 97 } 98 return result; 99 } 100 return {}; 101} 102 103Maybe<ResourceName> ResourceTableResolver::findName(ResourceId resId) { 104 for (const auto& assetManager : mSources) { 105 const android::ResTable& table = assetManager->getResources(false); 106 107 android::ResTable::resource_name resourceName; 108 if (!table.getResourceName(resId.id, false, &resourceName)) { 109 continue; 110 } 111 112 const ResourceType* type = parseResourceType(StringPiece16(resourceName.type, 113 resourceName.typeLen)); 114 assert(type); 115 return ResourceName{ 116 { resourceName.package, resourceName.packageLen }, 117 *type, 118 { resourceName.name, resourceName.nameLen } }; 119 } 120 return {}; 121} 122 123/** 124 * This is called when we need to lookup a resource name in the AssetManager. 125 * Since the values in the AssetManager are not parsed like in a ResourceTable, 126 * we must create Attribute objects here if we find them. 127 */ 128const ResourceTableResolver::CacheEntry* ResourceTableResolver::buildCacheEntry( 129 const ResourceName& name) { 130 for (const auto& assetManager : mSources) { 131 const android::ResTable& table = assetManager->getResources(false); 132 133 const StringPiece16 type16 = toString(name.type); 134 ResourceId resId { 135 table.identifierForName( 136 name.entry.data(), name.entry.size(), 137 type16.data(), type16.size(), 138 name.package.data(), name.package.size()) 139 }; 140 141 if (!resId.isValid()) { 142 continue; 143 } 144 145 CacheEntry& entry = mCache[name]; 146 entry.id = resId; 147 148 // 149 // Now check to see if this resource is an Attribute. 150 // 151 152 const android::ResTable::bag_entry* bagBegin; 153 ssize_t bags = table.lockBag(resId.id, &bagBegin); 154 if (bags < 1) { 155 table.unlockBag(bagBegin); 156 return &entry; 157 } 158 159 // Look for the ATTR_TYPE key in the bag and check the types it supports. 160 uint32_t attrTypeMask = 0; 161 for (ssize_t i = 0; i < bags; i++) { 162 if (bagBegin[i].map.name.ident == android::ResTable_map::ATTR_TYPE) { 163 attrTypeMask = bagBegin[i].map.value.data; 164 } 165 } 166 167 entry.attr = util::make_unique<Attribute>(false); 168 169 if (attrTypeMask & android::ResTable_map::TYPE_ENUM || 170 attrTypeMask & android::ResTable_map::TYPE_FLAGS) { 171 for (ssize_t i = 0; i < bags; i++) { 172 if (Res_INTERNALID(bagBegin[i].map.name.ident)) { 173 // Internal IDs are special keys, which are not enum/flag symbols, so skip. 174 continue; 175 } 176 177 android::ResTable::resource_name symbolName; 178 bool result = table.getResourceName(bagBegin[i].map.name.ident, false, 179 &symbolName); 180 assert(result); 181 const ResourceType* type = parseResourceType( 182 StringPiece16(symbolName.type, symbolName.typeLen)); 183 assert(type); 184 185 entry.attr->symbols.push_back(Attribute::Symbol{ 186 Reference(ResourceNameRef( 187 StringPiece16(symbolName.package, symbolName.packageLen), 188 *type, 189 StringPiece16(symbolName.name, symbolName.nameLen))), 190 bagBegin[i].map.value.data 191 }); 192 } 193 } 194 195 entry.attr->typeMask |= attrTypeMask; 196 table.unlockBag(bagBegin); 197 return &entry; 198 } 199 return nullptr; 200} 201 202} // namespace aapt 203