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