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