Home | History | Annotate | Download | only in aapt2
      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 
     32 static bool lessThanType(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) {
     33     return lhs->type < rhs;
     34 }
     35 
     36 template <typename T>
     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 
     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 
     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 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 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  */
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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>
    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