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