• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "ConfigDescription.h"
18 #include "NameMangler.h"
19 #include "ResourceTable.h"
20 #include "ResourceValues.h"
21 #include "ValueVisitor.h"
22 #include "util/Util.h"
23 
24 #include <algorithm>
25 #include <androidfw/ResourceTypes.h>
26 #include <memory>
27 #include <string>
28 #include <tuple>
29 
30 namespace aapt {
31 
lessThanType(const std::unique_ptr<ResourceTableType> & lhs,ResourceType rhs)32 static bool lessThanType(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) {
33     return lhs->type < rhs;
34 }
35 
36 template <typename T>
lessThanStructWithName(const std::unique_ptr<T> & lhs,const StringPiece16 & rhs)37 static bool lessThanStructWithName(const std::unique_ptr<T>& lhs,
38                                    const StringPiece16& rhs) {
39     return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
40 }
41 
findPackage(const StringPiece16 & name)42 ResourceTablePackage* ResourceTable::findPackage(const StringPiece16& name) {
43     const auto last = packages.end();
44     auto iter = std::lower_bound(packages.begin(), last, name,
45                                  lessThanStructWithName<ResourceTablePackage>);
46     if (iter != last && name == (*iter)->name) {
47         return iter->get();
48     }
49     return nullptr;
50 }
51 
findPackageById(uint8_t id)52 ResourceTablePackage* ResourceTable::findPackageById(uint8_t id) {
53     for (auto& package : packages) {
54         if (package->id && package->id.value() == id) {
55             return package.get();
56         }
57     }
58     return nullptr;
59 }
60 
createPackage(const StringPiece16 & name,Maybe<uint8_t> id)61 ResourceTablePackage* ResourceTable::createPackage(const StringPiece16& name, Maybe<uint8_t> id) {
62     ResourceTablePackage* package = findOrCreatePackage(name);
63     if (id && !package->id) {
64         package->id = id;
65         return package;
66     }
67 
68     if (id && package->id && package->id.value() != id.value()) {
69         return nullptr;
70     }
71     return package;
72 }
73 
findOrCreatePackage(const StringPiece16 & name)74 ResourceTablePackage* ResourceTable::findOrCreatePackage(const StringPiece16& name) {
75     const auto last = packages.end();
76     auto iter = std::lower_bound(packages.begin(), last, name,
77                                  lessThanStructWithName<ResourceTablePackage>);
78     if (iter != last && name == (*iter)->name) {
79         return iter->get();
80     }
81 
82     std::unique_ptr<ResourceTablePackage> newPackage = util::make_unique<ResourceTablePackage>();
83     newPackage->name = name.toString();
84     return packages.emplace(iter, std::move(newPackage))->get();
85 }
86 
findType(ResourceType type)87 ResourceTableType* ResourceTablePackage::findType(ResourceType type) {
88     const auto last = types.end();
89     auto iter = std::lower_bound(types.begin(), last, type, lessThanType);
90     if (iter != last && (*iter)->type == type) {
91         return iter->get();
92     }
93     return nullptr;
94 }
95 
findOrCreateType(ResourceType type)96 ResourceTableType* ResourceTablePackage::findOrCreateType(ResourceType type) {
97     const auto last = types.end();
98     auto iter = std::lower_bound(types.begin(), last, type, lessThanType);
99     if (iter != last && (*iter)->type == type) {
100         return iter->get();
101     }
102     return types.emplace(iter, new ResourceTableType(type))->get();
103 }
104 
findEntry(const StringPiece16 & name)105 ResourceEntry* ResourceTableType::findEntry(const StringPiece16& name) {
106     const auto last = entries.end();
107     auto iter = std::lower_bound(entries.begin(), last, name,
108                                  lessThanStructWithName<ResourceEntry>);
109     if (iter != last && name == (*iter)->name) {
110         return iter->get();
111     }
112     return nullptr;
113 }
114 
findOrCreateEntry(const StringPiece16 & name)115 ResourceEntry* ResourceTableType::findOrCreateEntry(const StringPiece16& name) {
116     auto last = entries.end();
117     auto iter = std::lower_bound(entries.begin(), last, name,
118                                  lessThanStructWithName<ResourceEntry>);
119     if (iter != last && name == (*iter)->name) {
120         return iter->get();
121     }
122     return entries.emplace(iter, new ResourceEntry(name))->get();
123 }
124 
findValue(const ConfigDescription & config)125 ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config) {
126     return findValue(config, StringPiece());
127 }
128 
129 struct ConfigKey {
130     const ConfigDescription* config;
131     const StringPiece& product;
132 };
133 
ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue> & lhs,const ConfigKey & rhs)134 bool ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
135     int cmp = lhs->config.compare(*rhs.config);
136     if (cmp == 0) {
137         cmp = StringPiece(lhs->product).compare(rhs.product);
138     }
139     return cmp < 0;
140 }
141 
findValue(const ConfigDescription & config,const StringPiece & product)142 ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config,
143                                               const StringPiece& product) {
144     auto iter = std::lower_bound(values.begin(), values.end(),
145                                  ConfigKey{ &config, product }, ltConfigKeyRef);
146     if (iter != values.end()) {
147         ResourceConfigValue* value = iter->get();
148         if (value->config == config && StringPiece(value->product) == product) {
149             return value;
150         }
151     }
152     return nullptr;
153 }
154 
findOrCreateValue(const ConfigDescription & config,const StringPiece & product)155 ResourceConfigValue* ResourceEntry::findOrCreateValue(const ConfigDescription& config,
156                                                       const StringPiece& product) {
157     auto iter = std::lower_bound(values.begin(), values.end(),
158                                  ConfigKey{ &config, product }, ltConfigKeyRef);
159     if (iter != values.end()) {
160         ResourceConfigValue* value = iter->get();
161         if (value->config == config && StringPiece(value->product) == product) {
162             return value;
163         }
164     }
165     ResourceConfigValue* newValue = values.insert(
166             iter, util::make_unique<ResourceConfigValue>(config, product))->get();
167     return newValue;
168 }
169 
findAllValues(const ConfigDescription & config)170 std::vector<ResourceConfigValue*> ResourceEntry::findAllValues(const ConfigDescription& config) {
171     std::vector<ResourceConfigValue*> results;
172 
173     auto iter = values.begin();
174     for (; iter != values.end(); ++iter) {
175         ResourceConfigValue* value = iter->get();
176         if (value->config == config) {
177             results.push_back(value);
178             ++iter;
179             break;
180         }
181     }
182 
183     for (; iter != values.end(); ++iter) {
184         ResourceConfigValue* value = iter->get();
185         if (value->config == config) {
186             results.push_back(value);
187         }
188     }
189     return results;
190 }
191 
findValuesIf(const std::function<bool (ResourceConfigValue *)> & f)192 std::vector<ResourceConfigValue*> ResourceEntry::findValuesIf(
193         const std::function<bool(ResourceConfigValue*)>& f) {
194     std::vector<ResourceConfigValue*> results;
195     for (auto& configValue : values) {
196         if (f(configValue.get())) {
197             results.push_back(configValue.get());
198         }
199     }
200     return results;
201 }
202 
203 /**
204  * The default handler for collisions. A return value of -1 means keep the
205  * existing value, 0 means fail, and +1 means take the incoming value.
206  */
resolveValueCollision(Value * existing,Value * incoming)207 int ResourceTable::resolveValueCollision(Value* existing, Value* incoming) {
208     Attribute* existingAttr = valueCast<Attribute>(existing);
209     Attribute* incomingAttr = valueCast<Attribute>(incoming);
210 
211     if (!incomingAttr) {
212         if (incoming->isWeak()) {
213             // We're trying to add a weak resource but a resource
214             // already exists. Keep the existing.
215             return -1;
216         } else if (existing->isWeak()) {
217             // Override the weak resource with the new strong resource.
218             return 1;
219         }
220         // The existing and incoming values are strong, this is an error
221         // if the values are not both attributes.
222         return 0;
223     }
224 
225     if (!existingAttr) {
226         if (existing->isWeak()) {
227             // The existing value is not an attribute and it is weak,
228             // so take the incoming attribute value.
229             return 1;
230         }
231         // The existing value is not an attribute and it is strong,
232         // so the incoming attribute value is an error.
233         return 0;
234     }
235 
236     assert(incomingAttr && existingAttr);
237 
238     //
239     // Attribute specific handling. At this point we know both
240     // values are attributes. Since we can declare and define
241     // attributes all-over, we do special handling to see
242     // which definition sticks.
243     //
244     if (existingAttr->typeMask == incomingAttr->typeMask) {
245         // The two attributes are both DECLs, but they are plain attributes
246         // with the same formats.
247         // Keep the strongest one.
248         return existingAttr->isWeak() ? 1 : -1;
249     }
250 
251     if (existingAttr->isWeak() && existingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
252         // Any incoming attribute is better than this.
253         return 1;
254     }
255 
256     if (incomingAttr->isWeak() && incomingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
257         // The incoming attribute may be a USE instead of a DECL.
258         // Keep the existing attribute.
259         return -1;
260     }
261     return 0;
262 }
263 
264 static constexpr const char16_t* kValidNameChars = u"._-";
265 static constexpr const char16_t* kValidNameMangledChars = u"._-$";
266 
addResource(const ResourceNameRef & name,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)267 bool ResourceTable::addResource(const ResourceNameRef& name,
268                                 const ConfigDescription& config,
269                                 const StringPiece& product,
270                                 std::unique_ptr<Value> value,
271                                 IDiagnostics* diag) {
272     return addResourceImpl(name, {}, config, product, std::move(value), kValidNameChars,
273                            resolveValueCollision, diag);
274 }
275 
addResource(const ResourceNameRef & name,const ResourceId resId,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)276 bool ResourceTable::addResource(const ResourceNameRef& name,
277                                 const ResourceId resId,
278                                 const ConfigDescription& config,
279                                 const StringPiece& product,
280                                 std::unique_ptr<Value> value,
281                                 IDiagnostics* diag) {
282     return addResourceImpl(name, resId, config, product, std::move(value), kValidNameChars,
283                            resolveValueCollision, diag);
284 }
285 
addFileReference(const ResourceNameRef & name,const ConfigDescription & config,const Source & source,const StringPiece16 & path,IDiagnostics * diag)286 bool ResourceTable::addFileReference(const ResourceNameRef& name,
287                                      const ConfigDescription& config,
288                                      const Source& source,
289                                      const StringPiece16& path,
290                                      IDiagnostics* diag) {
291     return addFileReferenceImpl(name, config, source, path, nullptr, kValidNameChars, diag);
292 }
293 
addFileReferenceAllowMangled(const ResourceNameRef & name,const ConfigDescription & config,const Source & source,const StringPiece16 & path,io::IFile * file,IDiagnostics * diag)294 bool ResourceTable::addFileReferenceAllowMangled(const ResourceNameRef& name,
295                                                  const ConfigDescription& config,
296                                                  const Source& source,
297                                                  const StringPiece16& path,
298                                                  io::IFile* file,
299                                                  IDiagnostics* diag) {
300     return addFileReferenceImpl(name, config, source, path, file, kValidNameMangledChars, diag);
301 }
302 
addFileReferenceImpl(const ResourceNameRef & name,const ConfigDescription & config,const Source & source,const StringPiece16 & path,io::IFile * file,const char16_t * validChars,IDiagnostics * diag)303 bool ResourceTable::addFileReferenceImpl(const ResourceNameRef& name,
304                                          const ConfigDescription& config,
305                                          const Source& source,
306                                          const StringPiece16& path,
307                                          io::IFile* file,
308                                          const char16_t* validChars,
309                                          IDiagnostics* diag) {
310     std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
311             stringPool.makeRef(path));
312     fileRef->setSource(source);
313     fileRef->file = file;
314     return addResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef),
315                            kValidNameChars, resolveValueCollision, diag);
316 }
317 
addResourceAllowMangled(const ResourceNameRef & name,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)318 bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
319                                             const ConfigDescription& config,
320                                             const StringPiece& product,
321                                             std::unique_ptr<Value> value,
322                                             IDiagnostics* diag) {
323     return addResourceImpl(name, ResourceId{}, config, product, std::move(value),
324                            kValidNameMangledChars, resolveValueCollision, diag);
325 }
326 
addResourceAllowMangled(const ResourceNameRef & name,const ResourceId id,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)327 bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
328                                             const ResourceId id,
329                                             const ConfigDescription& config,
330                                             const StringPiece& product,
331                                             std::unique_ptr<Value> value,
332                                             IDiagnostics* diag) {
333     return addResourceImpl(name, id, config, product, std::move(value), kValidNameMangledChars,
334                            resolveValueCollision, diag);
335 }
336 
addResourceImpl(const ResourceNameRef & name,const ResourceId resId,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,const char16_t * validChars,std::function<int (Value *,Value *)> conflictResolver,IDiagnostics * diag)337 bool ResourceTable::addResourceImpl(const ResourceNameRef& name,
338                                     const ResourceId resId,
339                                     const ConfigDescription& config,
340                                     const StringPiece& product,
341                                     std::unique_ptr<Value> value,
342                                     const char16_t* validChars,
343                                     std::function<int(Value*,Value*)> conflictResolver,
344                                     IDiagnostics* diag) {
345     assert(value && "value can't be nullptr");
346     assert(diag && "diagnostics can't be nullptr");
347 
348     auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
349     if (badCharIter != name.entry.end()) {
350         diag->error(DiagMessage(value->getSource())
351                     << "resource '"
352                     << name
353                     << "' has invalid entry name '"
354                     << name.entry
355                     << "'. Invalid character '"
356                     << StringPiece16(badCharIter, 1)
357                     << "'");
358         return false;
359     }
360 
361     ResourceTablePackage* package = findOrCreatePackage(name.package);
362     if (resId.isValid() && package->id && package->id.value() != resId.packageId()) {
363         diag->error(DiagMessage(value->getSource())
364                     << "trying to add resource '"
365                     << name
366                     << "' with ID "
367                     << resId
368                     << " but package '"
369                     << package->name
370                     << "' already has ID "
371                     << std::hex << (int) package->id.value() << std::dec);
372         return false;
373     }
374 
375     ResourceTableType* type = package->findOrCreateType(name.type);
376     if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
377         diag->error(DiagMessage(value->getSource())
378                     << "trying to add resource '"
379                     << name
380                     << "' with ID "
381                     << resId
382                     << " but type '"
383                     << type->type
384                     << "' already has ID "
385                     << std::hex << (int) type->id.value() << std::dec);
386         return false;
387     }
388 
389     ResourceEntry* entry = type->findOrCreateEntry(name.entry);
390     if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
391         diag->error(DiagMessage(value->getSource())
392                     << "trying to add resource '"
393                     << name
394                     << "' with ID "
395                     << resId
396                     << " but resource already has ID "
397                     << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
398         return false;
399     }
400 
401     ResourceConfigValue* configValue = entry->findOrCreateValue(config, product);
402     if (!configValue->value) {
403         // Resource does not exist, add it now.
404         configValue->value = std::move(value);
405 
406     } else {
407         int collisionResult = conflictResolver(configValue->value.get(), value.get());
408         if (collisionResult > 0) {
409             // Take the incoming value.
410             configValue->value = std::move(value);
411         } else if (collisionResult == 0) {
412             diag->error(DiagMessage(value->getSource())
413                         << "duplicate value for resource '" << name << "' "
414                         << "with config '" << config << "'");
415             diag->error(DiagMessage(configValue->value->getSource())
416                         << "resource previously defined here");
417             return false;
418         }
419     }
420 
421     if (resId.isValid()) {
422         package->id = resId.packageId();
423         type->id = resId.typeId();
424         entry->id = resId.entryId();
425     }
426     return true;
427 }
428 
setSymbolState(const ResourceNameRef & name,const ResourceId resId,const Symbol & symbol,IDiagnostics * diag)429 bool ResourceTable::setSymbolState(const ResourceNameRef& name, const ResourceId resId,
430                                    const Symbol& symbol, IDiagnostics* diag) {
431     return setSymbolStateImpl(name, resId, symbol, kValidNameChars, diag);
432 }
433 
setSymbolStateAllowMangled(const ResourceNameRef & name,const ResourceId resId,const Symbol & symbol,IDiagnostics * diag)434 bool ResourceTable::setSymbolStateAllowMangled(const ResourceNameRef& name,
435                                                const ResourceId resId,
436                                                const Symbol& symbol, IDiagnostics* diag) {
437     return setSymbolStateImpl(name, resId, symbol, kValidNameMangledChars, diag);
438 }
439 
setSymbolStateImpl(const ResourceNameRef & name,const ResourceId resId,const Symbol & symbol,const char16_t * validChars,IDiagnostics * diag)440 bool ResourceTable::setSymbolStateImpl(const ResourceNameRef& name, const ResourceId resId,
441                                        const Symbol& symbol, const char16_t* validChars,
442                                        IDiagnostics* diag) {
443     assert(diag && "diagnostics can't be nullptr");
444 
445     auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
446     if (badCharIter != name.entry.end()) {
447         diag->error(DiagMessage(symbol.source)
448                     << "resource '"
449                     << name
450                     << "' has invalid entry name '"
451                     << name.entry
452                     << "'. Invalid character '"
453                     << StringPiece16(badCharIter, 1)
454                     << "'");
455         return false;
456     }
457 
458     ResourceTablePackage* package = findOrCreatePackage(name.package);
459     if (resId.isValid() && package->id && package->id.value() != resId.packageId()) {
460         diag->error(DiagMessage(symbol.source)
461                     << "trying to add resource '"
462                     << name
463                     << "' with ID "
464                     << resId
465                     << " but package '"
466                     << package->name
467                     << "' already has ID "
468                     << std::hex << (int) package->id.value() << std::dec);
469         return false;
470     }
471 
472     ResourceTableType* type = package->findOrCreateType(name.type);
473     if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
474         diag->error(DiagMessage(symbol.source)
475                     << "trying to add resource '"
476                     << name
477                     << "' with ID "
478                     << resId
479                     << " but type '"
480                     << type->type
481                     << "' already has ID "
482                     << std::hex << (int) type->id.value() << std::dec);
483         return false;
484     }
485 
486     ResourceEntry* entry = type->findOrCreateEntry(name.entry);
487     if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
488         diag->error(DiagMessage(symbol.source)
489                     << "trying to add resource '"
490                     << name
491                     << "' with ID "
492                     << resId
493                     << " but resource already has ID "
494                     << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
495         return false;
496     }
497 
498     if (resId.isValid()) {
499         package->id = resId.packageId();
500         type->id = resId.typeId();
501         entry->id = resId.entryId();
502     }
503 
504     // Only mark the type state as public, it doesn't care about being private.
505     if (symbol.state == SymbolState::kPublic) {
506         type->symbolStatus.state = SymbolState::kPublic;
507     }
508 
509     if (symbol.state == SymbolState::kUndefined &&
510             entry->symbolStatus.state != SymbolState::kUndefined) {
511         // We can't undefine a symbol (remove its visibility). Ignore.
512         return true;
513     }
514 
515     if (symbol.state == SymbolState::kPrivate &&
516             entry->symbolStatus.state == SymbolState::kPublic) {
517         // We can't downgrade public to private. Ignore.
518         return true;
519     }
520 
521     entry->symbolStatus = std::move(symbol);
522     return true;
523 }
524 
525 Maybe<ResourceTable::SearchResult>
findResource(const ResourceNameRef & name)526 ResourceTable::findResource(const ResourceNameRef& name) {
527     ResourceTablePackage* package = findPackage(name.package);
528     if (!package) {
529         return {};
530     }
531 
532     ResourceTableType* type = package->findType(name.type);
533     if (!type) {
534         return {};
535     }
536 
537     ResourceEntry* entry = type->findEntry(name.entry);
538     if (!entry) {
539         return {};
540     }
541     return SearchResult{ package, type, entry };
542 }
543 
544 } // namespace aapt
545