1 /* 2 * Copyright (C) 2016 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 #define ATRACE_TAG ATRACE_TAG_RESOURCES 18 19 #include "androidfw/AssetManager2.h" 20 21 #include <set> 22 23 #include "android-base/logging.h" 24 #include "android-base/stringprintf.h" 25 #include "utils/ByteOrder.h" 26 #include "utils/Trace.h" 27 28 #ifdef _WIN32 29 #ifdef ERROR 30 #undef ERROR 31 #endif 32 #endif 33 34 #include "androidfw/ResourceUtils.h" 35 36 namespace android { 37 38 AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } 39 40 bool AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, 41 bool invalidate_caches) { 42 apk_assets_ = apk_assets; 43 BuildDynamicRefTable(); 44 if (invalidate_caches) { 45 InvalidateCaches(static_cast<uint32_t>(-1)); 46 } 47 return true; 48 } 49 50 void AssetManager2::BuildDynamicRefTable() { 51 package_groups_.clear(); 52 package_ids_.fill(0xff); 53 54 // 0x01 is reserved for the android package. 55 int next_package_id = 0x02; 56 const size_t apk_assets_count = apk_assets_.size(); 57 for (size_t i = 0; i < apk_assets_count; i++) { 58 const ApkAssets* apk_asset = apk_assets_[i]; 59 for (const std::unique_ptr<const LoadedPackage>& package : 60 apk_asset->GetLoadedArsc()->GetPackages()) { 61 // Get the package ID or assign one if a shared library. 62 int package_id; 63 if (package->IsDynamic()) { 64 package_id = next_package_id++; 65 } else { 66 package_id = package->GetPackageId(); 67 } 68 69 // Add the mapping for package ID to index if not present. 70 uint8_t idx = package_ids_[package_id]; 71 if (idx == 0xff) { 72 package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size()); 73 package_groups_.push_back({}); 74 package_groups_.back().dynamic_ref_table.mAssignedPackageId = package_id; 75 } 76 PackageGroup* package_group = &package_groups_[idx]; 77 78 // Add the package and to the set of packages with the same ID. 79 package_group->packages_.push_back(package.get()); 80 package_group->cookies_.push_back(static_cast<ApkAssetsCookie>(i)); 81 82 // Add the package name -> build time ID mappings. 83 for (const DynamicPackageEntry& entry : package->GetDynamicPackageMap()) { 84 String16 package_name(entry.package_name.c_str(), entry.package_name.size()); 85 package_group->dynamic_ref_table.mEntries.replaceValueFor( 86 package_name, static_cast<uint8_t>(entry.package_id)); 87 } 88 } 89 } 90 91 // Now assign the runtime IDs so that we have a build-time to runtime ID map. 92 const auto package_groups_end = package_groups_.end(); 93 for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) { 94 const std::string& package_name = iter->packages_[0]->GetPackageName(); 95 for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) { 96 iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()), 97 iter->dynamic_ref_table.mAssignedPackageId); 98 } 99 } 100 } 101 102 void AssetManager2::DumpToLog() const { 103 base::ScopedLogSeverity _log(base::INFO); 104 105 std::string list; 106 for (size_t i = 0; i < package_ids_.size(); i++) { 107 if (package_ids_[i] != 0xff) { 108 base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]); 109 } 110 } 111 LOG(INFO) << "Package ID map: " << list; 112 113 for (const auto& package_group: package_groups_) { 114 list = ""; 115 for (const auto& package : package_group.packages_) { 116 base::StringAppendF(&list, "%s(%02x), ", package->GetPackageName().c_str(), package->GetPackageId()); 117 } 118 LOG(INFO) << base::StringPrintf("PG (%02x): ", package_group.dynamic_ref_table.mAssignedPackageId) << list; 119 } 120 } 121 122 const ResStringPool* AssetManager2::GetStringPoolForCookie(ApkAssetsCookie cookie) const { 123 if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) { 124 return nullptr; 125 } 126 return apk_assets_[cookie]->GetLoadedArsc()->GetStringPool(); 127 } 128 129 const DynamicRefTable* AssetManager2::GetDynamicRefTableForPackage(uint32_t package_id) const { 130 if (package_id >= package_ids_.size()) { 131 return nullptr; 132 } 133 134 const size_t idx = package_ids_[package_id]; 135 if (idx == 0xff) { 136 return nullptr; 137 } 138 return &package_groups_[idx].dynamic_ref_table; 139 } 140 141 const DynamicRefTable* AssetManager2::GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const { 142 for (const PackageGroup& package_group : package_groups_) { 143 for (const ApkAssetsCookie& package_cookie : package_group.cookies_) { 144 if (package_cookie == cookie) { 145 return &package_group.dynamic_ref_table; 146 } 147 } 148 } 149 return nullptr; 150 } 151 152 void AssetManager2::SetConfiguration(const ResTable_config& configuration) { 153 const int diff = configuration_.diff(configuration); 154 configuration_ = configuration; 155 156 if (diff) { 157 InvalidateCaches(static_cast<uint32_t>(diff)); 158 } 159 } 160 161 std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system, 162 bool exclude_mipmap) { 163 ATRACE_CALL(); 164 std::set<ResTable_config> configurations; 165 for (const PackageGroup& package_group : package_groups_) { 166 for (const LoadedPackage* package : package_group.packages_) { 167 if (exclude_system && package->IsSystem()) { 168 continue; 169 } 170 package->CollectConfigurations(exclude_mipmap, &configurations); 171 } 172 } 173 return configurations; 174 } 175 176 std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system, 177 bool merge_equivalent_languages) { 178 ATRACE_CALL(); 179 std::set<std::string> locales; 180 for (const PackageGroup& package_group : package_groups_) { 181 for (const LoadedPackage* package : package_group.packages_) { 182 if (exclude_system && package->IsSystem()) { 183 continue; 184 } 185 package->CollectLocales(merge_equivalent_languages, &locales); 186 } 187 } 188 return locales; 189 } 190 191 std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) { 192 const std::string new_path = "assets/" + filename; 193 return OpenNonAsset(new_path, mode); 194 } 195 196 std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie, 197 Asset::AccessMode mode) { 198 const std::string new_path = "assets/" + filename; 199 return OpenNonAsset(new_path, cookie, mode); 200 } 201 202 std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) { 203 ATRACE_CALL(); 204 205 std::string full_path = "assets/" + dirname; 206 std::unique_ptr<SortedVector<AssetDir::FileInfo>> files = 207 util::make_unique<SortedVector<AssetDir::FileInfo>>(); 208 209 // Start from the back. 210 for (auto iter = apk_assets_.rbegin(); iter != apk_assets_.rend(); ++iter) { 211 const ApkAssets* apk_assets = *iter; 212 213 auto func = [&](const StringPiece& name, FileType type) { 214 AssetDir::FileInfo info; 215 info.setFileName(String8(name.data(), name.size())); 216 info.setFileType(type); 217 info.setSourceName(String8(apk_assets->GetPath().c_str())); 218 files->add(info); 219 }; 220 221 if (!apk_assets->ForEachFile(full_path, func)) { 222 return {}; 223 } 224 } 225 226 std::unique_ptr<AssetDir> asset_dir = util::make_unique<AssetDir>(); 227 asset_dir->setFileList(files.release()); 228 return asset_dir; 229 } 230 231 // Search in reverse because that's how we used to do it and we need to preserve behaviour. 232 // This is unfortunate, because ClassLoaders delegate to the parent first, so the order 233 // is inconsistent for split APKs. 234 std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, 235 Asset::AccessMode mode, 236 ApkAssetsCookie* out_cookie) { 237 ATRACE_CALL(); 238 for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) { 239 std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode); 240 if (asset) { 241 if (out_cookie != nullptr) { 242 *out_cookie = i; 243 } 244 return asset; 245 } 246 } 247 248 if (out_cookie != nullptr) { 249 *out_cookie = kInvalidCookie; 250 } 251 return {}; 252 } 253 254 std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, 255 ApkAssetsCookie cookie, Asset::AccessMode mode) { 256 ATRACE_CALL(); 257 if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) { 258 return {}; 259 } 260 return apk_assets_[cookie]->Open(filename, mode); 261 } 262 263 ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, 264 bool stop_at_first_match, LoadedArscEntry* out_entry, 265 ResTable_config* out_selected_config, 266 uint32_t* out_flags) { 267 ATRACE_CALL(); 268 269 // Might use this if density_override != 0. 270 ResTable_config density_override_config; 271 272 // Select our configuration or generate a density override configuration. 273 ResTable_config* desired_config = &configuration_; 274 if (density_override != 0 && density_override != configuration_.density) { 275 density_override_config = configuration_; 276 density_override_config.density = density_override; 277 desired_config = &density_override_config; 278 } 279 280 if (!is_valid_resid(resid)) { 281 LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); 282 return kInvalidCookie; 283 } 284 285 const uint32_t package_id = get_package_id(resid); 286 const uint8_t type_idx = get_type_id(resid) - 1; 287 const uint16_t entry_id = get_entry_id(resid); 288 289 const uint8_t idx = package_ids_[package_id]; 290 if (idx == 0xff) { 291 LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); 292 return kInvalidCookie; 293 } 294 295 LoadedArscEntry best_entry; 296 ResTable_config best_config; 297 ApkAssetsCookie best_cookie = kInvalidCookie; 298 uint32_t cumulated_flags = 0u; 299 300 const PackageGroup& package_group = package_groups_[idx]; 301 const size_t package_count = package_group.packages_.size(); 302 for (size_t i = 0; i < package_count; i++) { 303 LoadedArscEntry current_entry; 304 ResTable_config current_config; 305 uint32_t current_flags = 0; 306 307 const LoadedPackage* loaded_package = package_group.packages_[i]; 308 if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry, 309 ¤t_config, ¤t_flags)) { 310 continue; 311 } 312 313 cumulated_flags |= current_flags; 314 315 if (best_cookie == kInvalidCookie || current_config.isBetterThan(best_config, desired_config)) { 316 best_entry = current_entry; 317 best_config = current_config; 318 best_cookie = package_group.cookies_[i]; 319 if (stop_at_first_match) { 320 break; 321 } 322 } 323 } 324 325 if (best_cookie == kInvalidCookie) { 326 return kInvalidCookie; 327 } 328 329 *out_entry = best_entry; 330 out_entry->dynamic_ref_table = &package_group.dynamic_ref_table; 331 *out_selected_config = best_config; 332 *out_flags = cumulated_flags; 333 return best_cookie; 334 } 335 336 bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { 337 ATRACE_CALL(); 338 339 LoadedArscEntry entry; 340 ResTable_config config; 341 uint32_t flags = 0u; 342 ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, 343 true /* stop_at_first_match */, &entry, &config, &flags); 344 if (cookie == kInvalidCookie) { 345 return false; 346 } 347 348 const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid); 349 if (package == nullptr) { 350 return false; 351 } 352 353 out_name->package = package->GetPackageName().data(); 354 out_name->package_len = package->GetPackageName().size(); 355 356 out_name->type = entry.type_string_ref.string8(&out_name->type_len); 357 out_name->type16 = nullptr; 358 if (out_name->type == nullptr) { 359 out_name->type16 = entry.type_string_ref.string16(&out_name->type_len); 360 if (out_name->type16 == nullptr) { 361 return false; 362 } 363 } 364 365 out_name->entry = entry.entry_string_ref.string8(&out_name->entry_len); 366 out_name->entry16 = nullptr; 367 if (out_name->entry == nullptr) { 368 out_name->entry16 = entry.entry_string_ref.string16(&out_name->entry_len); 369 if (out_name->entry16 == nullptr) { 370 return false; 371 } 372 } 373 return true; 374 } 375 376 bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { 377 LoadedArscEntry entry; 378 ResTable_config config; 379 ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, 380 false /* stop_at_first_match */, &entry, &config, out_flags); 381 return cookie != kInvalidCookie; 382 } 383 384 ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, 385 uint16_t density_override, Res_value* out_value, 386 ResTable_config* out_selected_config, 387 uint32_t* out_flags) { 388 ATRACE_CALL(); 389 390 LoadedArscEntry entry; 391 ResTable_config config; 392 uint32_t flags = 0u; 393 ApkAssetsCookie cookie = 394 FindEntry(resid, density_override, false /* stop_at_first_match */, &entry, &config, &flags); 395 if (cookie == kInvalidCookie) { 396 return kInvalidCookie; 397 } 398 399 if (dtohl(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) { 400 if (!may_be_bag) { 401 LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid); 402 return kInvalidCookie; 403 } 404 405 // Create a reference since we can't represent this complex type as a Res_value. 406 out_value->dataType = Res_value::TYPE_REFERENCE; 407 out_value->data = resid; 408 *out_selected_config = config; 409 *out_flags = flags; 410 return cookie; 411 } 412 413 const Res_value* device_value = reinterpret_cast<const Res_value*>( 414 reinterpret_cast<const uint8_t*>(entry.entry) + dtohs(entry.entry->size)); 415 out_value->copyFrom_dtoh(*device_value); 416 417 // Convert the package ID to the runtime assigned package ID. 418 entry.dynamic_ref_table->lookupResourceValue(out_value); 419 420 *out_selected_config = config; 421 *out_flags = flags; 422 return cookie; 423 } 424 425 ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, 426 ResTable_config* in_out_selected_config, 427 uint32_t* in_out_flags, 428 uint32_t* out_last_reference) { 429 ATRACE_CALL(); 430 constexpr const int kMaxIterations = 20; 431 432 *out_last_reference = 0u; 433 for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && 434 in_out_value->data != 0u && iteration < kMaxIterations; 435 iteration++) { 436 if (out_last_reference != nullptr) { 437 *out_last_reference = in_out_value->data; 438 } 439 uint32_t new_flags = 0u; 440 cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/, 441 in_out_value, in_out_selected_config, &new_flags); 442 if (cookie == kInvalidCookie) { 443 return kInvalidCookie; 444 } 445 if (in_out_flags != nullptr) { 446 *in_out_flags |= new_flags; 447 } 448 if (*out_last_reference == in_out_value->data) { 449 // This reference can't be resolved, so exit now and let the caller deal with it. 450 return cookie; 451 } 452 } 453 return cookie; 454 } 455 456 const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { 457 ATRACE_CALL(); 458 459 auto cached_iter = cached_bags_.find(resid); 460 if (cached_iter != cached_bags_.end()) { 461 return cached_iter->second.get(); 462 } 463 464 LoadedArscEntry entry; 465 ResTable_config config; 466 uint32_t flags = 0u; 467 ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, 468 false /* stop_at_first_match */, &entry, &config, &flags); 469 if (cookie == kInvalidCookie) { 470 return nullptr; 471 } 472 473 // Check that the size of the entry header is at least as big as 474 // the desired ResTable_map_entry. Also verify that the entry 475 // was intended to be a map. 476 if (dtohs(entry.entry->size) < sizeof(ResTable_map_entry) || 477 (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) { 478 // Not a bag, nothing to do. 479 return nullptr; 480 } 481 482 const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry.entry); 483 const ResTable_map* map_entry = 484 reinterpret_cast<const ResTable_map*>(reinterpret_cast<const uint8_t*>(map) + map->size); 485 const ResTable_map* const map_entry_end = map_entry + dtohl(map->count); 486 487 uint32_t parent_resid = dtohl(map->parent.ident); 488 if (parent_resid == 0) { 489 // There is no parent, meaning there is nothing to inherit and we can do a simple 490 // copy of the entries in the map. 491 const size_t entry_count = map_entry_end - map_entry; 492 util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>( 493 malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag::Entry))))}; 494 ResolvedBag::Entry* new_entry = new_bag->entries; 495 for (; map_entry != map_entry_end; ++map_entry) { 496 uint32_t new_key = dtohl(map_entry->name.ident); 497 if (!is_internal_resid(new_key)) { 498 // Attributes, arrays, etc don't have a resource id as the name. They specify 499 // other data, which would be wrong to change via a lookup. 500 if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { 501 LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); 502 return nullptr; 503 } 504 } 505 new_entry->cookie = cookie; 506 new_entry->value.copyFrom_dtoh(map_entry->value); 507 new_entry->key = new_key; 508 new_entry->key_pool = nullptr; 509 new_entry->type_pool = nullptr; 510 ++new_entry; 511 } 512 new_bag->type_spec_flags = flags; 513 new_bag->entry_count = static_cast<uint32_t>(entry_count); 514 ResolvedBag* result = new_bag.get(); 515 cached_bags_[resid] = std::move(new_bag); 516 return result; 517 } 518 519 // In case the parent is a dynamic reference, resolve it. 520 entry.dynamic_ref_table->lookupResourceId(&parent_resid); 521 522 // Get the parent and do a merge of the keys. 523 const ResolvedBag* parent_bag = GetBag(parent_resid); 524 if (parent_bag == nullptr) { 525 // Failed to get the parent that should exist. 526 LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid); 527 return nullptr; 528 } 529 530 // Combine flags from the parent and our own bag. 531 flags |= parent_bag->type_spec_flags; 532 533 // Create the max possible entries we can make. Once we construct the bag, 534 // we will realloc to fit to size. 535 const size_t max_count = parent_bag->entry_count + dtohl(map->count); 536 ResolvedBag* new_bag = reinterpret_cast<ResolvedBag*>( 537 malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry)))); 538 ResolvedBag::Entry* new_entry = new_bag->entries; 539 540 const ResolvedBag::Entry* parent_entry = parent_bag->entries; 541 const ResolvedBag::Entry* const parent_entry_end = parent_entry + parent_bag->entry_count; 542 543 // The keys are expected to be in sorted order. Merge the two bags. 544 while (map_entry != map_entry_end && parent_entry != parent_entry_end) { 545 uint32_t child_key = dtohl(map_entry->name.ident); 546 if (!is_internal_resid(child_key)) { 547 if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) { 548 LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid); 549 return nullptr; 550 } 551 } 552 553 if (child_key <= parent_entry->key) { 554 // Use the child key if it comes before the parent 555 // or is equal to the parent (overrides). 556 new_entry->cookie = cookie; 557 new_entry->value.copyFrom_dtoh(map_entry->value); 558 new_entry->key = child_key; 559 new_entry->key_pool = nullptr; 560 new_entry->type_pool = nullptr; 561 ++map_entry; 562 } else { 563 // Take the parent entry as-is. 564 memcpy(new_entry, parent_entry, sizeof(*new_entry)); 565 } 566 567 if (child_key >= parent_entry->key) { 568 // Move to the next parent entry if we used it or it was overridden. 569 ++parent_entry; 570 } 571 // Increment to the next entry to fill. 572 ++new_entry; 573 } 574 575 // Finish the child entries if they exist. 576 while (map_entry != map_entry_end) { 577 uint32_t new_key = dtohl(map_entry->name.ident); 578 if (!is_internal_resid(new_key)) { 579 if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { 580 LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); 581 return nullptr; 582 } 583 } 584 new_entry->cookie = cookie; 585 new_entry->value.copyFrom_dtoh(map_entry->value); 586 new_entry->key = new_key; 587 new_entry->key_pool = nullptr; 588 new_entry->type_pool = nullptr; 589 ++map_entry; 590 ++new_entry; 591 } 592 593 // Finish the parent entries if they exist. 594 if (parent_entry != parent_entry_end) { 595 // Take the rest of the parent entries as-is. 596 const size_t num_entries_to_copy = parent_entry_end - parent_entry; 597 memcpy(new_entry, parent_entry, num_entries_to_copy * sizeof(*new_entry)); 598 new_entry += num_entries_to_copy; 599 } 600 601 // Resize the resulting array to fit. 602 const size_t actual_count = new_entry - new_bag->entries; 603 if (actual_count != max_count) { 604 new_bag = reinterpret_cast<ResolvedBag*>( 605 realloc(new_bag, sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry)))); 606 } 607 608 util::unique_cptr<ResolvedBag> final_bag{new_bag}; 609 final_bag->type_spec_flags = flags; 610 final_bag->entry_count = static_cast<uint32_t>(actual_count); 611 ResolvedBag* result = final_bag.get(); 612 cached_bags_[resid] = std::move(final_bag); 613 return result; 614 } 615 616 static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) { 617 ssize_t len = 618 utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str.data()), str.size(), false); 619 if (len < 0) { 620 return false; 621 } 622 out->resize(static_cast<size_t>(len)); 623 utf8_to_utf16(reinterpret_cast<const uint8_t*>(str.data()), str.size(), &*out->begin(), 624 static_cast<size_t>(len + 1)); 625 return true; 626 } 627 628 uint32_t AssetManager2::GetResourceId(const std::string& resource_name, 629 const std::string& fallback_type, 630 const std::string& fallback_package) { 631 StringPiece package_name, type, entry; 632 if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) { 633 return 0u; 634 } 635 636 if (entry.empty()) { 637 return 0u; 638 } 639 640 if (package_name.empty()) { 641 package_name = fallback_package; 642 } 643 644 if (type.empty()) { 645 type = fallback_type; 646 } 647 648 std::u16string type16; 649 if (!Utf8ToUtf16(type, &type16)) { 650 return 0u; 651 } 652 653 std::u16string entry16; 654 if (!Utf8ToUtf16(entry, &entry16)) { 655 return 0u; 656 } 657 658 const StringPiece16 kAttr16 = u"attr"; 659 const static std::u16string kAttrPrivate16 = u"^attr-private"; 660 661 for (const PackageGroup& package_group : package_groups_) { 662 for (const LoadedPackage* package : package_group.packages_) { 663 if (package_name != package->GetPackageName()) { 664 // All packages in the same group are expected to have the same package name. 665 break; 666 } 667 668 uint32_t resid = package->FindEntryByName(type16, entry16); 669 if (resid == 0u && kAttr16 == type16) { 670 // Private attributes in libraries (such as the framework) are sometimes encoded 671 // under the type '^attr-private' in order to leave the ID space of public 'attr' 672 // free for future additions. Check '^attr-private' for the same name. 673 resid = package->FindEntryByName(kAttrPrivate16, entry16); 674 } 675 676 if (resid != 0u) { 677 return fix_package_id(resid, package_group.dynamic_ref_table.mAssignedPackageId); 678 } 679 } 680 } 681 return 0u; 682 } 683 684 void AssetManager2::InvalidateCaches(uint32_t diff) { 685 if (diff == 0xffffffffu) { 686 // Everything must go. 687 cached_bags_.clear(); 688 return; 689 } 690 691 // Be more conservative with what gets purged. Only if the bag has other possible 692 // variations with respect to what changed (diff) should we remove it. 693 for (auto iter = cached_bags_.cbegin(); iter != cached_bags_.cend();) { 694 if (diff & iter->second->type_spec_flags) { 695 iter = cached_bags_.erase(iter); 696 } else { 697 ++iter; 698 } 699 } 700 } 701 702 std::unique_ptr<Theme> AssetManager2::NewTheme() { return std::unique_ptr<Theme>(new Theme(this)); } 703 704 bool Theme::ApplyStyle(uint32_t resid, bool force) { 705 ATRACE_CALL(); 706 707 const ResolvedBag* bag = asset_manager_->GetBag(resid); 708 if (bag == nullptr) { 709 return false; 710 } 711 712 // Merge the flags from this style. 713 type_spec_flags_ |= bag->type_spec_flags; 714 715 // On the first iteration, verify the attribute IDs and 716 // update the entry count in each type. 717 const auto bag_iter_end = end(bag); 718 for (auto bag_iter = begin(bag); bag_iter != bag_iter_end; ++bag_iter) { 719 const uint32_t attr_resid = bag_iter->key; 720 721 // If the resource ID passed in is not a style, the key can be 722 // some other identifier that is not a resource ID. 723 if (!is_valid_resid(attr_resid)) { 724 return false; 725 } 726 727 const uint32_t package_idx = get_package_id(attr_resid); 728 729 // The type ID is 1-based, so subtract 1 to get an index. 730 const uint32_t type_idx = get_type_id(attr_resid) - 1; 731 const uint32_t entry_idx = get_entry_id(attr_resid); 732 733 std::unique_ptr<Package>& package = packages_[package_idx]; 734 if (package == nullptr) { 735 package.reset(new Package()); 736 } 737 738 util::unique_cptr<Type>& type = package->types[type_idx]; 739 if (type == nullptr) { 740 // Set the initial capacity to take up a total amount of 1024 bytes. 741 constexpr uint32_t kInitialCapacity = (1024u - sizeof(Type)) / sizeof(Entry); 742 const uint32_t initial_capacity = std::max(entry_idx, kInitialCapacity); 743 type.reset( 744 reinterpret_cast<Type*>(calloc(sizeof(Type) + (initial_capacity * sizeof(Entry)), 1))); 745 type->entry_capacity = initial_capacity; 746 } 747 748 // Set the entry_count to include this entry. We will populate 749 // and resize the array as necessary in the next pass. 750 if (entry_idx + 1 > type->entry_count) { 751 // Increase the entry count to include this. 752 type->entry_count = entry_idx + 1; 753 } 754 } 755 756 // On the second pass, we will realloc to fit the entry counts 757 // and populate the structures. 758 for (auto bag_iter = begin(bag); bag_iter != bag_iter_end; ++bag_iter) { 759 const uint32_t attr_resid = bag_iter->key; 760 const uint32_t package_idx = get_package_id(attr_resid); 761 const uint32_t type_idx = get_type_id(attr_resid) - 1; 762 const uint32_t entry_idx = get_entry_id(attr_resid); 763 Package* package = packages_[package_idx].get(); 764 util::unique_cptr<Type>& type = package->types[type_idx]; 765 if (type->entry_count != type->entry_capacity) { 766 // Resize to fit the actual entries that will be included. 767 Type* type_ptr = type.release(); 768 type.reset(reinterpret_cast<Type*>( 769 realloc(type_ptr, sizeof(Type) + (type_ptr->entry_count * sizeof(Entry))))); 770 if (type->entry_capacity < type->entry_count) { 771 // Clear the newly allocated memory (which does not get zero initialized). 772 // We need to do this because we |= type_spec_flags. 773 memset(type->entries + type->entry_capacity, 0, 774 sizeof(Entry) * (type->entry_count - type->entry_capacity)); 775 } 776 type->entry_capacity = type->entry_count; 777 } 778 Entry& entry = type->entries[entry_idx]; 779 if (force || entry.value.dataType == Res_value::TYPE_NULL) { 780 entry.cookie = bag_iter->cookie; 781 entry.type_spec_flags |= bag->type_spec_flags; 782 entry.value = bag_iter->value; 783 } 784 } 785 return true; 786 } 787 788 ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, 789 uint32_t* out_flags) const { 790 constexpr const int kMaxIterations = 20; 791 792 uint32_t type_spec_flags = 0u; 793 794 for (int iterations_left = kMaxIterations; iterations_left > 0; iterations_left--) { 795 if (!is_valid_resid(resid)) { 796 return kInvalidCookie; 797 } 798 799 const uint32_t package_idx = get_package_id(resid); 800 801 // Type ID is 1-based, subtract 1 to get the index. 802 const uint32_t type_idx = get_type_id(resid) - 1; 803 const uint32_t entry_idx = get_entry_id(resid); 804 805 const Package* package = packages_[package_idx].get(); 806 if (package == nullptr) { 807 return kInvalidCookie; 808 } 809 810 const Type* type = package->types[type_idx].get(); 811 if (type == nullptr) { 812 return kInvalidCookie; 813 } 814 815 if (entry_idx >= type->entry_count) { 816 return kInvalidCookie; 817 } 818 819 const Entry& entry = type->entries[entry_idx]; 820 type_spec_flags |= entry.type_spec_flags; 821 822 switch (entry.value.dataType) { 823 case Res_value::TYPE_NULL: 824 return kInvalidCookie; 825 826 case Res_value::TYPE_ATTRIBUTE: 827 resid = entry.value.data; 828 break; 829 830 case Res_value::TYPE_DYNAMIC_ATTRIBUTE: { 831 // Resolve the dynamic attribute to a normal attribute 832 // (with the right package ID). 833 resid = entry.value.data; 834 const DynamicRefTable* ref_table = 835 asset_manager_->GetDynamicRefTableForPackage(package_idx); 836 if (ref_table == nullptr || ref_table->lookupResourceId(&resid) != NO_ERROR) { 837 LOG(ERROR) << base::StringPrintf("Failed to resolve dynamic attribute 0x%08x", resid); 838 return kInvalidCookie; 839 } 840 } break; 841 842 case Res_value::TYPE_DYNAMIC_REFERENCE: { 843 // Resolve the dynamic reference to a normal reference 844 // (with the right package ID). 845 out_value->dataType = Res_value::TYPE_REFERENCE; 846 out_value->data = entry.value.data; 847 const DynamicRefTable* ref_table = 848 asset_manager_->GetDynamicRefTableForPackage(package_idx); 849 if (ref_table == nullptr || ref_table->lookupResourceId(&out_value->data) != NO_ERROR) { 850 LOG(ERROR) << base::StringPrintf("Failed to resolve dynamic reference 0x%08x", 851 out_value->data); 852 return kInvalidCookie; 853 } 854 855 if (out_flags != nullptr) { 856 *out_flags = type_spec_flags; 857 } 858 return entry.cookie; 859 } 860 861 default: 862 *out_value = entry.value; 863 if (out_flags != nullptr) { 864 *out_flags = type_spec_flags; 865 } 866 return entry.cookie; 867 } 868 } 869 870 LOG(WARNING) << base::StringPrintf("Too many (%d) attribute references, stopped at: 0x%08x", 871 kMaxIterations, resid); 872 return kInvalidCookie; 873 } 874 875 ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, 876 ResTable_config* in_out_selected_config, 877 uint32_t* in_out_type_spec_flags, 878 uint32_t* out_last_ref) { 879 if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) { 880 uint32_t new_flags; 881 cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags); 882 if (cookie == kInvalidCookie) { 883 return kInvalidCookie; 884 } 885 886 if (in_out_type_spec_flags != nullptr) { 887 *in_out_type_spec_flags |= new_flags; 888 } 889 } 890 return asset_manager_->ResolveReference(cookie, in_out_value, in_out_selected_config, 891 in_out_type_spec_flags, out_last_ref); 892 } 893 894 void Theme::Clear() { 895 type_spec_flags_ = 0u; 896 for (std::unique_ptr<Package>& package : packages_) { 897 package.reset(); 898 } 899 } 900 901 bool Theme::SetTo(const Theme& o) { 902 if (this == &o) { 903 return true; 904 } 905 906 if (asset_manager_ != o.asset_manager_) { 907 return false; 908 } 909 910 type_spec_flags_ = o.type_spec_flags_; 911 912 for (size_t p = 0; p < packages_.size(); p++) { 913 const Package* package = o.packages_[p].get(); 914 if (package == nullptr) { 915 packages_[p].reset(); 916 continue; 917 } 918 919 for (size_t t = 0; t < package->types.size(); t++) { 920 const Type* type = package->types[t].get(); 921 if (type == nullptr) { 922 packages_[p]->types[t].reset(); 923 continue; 924 } 925 926 const size_t type_alloc_size = sizeof(Type) + (type->entry_capacity * sizeof(Entry)); 927 void* copied_data = malloc(type_alloc_size); 928 memcpy(copied_data, type, type_alloc_size); 929 packages_[p]->types[t].reset(reinterpret_cast<Type*>(copied_data)); 930 } 931 } 932 return true; 933 } 934 935 } // namespace android 936