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 
30 namespace aapt {
31 
ResourceTableResolver(std::shared_ptr<const ResourceTable> table,const std::vector<std::shared_ptr<const android::AssetManager>> & sources)32 ResourceTableResolver::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 
findId(const ResourceName & name)46 Maybe<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 
findAttribute(const ResourceName & name)54 Maybe<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 
findName(ResourceId resId)103 Maybe<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  */
buildCacheEntry(const ResourceName & name)128 const 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