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 #ifndef ANDROIDFW_ASSETMANAGER2_H_ 18 #define ANDROIDFW_ASSETMANAGER2_H_ 19 20 #include <utils/RefBase.h> 21 22 #include <array> 23 #include <limits> 24 #include <set> 25 #include <span> 26 #include <unordered_map> 27 28 #include "android-base/function_ref.h" 29 #include "android-base/macros.h" 30 #include "androidfw/ApkAssets.h" 31 #include "androidfw/Asset.h" 32 #include "androidfw/AssetManager.h" 33 #include "androidfw/ResourceTypes.h" 34 #include "androidfw/Util.h" 35 36 namespace android { 37 38 class Theme; 39 40 using ApkAssetsCookie = int32_t; 41 42 enum : ApkAssetsCookie { 43 kInvalidCookie = -1, 44 }; 45 46 // Holds a bag that has been merged with its parent, if one exists. 47 struct ResolvedBag { 48 // A single key-value entry in a bag. 49 struct Entry { 50 // The key, as described in ResTable_map::name. 51 uint32_t key; 52 53 Res_value value; 54 55 // The resource ID of the origin style associated with the given entry. 56 uint32_t style; 57 58 // Which ApkAssets this entry came from. 59 ApkAssetsCookie cookie; 60 61 ResStringPool* key_pool; 62 ResStringPool* type_pool; 63 }; 64 65 // Denotes the configuration axis that this bag varies with. 66 // If a configuration changes with respect to one of these axis, 67 // the bag should be reloaded. 68 uint32_t type_spec_flags; 69 70 // The number of entries in this bag. Access them by indexing into `entries`. 71 uint32_t entry_count; 72 73 // The array of entries for this bag. An empty array is a neat trick to force alignment 74 // of the Entry structs that follow this structure and avoids a bunch of casts. 75 Entry entries[0]; 76 }; 77 78 struct FindEntryResult; 79 80 // AssetManager2 is the main entry point for accessing assets and resources. 81 // AssetManager2 provides caching of resources retrieved via the underlying ApkAssets. 82 class AssetManager2 { 83 friend Theme; 84 85 public: 86 struct ResourceName { 87 const char* package = nullptr; 88 size_t package_len = 0u; 89 90 const char* type = nullptr; 91 const char16_t* type16 = nullptr; 92 size_t type_len = 0u; 93 94 const char* entry = nullptr; 95 const char16_t* entry16 = nullptr; 96 size_t entry_len = 0u; 97 }; 98 99 using ApkAssetsPtr = sp<const ApkAssets>; 100 using ApkAssetsWPtr = wp<const ApkAssets>; 101 using ApkAssetsList = std::span<const ApkAssetsPtr>; 102 103 AssetManager2(); 104 explicit AssetManager2(AssetManager2&& other) = default; 105 AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration); 106 107 struct ScopedOperation { 108 DISALLOW_COPY_AND_ASSIGN(ScopedOperation); 109 friend AssetManager2; 110 const AssetManager2& am_; 111 ScopedOperation(const AssetManager2& am); 112 113 public: 114 ~ScopedOperation(); 115 }; 116 117 [[nodiscard]] ScopedOperation StartOperation() const; 118 119 // Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets 120 // are not owned by the AssetManager, and must have a longer lifetime. 121 // 122 // Only pass invalidate_caches=false when it is known that the structure 123 // change in ApkAssets is due to a safe addition of resources with completely 124 // new resource IDs. 125 bool SetApkAssets(ApkAssetsList apk_assets, bool invalidate_caches = true); 126 bool SetApkAssets(std::initializer_list<ApkAssetsPtr> apk_assets, bool invalidate_caches = true); 127 // This one is an optimization - it skips all calculations for applying the currently set 128 // configuration, expecting a configuration update later with a forced refresh. 129 void PresetApkAssets(ApkAssetsList apk_assets); 130 131 const ApkAssetsPtr& GetApkAssets(ApkAssetsCookie cookie) const; GetApkAssetsCount()132 int GetApkAssetsCount() const { 133 return int(apk_assets_.size()); 134 } 135 136 // Returns the string pool for the given asset cookie. 137 // Use the string pool returned here with a valid Res_value object of type Res_value::TYPE_STRING. 138 const ResStringPool* GetStringPoolForCookie(ApkAssetsCookie cookie) const; 139 140 // Returns the DynamicRefTable for the given package ID. 141 // This may be nullptr if the APK represented by `cookie` has no resource table. 142 const DynamicRefTable* GetDynamicRefTableForPackage(uint32_t package_id) const; 143 144 // Returns the DynamicRefTable for the ApkAssets represented by the cookie. 145 // This may be nullptr if the APK represented by `cookie` has no resource table. 146 std::shared_ptr<const DynamicRefTable> GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const; 147 148 // Retrieve the assigned package id of the package if loaded into this AssetManager 149 uint8_t GetAssignedPackageId(const LoadedPackage* package) const; 150 151 // Returns a string representation of the overlayable API of a package. 152 bool GetOverlayablesToString(android::StringPiece package_name, std::string* out) const; 153 154 const std::unordered_map<std::string, std::string>* GetOverlayableMapForPackage( 155 uint32_t package_id) const; 156 157 // Returns whether the resources.arsc of any loaded apk assets is allocated in RAM (not mmapped). 158 bool ContainsAllocatedTable() const; 159 160 // Sets/resets the configuration for this AssetManager. This will cause all 161 // caches that are related to the configuration change to be invalidated. 162 void SetConfigurations(std::vector<ResTable_config> configurations, bool force_refresh = false); 163 GetConfigurations()164 inline const std::vector<ResTable_config>& GetConfigurations() const { 165 return configurations_; 166 } 167 SetDefaultLocale(uint32_t default_locale)168 inline void SetDefaultLocale(uint32_t default_locale) { 169 default_locale_ = default_locale; 170 } 171 172 // Returns all configurations for which there are resources defined, or an I/O error if reading 173 // resource data failed. 174 // 175 // This includes resource configurations in all the ApkAssets set for this AssetManager. 176 // If `exclude_system` is set to true, resource configurations from system APKs 177 // ('android' package, other libraries) will be excluded from the list. 178 // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap' 179 // will be excluded from the list. 180 base::expected<std::set<ResTable_config>, IOError> GetResourceConfigurations( 181 bool exclude_system = false, bool exclude_mipmap = false) const; 182 183 // Returns all the locales for which there are resources defined. This includes resource 184 // locales in all the ApkAssets set for this AssetManager. 185 // If `exclude_system` is set to true, resource locales from system APKs 186 // ('android' package, other libraries) will be excluded from the list. 187 // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized 188 // and de-duped in the resulting list. 189 std::set<std::string> GetResourceLocales(bool exclude_system = false, 190 bool merge_equivalent_languages = false) const; 191 192 // Searches the set of APKs loaded by this AssetManager and opens the first one found located 193 // in the assets/ directory. 194 // `mode` controls how the file is opened. 195 // 196 // NOTE: The loaded APKs are searched in reverse order. 197 std::unique_ptr<Asset> Open(const std::string& filename, Asset::AccessMode mode) const; 198 199 // Opens a file within the assets/ directory of the APK specified by `cookie`. 200 // `mode` controls how the file is opened. 201 std::unique_ptr<Asset> Open(const std::string& filename, ApkAssetsCookie cookie, 202 Asset::AccessMode mode) const; 203 204 // Opens the directory specified by `dirname`. The result is an AssetDir that is the combination 205 // of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded. 206 // The entries are sorted by their ASCII name. 207 std::unique_ptr<AssetDir> OpenDir(const std::string& dirname) const; 208 209 // Searches the set of APKs loaded by this AssetManager and opens the first one found. 210 // `mode` controls how the file is opened. 211 // `out_cookie` is populated with the cookie of the APK this file was found in. 212 // 213 // NOTE: The loaded APKs are searched in reverse order. 214 std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, Asset::AccessMode mode, 215 ApkAssetsCookie* out_cookie = nullptr) const; 216 217 // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened. 218 // This is typically used to open a specific AndroidManifest.xml, or a binary XML file 219 // referenced by a resource lookup with GetResource(). 220 std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie, 221 Asset::AccessMode mode) const; 222 223 // Returns the resource id of parent style of the specified theme. 224 // 225 // Returns a null error if the name is missing/corrupt, or an I/O error if reading resource data 226 // failed. 227 base::expected<uint32_t, NullOrIOError> GetParentThemeResourceId(uint32_t resid) const; 228 229 // Returns the resource name of the specified resource ID. 230 // 231 // Utf8 strings are preferred, and only if they are unavailable are the Utf16 variants populated. 232 // 233 // Returns a null error if the name is missing/corrupt, or an I/O error if reading resource data 234 // failed. 235 base::expected<ResourceName, NullOrIOError> GetResourceName(uint32_t resid) const; 236 237 // Finds the resource ID assigned to `resource_name`. 238 // 239 // `resource_name` must be of the form '[package:][type/]entry'. 240 // If no package is specified in `resource_name`, then `fallback_package` is used as the package. 241 // If no type is specified in `resource_name`, then `fallback_type` is used as the type. 242 // 243 // Returns a null error if no resource by that name was found, or an I/O error if reading resource 244 // data failed. 245 base::expected<uint32_t, NullOrIOError> GetResourceId( 246 const std::string& resource_name, const std::string& fallback_type = {}, 247 const std::string& fallback_package = {}) const; 248 249 struct SelectedValue { 250 friend AssetManager2; 251 friend Theme; 252 SelectedValue() = default; SelectedValueSelectedValue253 SelectedValue(const ResolvedBag* bag, const ResolvedBag::Entry& entry) 254 : cookie(entry.cookie), 255 data(entry.value.data), 256 type(entry.value.dataType), 257 flags(bag->type_spec_flags), 258 resid(0U), 259 config() { 260 } 261 262 // The cookie representing the ApkAssets in which the value resides. 263 ApkAssetsCookie cookie = kInvalidCookie; 264 265 // The data for this value, as interpreted according to `type`. 266 Res_value::data_type data; 267 268 // Type of the data value. 269 uint8_t type; 270 271 // The bitmask of configuration axis that this resource varies with. 272 // See ResTable_config::CONFIG_*. 273 uint32_t flags; 274 275 // The resource ID from which this value was resolved. 276 uint32_t resid; 277 278 // The configuration for which the resolved value was defined. 279 ResTable_config config; 280 281 private: SelectedValueSelectedValue282 SelectedValue(uint8_t value_type, Res_value::data_type value_data, ApkAssetsCookie cookie, 283 uint32_t type_flags, uint32_t resid, const ResTable_config& config) : 284 cookie(cookie), data(value_data), type(value_type), flags(type_flags), 285 resid(resid), config(config) {}; 286 }; 287 288 // Retrieves the best matching resource value with ID `resid`. 289 // 290 // If `may_be_bag` is false, this function logs if the resource was a map/bag type and returns a 291 // null result. If `density_override` is non-zero, the configuration to match against is 292 // overridden with that density. 293 // 294 // Returns a null error if a best match could not be found, or an I/O error if reading resource 295 // data failed. 296 base::expected<SelectedValue, NullOrIOError> GetResource(uint32_t resid, bool may_be_bag = false, 297 uint16_t density_override = 0U) const; 298 299 // Resolves the resource referenced in `value` if the type is Res_value::TYPE_REFERENCE. 300 // 301 // If the data type is not Res_value::TYPE_REFERENCE, no work is done. Configuration flags of the 302 // values pointed to by the reference are OR'd into `value.flags`. If `cache_value` is true, then 303 // the resolved value will be cached and used when attempting to resolve the resource id specified 304 // in `value`. 305 // 306 // Returns a null error if the resource could not be resolved, or an I/O error if reading 307 // resource data failed. 308 base::expected<std::monostate, NullOrIOError> ResolveReference(SelectedValue& value, 309 bool cache_value = false) const; 310 311 // Retrieves the best matching bag/map resource with ID `resid`. 312 // 313 // This method will resolve all parent references for this bag and merge keys with the child. 314 // To iterate over the keys, use the following idiom: 315 // 316 // base::expected<const ResolvedBag*, NullOrIOError> bag = asset_manager->GetBag(id); 317 // if (bag.has_value()) { 318 // for (auto iter = begin(*bag); iter != end(*bag); ++iter) { 319 // ... 320 // } 321 // } 322 // 323 // Returns a null error if a best match could not be found, or an I/O error if reading resource 324 // data failed. 325 base::expected<const ResolvedBag*, NullOrIOError> GetBag(uint32_t resid) const; 326 327 // Retrieves the best matching bag/map resource of the resource referenced in `value`. 328 // 329 // If `value.type` is not Res_value::TYPE_REFERENCE, a null result is returned. 330 // Configuration flags of the bag pointed to by the reference are OR'd into `value.flags`. 331 // 332 // Returns a null error if a best match could not be found, or an I/O error if reading resource 333 // data failed. 334 base::expected<const ResolvedBag*, NullOrIOError> ResolveBag(SelectedValue& value) const; 335 336 // Returns the android::ResTable_typeSpec flags of the resource ID. 337 // 338 // Returns a null error if the resource could not be resolved, or an I/O error if reading 339 // resource data failed. 340 base::expected<uint32_t, NullOrIOError> GetResourceTypeSpecFlags(uint32_t resid) const; 341 342 base::expected<const std::vector<uint32_t>*, NullOrIOError> GetBagResIdStack( 343 uint32_t resid) const; 344 345 // Resets the resource resolution structures in preparation for the next resource retrieval. 346 void ResetResourceResolution() const; 347 348 // Enables or disables resource resolution logging. Clears stored steps when disabled. 349 void SetResourceResolutionLoggingEnabled(bool enabled); 350 351 // Returns formatted log of last resource resolution path, or empty if no resource has been 352 // resolved yet. 353 std::string GetLastResourceResolution() const; 354 355 // Creates a new Theme from this AssetManager. 356 std::unique_ptr<Theme> NewTheme(); 357 358 void ForEachPackage(base::function_ref<bool(const std::string&, uint8_t)> func, 359 package_property_t excluded_property_flags = 0U) const; 360 361 void DumpToLog() const; 362 363 private: 364 DISALLOW_COPY_AND_ASSIGN(AssetManager2); 365 366 // A collection of configurations and their associated ResTable_type that match the current 367 // AssetManager configuration. 368 struct FilteredConfigGroup { 369 std::vector<const TypeSpec::TypeEntry*> type_entries; 370 }; 371 372 // Represents an single package. 373 struct ConfiguredPackage { 374 // A pointer to the immutable, loaded package info. 375 const LoadedPackage* loaded_package_; 376 377 // A mutable AssetManager-specific list of configurations that match the AssetManager's 378 // current configuration. This is used as an optimization to avoid checking every single 379 // candidate configuration when looking up resources. 380 ByteBucketArray<FilteredConfigGroup> filtered_configs_; 381 }; 382 383 // Represents a Runtime Resource Overlay that overlays resources in the logical package. 384 struct ConfiguredOverlay { 385 // The set of package groups that overlay this package group. 386 IdmapResMap overlay_res_maps_; 387 388 // The cookie of the overlay assets. 389 ApkAssetsCookie cookie; 390 }; 391 392 // Represents a logical package, which can be made up of many individual packages. Each package 393 // in a PackageGroup shares the same package name and package ID. 394 struct PackageGroup { 395 // The set of packages that make-up this group. 396 std::vector<ConfiguredPackage> packages_; 397 398 // The cookies associated with each package in the group. They share the same order as 399 // packages_. 400 std::vector<ApkAssetsCookie> cookies_; 401 402 // Runtime Resource Overlays that overlay resources in this package group. 403 std::vector<ConfiguredOverlay> overlays_; 404 405 // A library reference table that contains build-package ID to runtime-package ID mappings. 406 std::shared_ptr<DynamicRefTable> dynamic_ref_table = std::make_shared<DynamicRefTable>(); 407 }; 408 409 // Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple 410 // Res_value, or a complex map/bag type. Returns a null result if a best entry cannot be found. 411 // 412 // `density_override` overrides the density of the current configuration when doing a search. 413 // 414 // When `stop_at_first_match` is true, the first match found is selected and the search 415 // terminates. This is useful for methods that just look up the name of a resource and don't 416 // care about the value. In this case, the value of `FindEntryResult::type_flags` is incomplete 417 // and should not be used. 418 // 419 // When `ignore_configuration` is true, FindEntry will return always select the first entry in 420 // for the type seen regardless of its configuration. 421 // 422 // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly 423 // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds. 424 base::expected<FindEntryResult, NullOrIOError> FindEntry(uint32_t resid, 425 uint16_t density_override, 426 bool stop_at_first_match, 427 bool ignore_configuration) const; 428 429 base::expected<FindEntryResult, NullOrIOError> FindEntryInternal( 430 const PackageGroup& package_group, uint8_t type_idx, uint16_t entry_idx, 431 const ResTable_config& desired_config, bool stop_at_first_match, 432 bool ignore_configuration) const; 433 434 // Assigns package IDs to all shared library ApkAssets. 435 // Should be called whenever the ApkAssets are changed. 436 void BuildDynamicRefTable(ApkAssetsList assets); 437 438 // Purge all resources that are cached and vary by the configuration axis denoted by the 439 // bitmask `diff`. 440 void InvalidateCaches(uint32_t diff); 441 442 // Triggers the re-construction of lists of types that match the set configuration. 443 // This should always be called when mutating the AssetManager's configuration or ApkAssets set. 444 void RebuildFilterList(); 445 446 // Retrieves the APK paths of overlays that overlay non-system packages. 447 std::set<ApkAssetsPtr> GetNonSystemOverlays() const; 448 449 // AssetManager2::GetBag(resid) wraps this function to track which resource ids have already 450 // been seen while traversing bag parents. 451 base::expected<const ResolvedBag*, NullOrIOError> GetBag( 452 uint32_t resid, std::vector<uint32_t>& child_resids) const; 453 454 // Finish an operation that was running with the current asset manager, and clean up the 455 // promoted apk assets when the last operation ends. 456 void FinishOperation() const; 457 458 // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must 459 // have a longer lifetime. 460 // The second pair element is the promoted version of the assets, that is held for the duration 461 // of the currently running operation. FinishOperation() clears all promoted assets to make sure 462 // they can be released when the system needs that. 463 mutable std::vector<std::pair<ApkAssetsWPtr, ApkAssetsPtr>> apk_assets_; 464 465 // DynamicRefTables for shared library package resolution. 466 // These are ordered according to apk_assets_. The mappings may change depending on what is 467 // in apk_assets_, therefore they must be stored in the AssetManager and not in the 468 // immutable ApkAssets class. 469 std::vector<PackageGroup> package_groups_; 470 471 // An array mapping package ID to index into package_groups. This keeps the lookup fast 472 // without taking too much memory. 473 std::array<uint8_t, std::numeric_limits<uint8_t>::max() + 1> package_ids_; 474 475 uint32_t default_locale_; 476 477 // The current configurations set for this AssetManager. When this changes, cached resources 478 // may need to be purged. 479 std::vector<ResTable_config> configurations_; 480 481 // Cached set of bags. These are cached because they can inherit keys from parent bags, 482 // which involves some calculation. 483 mutable std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_; 484 485 // Cached set of bag resid stacks for each bag. These are cached because they might be requested 486 // a number of times for each view during View inspection. 487 mutable std::unordered_map<uint32_t, std::vector<uint32_t>> cached_bag_resid_stacks_; 488 489 // Cached set of resolved resource values. 490 mutable std::unordered_map<uint32_t, SelectedValue> cached_resolved_values_; 491 492 // Tracking the number of the started operations running with the current AssetManager. 493 // Finishing the last one clears all promoted apk assets. 494 mutable int number_of_running_scoped_operations_ = 0; 495 496 // Whether or not to save resource resolution steps 497 bool resource_resolution_logging_enabled_ = false; 498 499 struct Resolution { 500 struct Step { 501 enum class Type { 502 INITIAL, 503 BETTER_MATCH, 504 OVERLAID, 505 OVERLAID_INLINE, 506 SKIPPED, 507 NO_ENTRY, 508 }; 509 510 // Marks what kind of override this step was. 511 Type type; 512 513 ApkAssetsCookie cookie = kInvalidCookie; 514 515 // Built name of configuration for this step. 516 String8 config_name; 517 }; 518 519 // Last resolved resource ID. 520 uint32_t resid; 521 522 // Last resolved resource result cookie. 523 ApkAssetsCookie cookie = kInvalidCookie; 524 525 // Last resolved resource type. 526 StringPoolRef type_string_ref; 527 528 // Last resolved resource entry. 529 StringPoolRef entry_string_ref; 530 531 // Steps taken to resolve last resource. 532 std::vector<Step> steps; 533 534 // The configuration name of the best resource found. 535 String8 best_config_name; 536 537 // The package name of the best resource found. 538 String8 best_package_name; 539 }; 540 541 // Record of the last resolved resource's resolution path. 542 mutable Resolution last_resolution_; 543 }; 544 545 class Theme { 546 friend class AssetManager2; 547 548 public: 549 ~Theme(); 550 551 // Applies the style identified by `resid` to this theme. 552 // 553 // This can be called multiple times with different styles. By default, any theme attributes that 554 // are already defined before this call are not overridden. If `force` is set to true, this 555 // behavior is changed and all theme attributes from the style at `resid` are applied. 556 // 557 // Returns a null error if the style could not be applied, or an I/O error if reading resource 558 // data failed. 559 base::expected<std::monostate, NullOrIOError> ApplyStyle(uint32_t resid, bool force = false); 560 561 // Clears the existing theme, sets the new asset manager to use for this theme, and applies the 562 // styles in `style_ids` through repeated invocations of `ApplyStyle`. 563 void Rebase(AssetManager2* am, const uint32_t* style_ids, const uint8_t* force, 564 size_t style_count); 565 566 // Sets this Theme to be a copy of `source` if `source` has the same AssetManager as this Theme. 567 // 568 // If `source` does not have the same AssetManager as this theme, only attributes from ApkAssets 569 // loaded into both AssetManagers will be copied to this theme. 570 // 571 // Returns an I/O error if reading resource data failed. 572 base::expected<std::monostate, IOError> SetTo(const Theme& source); 573 574 void Clear(); 575 576 // Retrieves the value of attribute ID `resid` in the theme. 577 // 578 // NOTE: This function does not do reference traversal. If you want to follow references to other 579 // resources to get the "real" value to use, you need to call ResolveReference() after this 580 // function. 581 std::optional<AssetManager2::SelectedValue> GetAttribute(uint32_t resid) const; 582 583 // This is like AssetManager2::ResolveReference(), but also takes care of resolving attribute 584 // references to the theme. 585 base::expected<std::monostate, NullOrIOError> ResolveAttributeReference( 586 AssetManager2::SelectedValue& value) const; 587 GetAssetManager()588 AssetManager2* GetAssetManager() { 589 return asset_manager_; 590 } 591 GetAssetManager()592 const AssetManager2* GetAssetManager() const { 593 return asset_manager_; 594 } 595 596 // Returns a bit mask of configuration changes that will impact this 597 // theme (and thus require completely reloading it). GetChangingConfigurations()598 uint32_t GetChangingConfigurations() const { 599 return type_spec_flags_; 600 } 601 602 void Dump() const; 603 604 struct Entry; 605 private: 606 DISALLOW_COPY_AND_ASSIGN(Theme); 607 608 explicit Theme(AssetManager2* asset_manager); 609 610 AssetManager2* asset_manager_ = nullptr; 611 uint32_t type_spec_flags_ = 0u; 612 613 std::vector<uint32_t> keys_; 614 std::vector<Entry> entries_; 615 }; 616 begin(const ResolvedBag * bag)617 inline const ResolvedBag::Entry* begin(const ResolvedBag* bag) { 618 return bag->entries; 619 } 620 end(const ResolvedBag * bag)621 inline const ResolvedBag::Entry* end(const ResolvedBag* bag) { 622 return bag->entries + bag->entry_count; 623 } 624 625 } // namespace android 626 627 #endif /* ANDROIDFW_ASSETMANAGER2_H_ */ 628