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 "Logger.h" 19 #include "NameMangler.h" 20 #include "ResourceTable.h" 21 #include "ResourceValues.h" 22 #include "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 compareConfigs(const ResourceConfigValue& lhs, const ConfigDescription& rhs) { 33 return lhs.config < rhs; 34 } 35 36 static bool lessThanType(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) { 37 return lhs->type < rhs; 38 } 39 40 static bool lessThanEntry(const std::unique_ptr<ResourceEntry>& lhs, const StringPiece16& rhs) { 41 return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0; 42 } 43 44 ResourceTable::ResourceTable() : mPackageId(kUnsetPackageId) { 45 // Make sure attrs always have type ID 1. 46 findOrCreateType(ResourceType::kAttr)->typeId = 1; 47 } 48 49 std::unique_ptr<ResourceTableType>& ResourceTable::findOrCreateType(ResourceType type) { 50 auto last = mTypes.end(); 51 auto iter = std::lower_bound(mTypes.begin(), last, type, lessThanType); 52 if (iter != last) { 53 if ((*iter)->type == type) { 54 return *iter; 55 } 56 } 57 return *mTypes.emplace(iter, new ResourceTableType{ type }); 58 } 59 60 std::unique_ptr<ResourceEntry>& ResourceTable::findOrCreateEntry( 61 std::unique_ptr<ResourceTableType>& type, const StringPiece16& name) { 62 auto last = type->entries.end(); 63 auto iter = std::lower_bound(type->entries.begin(), last, name, lessThanEntry); 64 if (iter != last) { 65 if (name == (*iter)->name) { 66 return *iter; 67 } 68 } 69 return *type->entries.emplace(iter, new ResourceEntry{ name }); 70 } 71 72 struct IsAttributeVisitor : ConstValueVisitor { 73 bool isAttribute = false; 74 75 void visit(const Attribute&, ValueVisitorArgs&) override { 76 isAttribute = true; 77 } 78 79 operator bool() { 80 return isAttribute; 81 } 82 }; 83 84 /** 85 * The default handler for collisions. A return value of -1 means keep the 86 * existing value, 0 means fail, and +1 means take the incoming value. 87 */ 88 static int defaultCollisionHandler(const Value& existing, const Value& incoming) { 89 IsAttributeVisitor existingIsAttr, incomingIsAttr; 90 existing.accept(existingIsAttr, {}); 91 incoming.accept(incomingIsAttr, {}); 92 93 if (!incomingIsAttr) { 94 if (incoming.isWeak()) { 95 // We're trying to add a weak resource but a resource 96 // already exists. Keep the existing. 97 return -1; 98 } else if (existing.isWeak()) { 99 // Override the weak resource with the new strong resource. 100 return 1; 101 } 102 // The existing and incoming values are strong, this is an error 103 // if the values are not both attributes. 104 return 0; 105 } 106 107 if (!existingIsAttr) { 108 if (existing.isWeak()) { 109 // The existing value is not an attribute and it is weak, 110 // so take the incoming attribute value. 111 return 1; 112 } 113 // The existing value is not an attribute and it is strong, 114 // so the incoming attribute value is an error. 115 return 0; 116 } 117 118 // 119 // Attribute specific handling. At this point we know both 120 // values are attributes. Since we can declare and define 121 // attributes all-over, we do special handling to see 122 // which definition sticks. 123 // 124 const Attribute& existingAttr = static_cast<const Attribute&>(existing); 125 const Attribute& incomingAttr = static_cast<const Attribute&>(incoming); 126 if (existingAttr.typeMask == incomingAttr.typeMask) { 127 // The two attributes are both DECLs, but they are plain attributes 128 // with the same formats. 129 // Keep the strongest one. 130 return existingAttr.isWeak() ? 1 : -1; 131 } 132 133 if (existingAttr.isWeak() && existingAttr.typeMask == android::ResTable_map::TYPE_ANY) { 134 // Any incoming attribute is better than this. 135 return 1; 136 } 137 138 if (incomingAttr.isWeak() && incomingAttr.typeMask == android::ResTable_map::TYPE_ANY) { 139 // The incoming attribute may be a USE instead of a DECL. 140 // Keep the existing attribute. 141 return -1; 142 } 143 return 0; 144 } 145 146 static constexpr const char16_t* kValidNameChars = u"._-"; 147 static constexpr const char16_t* kValidNameMangledChars = u"._-$"; 148 149 bool ResourceTable::addResource(const ResourceNameRef& name, const ConfigDescription& config, 150 const SourceLine& source, std::unique_ptr<Value> value) { 151 return addResourceImpl(name, ResourceId{}, config, source, std::move(value), kValidNameChars); 152 } 153 154 bool ResourceTable::addResource(const ResourceNameRef& name, const ResourceId resId, 155 const ConfigDescription& config, const SourceLine& source, 156 std::unique_ptr<Value> value) { 157 return addResourceImpl(name, resId, config, source, std::move(value), kValidNameChars); 158 } 159 160 bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name, 161 const ConfigDescription& config, 162 const SourceLine& source, 163 std::unique_ptr<Value> value) { 164 return addResourceImpl(name, ResourceId{}, config, source, std::move(value), 165 kValidNameMangledChars); 166 } 167 168 bool ResourceTable::addResourceImpl(const ResourceNameRef& name, const ResourceId resId, 169 const ConfigDescription& config, const SourceLine& source, 170 std::unique_ptr<Value> value, const char16_t* validChars) { 171 if (!name.package.empty() && name.package != mPackage) { 172 Logger::error(source) 173 << "resource '" 174 << name 175 << "' has incompatible package. Must be '" 176 << mPackage 177 << "'." 178 << std::endl; 179 return false; 180 } 181 182 auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars); 183 if (badCharIter != name.entry.end()) { 184 Logger::error(source) 185 << "resource '" 186 << name 187 << "' has invalid entry name '" 188 << name.entry 189 << "'. Invalid character '" 190 << StringPiece16(badCharIter, 1) 191 << "'." 192 << std::endl; 193 return false; 194 } 195 196 std::unique_ptr<ResourceTableType>& type = findOrCreateType(name.type); 197 if (resId.isValid() && type->typeId != ResourceTableType::kUnsetTypeId && 198 type->typeId != resId.typeId()) { 199 Logger::error(source) 200 << "trying to add resource '" 201 << name 202 << "' with ID " 203 << resId 204 << " but type '" 205 << type->type 206 << "' already has ID " 207 << std::hex << type->typeId << std::dec 208 << "." 209 << std::endl; 210 return false; 211 } 212 213 std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, name.entry); 214 if (resId.isValid() && entry->entryId != ResourceEntry::kUnsetEntryId && 215 entry->entryId != resId.entryId()) { 216 Logger::error(source) 217 << "trying to add resource '" 218 << name 219 << "' with ID " 220 << resId 221 << " but resource already has ID " 222 << ResourceId(mPackageId, type->typeId, entry->entryId) 223 << "." 224 << std::endl; 225 return false; 226 } 227 228 const auto endIter = std::end(entry->values); 229 auto iter = std::lower_bound(std::begin(entry->values), endIter, config, compareConfigs); 230 if (iter == endIter || iter->config != config) { 231 // This resource did not exist before, add it. 232 entry->values.insert(iter, ResourceConfigValue{ config, source, {}, std::move(value) }); 233 } else { 234 int collisionResult = defaultCollisionHandler(*iter->value, *value); 235 if (collisionResult > 0) { 236 // Take the incoming value. 237 *iter = ResourceConfigValue{ config, source, {}, std::move(value) }; 238 } else if (collisionResult == 0) { 239 Logger::error(source) 240 << "duplicate value for resource '" << name << "' " 241 << "with config '" << iter->config << "'." 242 << std::endl; 243 244 Logger::error(iter->source) 245 << "resource previously defined here." 246 << std::endl; 247 return false; 248 } 249 } 250 251 if (resId.isValid()) { 252 type->typeId = resId.typeId(); 253 entry->entryId = resId.entryId(); 254 } 255 return true; 256 } 257 258 bool ResourceTable::markPublic(const ResourceNameRef& name, const ResourceId resId, 259 const SourceLine& source) { 260 return markPublicImpl(name, resId, source, kValidNameChars); 261 } 262 263 bool ResourceTable::markPublicAllowMangled(const ResourceNameRef& name, const ResourceId resId, 264 const SourceLine& source) { 265 return markPublicImpl(name, resId, source, kValidNameMangledChars); 266 } 267 268 bool ResourceTable::markPublicImpl(const ResourceNameRef& name, const ResourceId resId, 269 const SourceLine& source, const char16_t* validChars) { 270 if (!name.package.empty() && name.package != mPackage) { 271 Logger::error(source) 272 << "resource '" 273 << name 274 << "' has incompatible package. Must be '" 275 << mPackage 276 << "'." 277 << std::endl; 278 return false; 279 } 280 281 auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars); 282 if (badCharIter != name.entry.end()) { 283 Logger::error(source) 284 << "resource '" 285 << name 286 << "' has invalid entry name '" 287 << name.entry 288 << "'. Invalid character '" 289 << StringPiece16(badCharIter, 1) 290 << "'." 291 << std::endl; 292 return false; 293 } 294 295 std::unique_ptr<ResourceTableType>& type = findOrCreateType(name.type); 296 if (resId.isValid() && type->typeId != ResourceTableType::kUnsetTypeId && 297 type->typeId != resId.typeId()) { 298 Logger::error(source) 299 << "trying to make resource '" 300 << name 301 << "' public with ID " 302 << resId 303 << " but type '" 304 << type->type 305 << "' already has ID " 306 << std::hex << type->typeId << std::dec 307 << "." 308 << std::endl; 309 return false; 310 } 311 312 std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, name.entry); 313 if (resId.isValid() && entry->entryId != ResourceEntry::kUnsetEntryId && 314 entry->entryId != resId.entryId()) { 315 Logger::error(source) 316 << "trying to make resource '" 317 << name 318 << "' public with ID " 319 << resId 320 << " but resource already has ID " 321 << ResourceId(mPackageId, type->typeId, entry->entryId) 322 << "." 323 << std::endl; 324 return false; 325 } 326 327 type->publicStatus.isPublic = true; 328 entry->publicStatus.isPublic = true; 329 entry->publicStatus.source = source; 330 331 if (resId.isValid()) { 332 type->typeId = resId.typeId(); 333 entry->entryId = resId.entryId(); 334 } 335 return true; 336 } 337 338 bool ResourceTable::merge(ResourceTable&& other) { 339 const bool mangleNames = mPackage != other.getPackage(); 340 std::u16string mangledName; 341 342 for (auto& otherType : other) { 343 std::unique_ptr<ResourceTableType>& type = findOrCreateType(otherType->type); 344 if (otherType->publicStatus.isPublic) { 345 if (type->publicStatus.isPublic && type->typeId != otherType->typeId) { 346 Logger::error() << "can not merge type '" << type->type 347 << "': conflicting public IDs " 348 << "(" << type->typeId << " vs " << otherType->typeId << ")." 349 << std::endl; 350 return false; 351 } 352 type->publicStatus = std::move(otherType->publicStatus); 353 type->typeId = otherType->typeId; 354 } 355 356 for (auto& otherEntry : otherType->entries) { 357 const std::u16string* nameToAdd = &otherEntry->name; 358 if (mangleNames) { 359 mangledName = otherEntry->name; 360 NameMangler::mangle(other.getPackage(), &mangledName); 361 nameToAdd = &mangledName; 362 } 363 364 std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, *nameToAdd); 365 if (otherEntry->publicStatus.isPublic) { 366 if (entry->publicStatus.isPublic && entry->entryId != otherEntry->entryId) { 367 Logger::error() << "can not merge entry '" << type->type << "/" << entry->name 368 << "': conflicting public IDs " 369 << "(" << entry->entryId << " vs " << entry->entryId << ")." 370 << std::endl; 371 return false; 372 } 373 entry->publicStatus = std::move(otherEntry->publicStatus); 374 entry->entryId = otherEntry->entryId; 375 } 376 377 for (ResourceConfigValue& otherValue : otherEntry->values) { 378 auto iter = std::lower_bound(entry->values.begin(), entry->values.end(), 379 otherValue.config, compareConfigs); 380 if (iter != entry->values.end() && iter->config == otherValue.config) { 381 int collisionResult = defaultCollisionHandler(*iter->value, *otherValue.value); 382 if (collisionResult > 0) { 383 // Take the incoming value. 384 iter->source = std::move(otherValue.source); 385 iter->comment = std::move(otherValue.comment); 386 iter->value = std::unique_ptr<Value>(otherValue.value->clone(&mValuePool)); 387 } else if (collisionResult == 0) { 388 ResourceNameRef resourceName = { mPackage, type->type, entry->name }; 389 Logger::error(otherValue.source) 390 << "resource '" << resourceName << "' has a conflicting value for " 391 << "configuration (" << otherValue.config << ")." 392 << std::endl; 393 Logger::note(iter->source) << "originally defined here." << std::endl; 394 return false; 395 } 396 } else { 397 entry->values.insert(iter, ResourceConfigValue{ 398 otherValue.config, 399 std::move(otherValue.source), 400 std::move(otherValue.comment), 401 std::unique_ptr<Value>(otherValue.value->clone(&mValuePool)), 402 }); 403 } 404 } 405 } 406 } 407 return true; 408 } 409 410 std::tuple<const ResourceTableType*, const ResourceEntry*> 411 ResourceTable::findResource(const ResourceNameRef& name) const { 412 if (name.package != mPackage) { 413 return {}; 414 } 415 416 auto iter = std::lower_bound(mTypes.begin(), mTypes.end(), name.type, lessThanType); 417 if (iter == mTypes.end() || (*iter)->type != name.type) { 418 return {}; 419 } 420 421 const std::unique_ptr<ResourceTableType>& type = *iter; 422 auto iter2 = std::lower_bound(type->entries.begin(), type->entries.end(), name.entry, 423 lessThanEntry); 424 if (iter2 == type->entries.end() || name.entry != (*iter2)->name) { 425 return {}; 426 } 427 return std::make_tuple(iter->get(), iter2->get()); 428 } 429 430 } // namespace aapt 431