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