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
30namespace aapt {
31
32static bool lessThanType(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) {
33    return lhs->type < rhs;
34}
35
36template <typename T>
37static 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
42ResourceTablePackage* 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
52ResourceTablePackage* 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
61ResourceTablePackage* 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
74ResourceTablePackage* 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
87ResourceTableType* 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
96ResourceTableType* 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
105ResourceEntry* 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
115ResourceEntry* 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
125ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config) {
126    return findValue(config, StringPiece());
127}
128
129struct ConfigKey {
130    const ConfigDescription* config;
131    const StringPiece& product;
132};
133
134bool 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
142ResourceConfigValue* 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
155ResourceConfigValue* 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
170std::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
192std::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 */
207int 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
264static constexpr const char16_t* kValidNameChars = u"._-";
265static constexpr const char16_t* kValidNameMangledChars = u"._-$";
266
267bool 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
276bool 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
286bool 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
294bool 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
303bool 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
318bool 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
327bool 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
337bool 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
429bool ResourceTable::setSymbolState(const ResourceNameRef& name, const ResourceId resId,
430                                   const Symbol& symbol, IDiagnostics* diag) {
431    return setSymbolStateImpl(name, resId, symbol, kValidNameChars, diag);
432}
433
434bool 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
440bool 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
525Maybe<ResourceTable::SearchResult>
526ResourceTable::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