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 
192 /**
193  * The default handler for collisions. A return value of -1 means keep the
194  * existing value, 0 means fail, and +1 means take the incoming value.
195  */
resolveValueCollision(Value * existing,Value * incoming)196 int ResourceTable::resolveValueCollision(Value* existing, Value* incoming) {
197     Attribute* existingAttr = valueCast<Attribute>(existing);
198     Attribute* incomingAttr = valueCast<Attribute>(incoming);
199 
200     if (!incomingAttr) {
201         if (incoming->isWeak()) {
202             // We're trying to add a weak resource but a resource
203             // already exists. Keep the existing.
204             return -1;
205         } else if (existing->isWeak()) {
206             // Override the weak resource with the new strong resource.
207             return 1;
208         }
209         // The existing and incoming values are strong, this is an error
210         // if the values are not both attributes.
211         return 0;
212     }
213 
214     if (!existingAttr) {
215         if (existing->isWeak()) {
216             // The existing value is not an attribute and it is weak,
217             // so take the incoming attribute value.
218             return 1;
219         }
220         // The existing value is not an attribute and it is strong,
221         // so the incoming attribute value is an error.
222         return 0;
223     }
224 
225     assert(incomingAttr && existingAttr);
226 
227     //
228     // Attribute specific handling. At this point we know both
229     // values are attributes. Since we can declare and define
230     // attributes all-over, we do special handling to see
231     // which definition sticks.
232     //
233     if (existingAttr->typeMask == incomingAttr->typeMask) {
234         // The two attributes are both DECLs, but they are plain attributes
235         // with the same formats.
236         // Keep the strongest one.
237         return existingAttr->isWeak() ? 1 : -1;
238     }
239 
240     if (existingAttr->isWeak() && existingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
241         // Any incoming attribute is better than this.
242         return 1;
243     }
244 
245     if (incomingAttr->isWeak() && incomingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
246         // The incoming attribute may be a USE instead of a DECL.
247         // Keep the existing attribute.
248         return -1;
249     }
250     return 0;
251 }
252 
253 static constexpr const char16_t* kValidNameChars = u"._-";
254 static constexpr const char16_t* kValidNameMangledChars = u"._-$";
255 
addResource(const ResourceNameRef & name,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)256 bool ResourceTable::addResource(const ResourceNameRef& name,
257                                 const ConfigDescription& config,
258                                 const StringPiece& product,
259                                 std::unique_ptr<Value> value,
260                                 IDiagnostics* diag) {
261     return addResourceImpl(name, {}, config, product, std::move(value), kValidNameChars,
262                            resolveValueCollision, diag);
263 }
264 
addResource(const ResourceNameRef & name,const ResourceId resId,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)265 bool ResourceTable::addResource(const ResourceNameRef& name,
266                                 const ResourceId resId,
267                                 const ConfigDescription& config,
268                                 const StringPiece& product,
269                                 std::unique_ptr<Value> value,
270                                 IDiagnostics* diag) {
271     return addResourceImpl(name, resId, config, product, std::move(value), kValidNameChars,
272                            resolveValueCollision, diag);
273 }
274 
addFileReference(const ResourceNameRef & name,const ConfigDescription & config,const Source & source,const StringPiece16 & path,IDiagnostics * diag)275 bool ResourceTable::addFileReference(const ResourceNameRef& name,
276                                      const ConfigDescription& config,
277                                      const Source& source,
278                                      const StringPiece16& path,
279                                      IDiagnostics* diag) {
280     return addFileReferenceImpl(name, config, source, path, nullptr, kValidNameChars, diag);
281 }
282 
addFileReferenceAllowMangled(const ResourceNameRef & name,const ConfigDescription & config,const Source & source,const StringPiece16 & path,io::IFile * file,IDiagnostics * diag)283 bool ResourceTable::addFileReferenceAllowMangled(const ResourceNameRef& name,
284                                                  const ConfigDescription& config,
285                                                  const Source& source,
286                                                  const StringPiece16& path,
287                                                  io::IFile* file,
288                                                  IDiagnostics* diag) {
289     return addFileReferenceImpl(name, config, source, path, file, kValidNameMangledChars, diag);
290 }
291 
addFileReferenceImpl(const ResourceNameRef & name,const ConfigDescription & config,const Source & source,const StringPiece16 & path,io::IFile * file,const char16_t * validChars,IDiagnostics * diag)292 bool ResourceTable::addFileReferenceImpl(const ResourceNameRef& name,
293                                          const ConfigDescription& config,
294                                          const Source& source,
295                                          const StringPiece16& path,
296                                          io::IFile* file,
297                                          const char16_t* validChars,
298                                          IDiagnostics* diag) {
299     std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
300             stringPool.makeRef(path));
301     fileRef->setSource(source);
302     fileRef->file = file;
303     return addResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef),
304                            kValidNameChars, resolveValueCollision, diag);
305 }
306 
addResourceAllowMangled(const ResourceNameRef & name,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)307 bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
308                                             const ConfigDescription& config,
309                                             const StringPiece& product,
310                                             std::unique_ptr<Value> value,
311                                             IDiagnostics* diag) {
312     return addResourceImpl(name, ResourceId{}, config, product, std::move(value),
313                            kValidNameMangledChars, resolveValueCollision, diag);
314 }
315 
addResourceAllowMangled(const ResourceNameRef & name,const ResourceId id,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)316 bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
317                                             const ResourceId id,
318                                             const ConfigDescription& config,
319                                             const StringPiece& product,
320                                             std::unique_ptr<Value> value,
321                                             IDiagnostics* diag) {
322     return addResourceImpl(name, id, config, product, std::move(value), kValidNameMangledChars,
323                            resolveValueCollision, diag);
324 }
325 
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)326 bool ResourceTable::addResourceImpl(const ResourceNameRef& name,
327                                     const ResourceId resId,
328                                     const ConfigDescription& config,
329                                     const StringPiece& product,
330                                     std::unique_ptr<Value> value,
331                                     const char16_t* validChars,
332                                     std::function<int(Value*,Value*)> conflictResolver,
333                                     IDiagnostics* diag) {
334     assert(value && "value can't be nullptr");
335     assert(diag && "diagnostics can't be nullptr");
336 
337     auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
338     if (badCharIter != name.entry.end()) {
339         diag->error(DiagMessage(value->getSource())
340                     << "resource '"
341                     << name
342                     << "' has invalid entry name '"
343                     << name.entry
344                     << "'. Invalid character '"
345                     << StringPiece16(badCharIter, 1)
346                     << "'");
347         return false;
348     }
349 
350     ResourceTablePackage* package = findOrCreatePackage(name.package);
351     if (resId.isValid() && package->id && package->id.value() != resId.packageId()) {
352         diag->error(DiagMessage(value->getSource())
353                     << "trying to add resource '"
354                     << name
355                     << "' with ID "
356                     << resId
357                     << " but package '"
358                     << package->name
359                     << "' already has ID "
360                     << std::hex << (int) package->id.value() << std::dec);
361         return false;
362     }
363 
364     ResourceTableType* type = package->findOrCreateType(name.type);
365     if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
366         diag->error(DiagMessage(value->getSource())
367                     << "trying to add resource '"
368                     << name
369                     << "' with ID "
370                     << resId
371                     << " but type '"
372                     << type->type
373                     << "' already has ID "
374                     << std::hex << (int) type->id.value() << std::dec);
375         return false;
376     }
377 
378     ResourceEntry* entry = type->findOrCreateEntry(name.entry);
379     if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
380         diag->error(DiagMessage(value->getSource())
381                     << "trying to add resource '"
382                     << name
383                     << "' with ID "
384                     << resId
385                     << " but resource already has ID "
386                     << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
387         return false;
388     }
389 
390     ResourceConfigValue* configValue = entry->findOrCreateValue(config, product);
391     if (!configValue->value) {
392         // Resource does not exist, add it now.
393         configValue->value = std::move(value);
394 
395     } else {
396         int collisionResult = conflictResolver(configValue->value.get(), value.get());
397         if (collisionResult > 0) {
398             // Take the incoming value.
399             configValue->value = std::move(value);
400         } else if (collisionResult == 0) {
401             diag->error(DiagMessage(value->getSource())
402                         << "duplicate value for resource '" << name << "' "
403                         << "with config '" << config << "'");
404             diag->error(DiagMessage(configValue->value->getSource())
405                         << "resource previously defined here");
406             return false;
407         }
408     }
409 
410     if (resId.isValid()) {
411         package->id = resId.packageId();
412         type->id = resId.typeId();
413         entry->id = resId.entryId();
414     }
415     return true;
416 }
417 
setSymbolState(const ResourceNameRef & name,const ResourceId resId,const Symbol & symbol,IDiagnostics * diag)418 bool ResourceTable::setSymbolState(const ResourceNameRef& name, const ResourceId resId,
419                                    const Symbol& symbol, IDiagnostics* diag) {
420     return setSymbolStateImpl(name, resId, symbol, kValidNameChars, diag);
421 }
422 
setSymbolStateAllowMangled(const ResourceNameRef & name,const ResourceId resId,const Symbol & symbol,IDiagnostics * diag)423 bool ResourceTable::setSymbolStateAllowMangled(const ResourceNameRef& name,
424                                                const ResourceId resId,
425                                                const Symbol& symbol, IDiagnostics* diag) {
426     return setSymbolStateImpl(name, resId, symbol, kValidNameMangledChars, diag);
427 }
428 
setSymbolStateImpl(const ResourceNameRef & name,const ResourceId resId,const Symbol & symbol,const char16_t * validChars,IDiagnostics * diag)429 bool ResourceTable::setSymbolStateImpl(const ResourceNameRef& name, const ResourceId resId,
430                                        const Symbol& symbol, const char16_t* validChars,
431                                        IDiagnostics* diag) {
432     assert(diag && "diagnostics can't be nullptr");
433 
434     auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
435     if (badCharIter != name.entry.end()) {
436         diag->error(DiagMessage(symbol.source)
437                     << "resource '"
438                     << name
439                     << "' has invalid entry name '"
440                     << name.entry
441                     << "'. Invalid character '"
442                     << StringPiece16(badCharIter, 1)
443                     << "'");
444         return false;
445     }
446 
447     ResourceTablePackage* package = findOrCreatePackage(name.package);
448     if (resId.isValid() && package->id && package->id.value() != resId.packageId()) {
449         diag->error(DiagMessage(symbol.source)
450                     << "trying to add resource '"
451                     << name
452                     << "' with ID "
453                     << resId
454                     << " but package '"
455                     << package->name
456                     << "' already has ID "
457                     << std::hex << (int) package->id.value() << std::dec);
458         return false;
459     }
460 
461     ResourceTableType* type = package->findOrCreateType(name.type);
462     if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
463         diag->error(DiagMessage(symbol.source)
464                     << "trying to add resource '"
465                     << name
466                     << "' with ID "
467                     << resId
468                     << " but type '"
469                     << type->type
470                     << "' already has ID "
471                     << std::hex << (int) type->id.value() << std::dec);
472         return false;
473     }
474 
475     ResourceEntry* entry = type->findOrCreateEntry(name.entry);
476     if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
477         diag->error(DiagMessage(symbol.source)
478                     << "trying to add resource '"
479                     << name
480                     << "' with ID "
481                     << resId
482                     << " but resource already has ID "
483                     << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
484         return false;
485     }
486 
487     if (resId.isValid()) {
488         package->id = resId.packageId();
489         type->id = resId.typeId();
490         entry->id = resId.entryId();
491     }
492 
493     // Only mark the type state as public, it doesn't care about being private.
494     if (symbol.state == SymbolState::kPublic) {
495         type->symbolStatus.state = SymbolState::kPublic;
496     }
497 
498     if (symbol.state == SymbolState::kUndefined &&
499             entry->symbolStatus.state != SymbolState::kUndefined) {
500         // We can't undefine a symbol (remove its visibility). Ignore.
501         return true;
502     }
503 
504     if (symbol.state == SymbolState::kPrivate &&
505             entry->symbolStatus.state == SymbolState::kPublic) {
506         // We can't downgrade public to private. Ignore.
507         return true;
508     }
509 
510     entry->symbolStatus = std::move(symbol);
511     return true;
512 }
513 
514 Maybe<ResourceTable::SearchResult>
findResource(const ResourceNameRef & name)515 ResourceTable::findResource(const ResourceNameRef& name) {
516     ResourceTablePackage* package = findPackage(name.package);
517     if (!package) {
518         return {};
519     }
520 
521     ResourceTableType* type = package->findType(name.type);
522     if (!type) {
523         return {};
524     }
525 
526     ResourceEntry* entry = type->findEntry(name.entry);
527     if (!entry) {
528         return {};
529     }
530     return SearchResult{ package, type, entry };
531 }
532 
533 } // namespace aapt
534