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