• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 id of parent style of the specified theme.
196   //
197   // Returns a null error if the name is missing/corrupt, or an I/O error if reading resource data
198   // failed.
199   base::expected<uint32_t, NullOrIOError> GetParentThemeResourceId(uint32_t resid) const;
200 
201   // Returns the resource name of the specified resource ID.
202   //
203   // Utf8 strings are preferred, and only if they are unavailable are the Utf16 variants populated.
204   //
205   // Returns a null error if the name is missing/corrupt, or an I/O error if reading resource data
206   // failed.
207   base::expected<ResourceName, NullOrIOError> GetResourceName(uint32_t resid) const;
208 
209   // Finds the resource ID assigned to `resource_name`.
210   //
211   // `resource_name` must be of the form '[package:][type/]entry'.
212   // If no package is specified in `resource_name`, then `fallback_package` is used as the package.
213   // If no type is specified in `resource_name`, then `fallback_type` is used as the type.
214   //
215   // Returns a null error if no resource by that name was found, or an I/O error if reading resource
216   // data failed.
217   base::expected<uint32_t, NullOrIOError> GetResourceId(
218       const std::string& resource_name, const std::string& fallback_type = {},
219       const std::string& fallback_package = {}) const;
220 
221   struct SelectedValue {
222     friend AssetManager2;
223     friend Theme;
224     SelectedValue() = default;
SelectedValueSelectedValue225     SelectedValue(const ResolvedBag* bag, const ResolvedBag::Entry& entry) :
226         cookie(entry.cookie), data(entry.value.data), type(entry.value.dataType),
227         flags(bag->type_spec_flags), resid(0U), config({}) {};
228 
229     // The cookie representing the ApkAssets in which the value resides.
230     ApkAssetsCookie cookie = kInvalidCookie;
231 
232     // The data for this value, as interpreted according to `type`.
233     Res_value::data_type data;
234 
235     // Type of the data value.
236     uint8_t type;
237 
238     // The bitmask of configuration axis that this resource varies with.
239     // See ResTable_config::CONFIG_*.
240     uint32_t flags;
241 
242     // The resource ID from which this value was resolved.
243     uint32_t resid;
244 
245     // The configuration for which the resolved value was defined.
246     ResTable_config config;
247 
248    private:
SelectedValueSelectedValue249     SelectedValue(uint8_t value_type, Res_value::data_type value_data, ApkAssetsCookie cookie,
250                   uint32_t type_flags, uint32_t resid, const ResTable_config& config) :
251                   cookie(cookie), data(value_data), type(value_type), flags(type_flags),
252                   resid(resid), config(config) {};
253   };
254 
255   // Retrieves the best matching resource value with ID `resid`.
256   //
257   // If `may_be_bag` is false, this function logs if the resource was a map/bag type and returns a
258   // null result. If `density_override` is non-zero, the configuration to match against is
259   // overridden with that density.
260   //
261   // Returns a null error if a best match could not be found, or an I/O error if reading resource
262   // data failed.
263   base::expected<SelectedValue, NullOrIOError> GetResource(uint32_t resid, bool may_be_bag = false,
264                                                            uint16_t density_override = 0U) const;
265 
266   // Resolves the resource referenced in `value` if the type is Res_value::TYPE_REFERENCE.
267   //
268   // If the data type is not Res_value::TYPE_REFERENCE, no work is done. Configuration flags of the
269   // values pointed to by the reference are OR'd into `value.flags`. If `cache_value` is true, then
270   // the resolved value will be cached and used when attempting to resolve the resource id specified
271   // in `value`.
272   //
273   // Returns a null error if the resource could not be resolved, or an I/O error if reading
274   // resource data failed.
275   base::expected<std::monostate, NullOrIOError> ResolveReference(SelectedValue& value,
276                                                                  bool cache_value = false) const;
277 
278   // Retrieves the best matching bag/map resource with ID `resid`.
279   //
280   // This method will resolve all parent references for this bag and merge keys with the child.
281   // To iterate over the keys, use the following idiom:
282   //
283   //  base::expected<const ResolvedBag*, NullOrIOError> bag = asset_manager->GetBag(id);
284   //  if (bag.has_value()) {
285   //    for (auto iter = begin(*bag); iter != end(*bag); ++iter) {
286   //      ...
287   //    }
288   //  }
289   //
290   // Returns a null error if a best match could not be found, or an I/O error if reading resource
291   // data failed.
292   base::expected<const ResolvedBag*, NullOrIOError> GetBag(uint32_t resid) const;
293 
294   // Retrieves the best matching bag/map resource of the resource referenced in `value`.
295   //
296   // If `value.type` is not Res_value::TYPE_REFERENCE, a null result is returned.
297   // Configuration flags of the bag pointed to by the reference are OR'd into `value.flags`.
298   //
299   // Returns a null error if a best match could not be found, or an I/O error if reading resource
300   // data failed.
301   base::expected<const ResolvedBag*, NullOrIOError> ResolveBag(SelectedValue& value) const;
302 
303   // Returns the android::ResTable_typeSpec flags of the resource ID.
304   //
305   // Returns a null error if the resource could not be resolved, or an I/O error if reading
306   // resource data failed.
307   base::expected<uint32_t, NullOrIOError> GetResourceTypeSpecFlags(uint32_t resid) const;
308 
309   const std::vector<uint32_t> GetBagResIdStack(uint32_t resid) const;
310 
311   // Resets the resource resolution structures in preparation for the next resource retrieval.
312   void ResetResourceResolution() const;
313 
314   // Enables or disables resource resolution logging. Clears stored steps when disabled.
315   void SetResourceResolutionLoggingEnabled(bool enabled);
316 
317   // Returns formatted log of last resource resolution path, or empty if no resource has been
318   // resolved yet.
319   std::string GetLastResourceResolution() const;
320 
321   // Creates a new Theme from this AssetManager.
322   std::unique_ptr<Theme> NewTheme();
323 
324   void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func,
325                       package_property_t excluded_property_flags = 0U) const {
326     for (const PackageGroup& package_group : package_groups_) {
327       const auto loaded_package = package_group.packages_.front().loaded_package_;
328       if ((loaded_package->GetPropertyFlags() & excluded_property_flags) == 0U
329           && !func(loaded_package->GetPackageName(),
330                    package_group.dynamic_ref_table->mAssignedPackageId)) {
331         return;
332       }
333     }
334   }
335 
336   void DumpToLog() const;
337 
338  private:
339   DISALLOW_COPY_AND_ASSIGN(AssetManager2);
340 
341   // A collection of configurations and their associated ResTable_type that match the current
342   // AssetManager configuration.
343   struct FilteredConfigGroup {
344       std::vector<const TypeSpec::TypeEntry*> type_entries;
345   };
346 
347   // Represents an single package.
348   struct ConfiguredPackage {
349       // A pointer to the immutable, loaded package info.
350       const LoadedPackage* loaded_package_;
351 
352       // A mutable AssetManager-specific list of configurations that match the AssetManager's
353       // current configuration. This is used as an optimization to avoid checking every single
354       // candidate configuration when looking up resources.
355       ByteBucketArray<FilteredConfigGroup> filtered_configs_;
356   };
357 
358   // Represents a Runtime Resource Overlay that overlays resources in the logical package.
359   struct ConfiguredOverlay {
360       // The set of package groups that overlay this package group.
361       IdmapResMap overlay_res_maps_;
362 
363       // The cookie of the overlay assets.
364       ApkAssetsCookie cookie;
365   };
366 
367   // Represents a logical package, which can be made up of many individual packages. Each package
368   // in a PackageGroup shares the same package name and package ID.
369   struct PackageGroup {
370       // The set of packages that make-up this group.
371       std::vector<ConfiguredPackage> packages_;
372 
373       // The cookies associated with each package in the group. They share the same order as
374       // packages_.
375       std::vector<ApkAssetsCookie> cookies_;
376 
377       // Runtime Resource Overlays that overlay resources in this package group.
378       std::vector<ConfiguredOverlay> overlays_;
379 
380       // A library reference table that contains build-package ID to runtime-package ID mappings.
381       std::shared_ptr<DynamicRefTable> dynamic_ref_table = std::make_shared<DynamicRefTable>();
382   };
383 
384   // Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple
385   // Res_value, or a complex map/bag type. Returns a null result if a best entry cannot be found.
386   //
387   // `density_override` overrides the density of the current configuration when doing a search.
388   //
389   // When `stop_at_first_match` is true, the first match found is selected and the search
390   // terminates. This is useful for methods that just look up the name of a resource and don't
391   // care about the value. In this case, the value of `FindEntryResult::type_flags` is incomplete
392   // and should not be used.
393   //
394   // When `ignore_configuration` is true, FindEntry will return always select the first entry in
395   // for the type seen regardless of its configuration.
396   //
397   // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly
398   // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds.
399   base::expected<FindEntryResult, NullOrIOError> FindEntry(uint32_t resid,
400                                                            uint16_t density_override,
401                                                            bool stop_at_first_match,
402                                                            bool ignore_configuration) const;
403 
404   base::expected<FindEntryResult, NullOrIOError> FindEntryInternal(
405       const PackageGroup& package_group, uint8_t type_idx, uint16_t entry_idx,
406       const ResTable_config& desired_config, bool stop_at_first_match,
407       bool ignore_configuration) const;
408 
409   // Assigns package IDs to all shared library ApkAssets.
410   // Should be called whenever the ApkAssets are changed.
411   void BuildDynamicRefTable();
412 
413   // Purge all resources that are cached and vary by the configuration axis denoted by the
414   // bitmask `diff`.
415   void InvalidateCaches(uint32_t diff);
416 
417   // Triggers the re-construction of lists of types that match the set configuration.
418   // This should always be called when mutating the AssetManager's configuration or ApkAssets set.
419   void RebuildFilterList();
420 
421   // Retrieves the APK paths of overlays that overlay non-system packages.
422   std::set<const ApkAssets*> GetNonSystemOverlays() const;
423 
424   // AssetManager2::GetBag(resid) wraps this function to track which resource ids have already
425   // been seen while traversing bag parents.
426   base::expected<const ResolvedBag*, NullOrIOError> GetBag(
427       uint32_t resid, std::vector<uint32_t>& child_resids) const;
428 
429   // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
430   // have a longer lifetime.
431   std::vector<const ApkAssets*> apk_assets_;
432 
433   // DynamicRefTables for shared library package resolution.
434   // These are ordered according to apk_assets_. The mappings may change depending on what is
435   // in apk_assets_, therefore they must be stored in the AssetManager and not in the
436   // immutable ApkAssets class.
437   std::vector<PackageGroup> package_groups_;
438 
439   // An array mapping package ID to index into package_groups. This keeps the lookup fast
440   // without taking too much memory.
441   std::array<uint8_t, std::numeric_limits<uint8_t>::max() + 1> package_ids_;
442 
443   // The current configuration set for this AssetManager. When this changes, cached resources
444   // may need to be purged.
445   ResTable_config configuration_;
446 
447   // Cached set of bags. These are cached because they can inherit keys from parent bags,
448   // which involves some calculation.
449   mutable std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;
450 
451   // Cached set of bag resid stacks for each bag. These are cached because they might be requested
452   // a number of times for each view during View inspection.
453   mutable std::unordered_map<uint32_t, std::vector<uint32_t>> cached_bag_resid_stacks_;
454 
455   // Cached set of resolved resource values.
456   mutable std::unordered_map<uint32_t, SelectedValue> cached_resolved_values_;
457 
458   // Whether or not to save resource resolution steps
459   bool resource_resolution_logging_enabled_ = false;
460 
461   struct Resolution {
462     struct Step {
463       enum class Type {
464         INITIAL,
465         BETTER_MATCH,
466         OVERLAID,
467         OVERLAID_INLINE,
468         SKIPPED,
469         NO_ENTRY,
470       };
471 
472       // Marks what kind of override this step was.
473       Type type;
474 
475       // Built name of configuration for this step.
476       String8 config_name;
477 
478       ApkAssetsCookie cookie = kInvalidCookie;
479     };
480 
481     // Last resolved resource ID.
482     uint32_t resid;
483 
484     // Last resolved resource result cookie.
485     ApkAssetsCookie cookie = kInvalidCookie;
486 
487     // Last resolved resource type.
488     StringPoolRef type_string_ref;
489 
490     // Last resolved resource entry.
491     StringPoolRef entry_string_ref;
492 
493     // Steps taken to resolve last resource.
494     std::vector<Step> steps;
495 
496     // The configuration name of the best resource found.
497     String8 best_config_name;
498 
499     // The package name of the best resource found.
500     String8 best_package_name;
501   };
502 
503   // Record of the last resolved resource's resolution path.
504   mutable Resolution last_resolution_;
505 };
506 
507 class Theme {
508   friend class AssetManager2;
509 
510  public:
511   ~Theme();
512 
513   // Applies the style identified by `resid` to this theme.
514   //
515   // This can be called multiple times with different styles. By default, any theme attributes that
516   // are already defined before this call are not overridden. If `force` is set to true, this
517   // behavior is changed and all theme attributes from the style at `resid` are applied.
518   //
519   // Returns a null error if the style could not be applied, or an I/O error if reading resource
520   // data failed.
521   base::expected<std::monostate, NullOrIOError> ApplyStyle(uint32_t resid, bool force = false);
522 
523   // Clears the existing theme, sets the new asset manager to use for this theme, and applies the
524   // styles in `style_ids` through repeated invocations of `ApplyStyle`.
525   void Rebase(AssetManager2* am, const uint32_t* style_ids, const uint8_t* force,
526               size_t style_count);
527 
528   // Sets this Theme to be a copy of `source` if `source` has the same AssetManager as this Theme.
529   //
530   // If `source` does not have the same AssetManager as this theme, only attributes from ApkAssets
531   // loaded into both AssetManagers will be copied to this theme.
532   //
533   // Returns an I/O error if reading resource data failed.
534   base::expected<std::monostate, IOError> SetTo(const Theme& source);
535 
536   void Clear();
537 
538   // Retrieves the value of attribute ID `resid` in the theme.
539   //
540   // NOTE: This function does not do reference traversal. If you want to follow references to other
541   // resources to get the "real" value to use, you need to call ResolveReference() after this
542   // function.
543   std::optional<AssetManager2::SelectedValue> GetAttribute(uint32_t resid) const;
544 
545   // This is like AssetManager2::ResolveReference(), but also takes care of resolving attribute
546   // references to the theme.
547   base::expected<std::monostate, NullOrIOError> ResolveAttributeReference(
548       AssetManager2::SelectedValue& value) const;
549 
GetAssetManager()550   AssetManager2* GetAssetManager() {
551     return asset_manager_;
552   }
553 
GetAssetManager()554   const AssetManager2* GetAssetManager() const {
555     return asset_manager_;
556   }
557 
558   // Returns a bit mask of configuration changes that will impact this
559   // theme (and thus require completely reloading it).
GetChangingConfigurations()560   uint32_t GetChangingConfigurations() const {
561     return type_spec_flags_;
562   }
563 
564   void Dump() const;
565 
566   struct Entry;
567  private:
568   DISALLOW_COPY_AND_ASSIGN(Theme);
569 
570   explicit Theme(AssetManager2* asset_manager);
571 
572   AssetManager2* asset_manager_ = nullptr;
573   uint32_t type_spec_flags_ = 0u;
574 
575   std::vector<Entry> entries_;
576 };
577 
begin(const ResolvedBag * bag)578 inline const ResolvedBag::Entry* begin(const ResolvedBag* bag) {
579   return bag->entries;
580 }
581 
end(const ResolvedBag * bag)582 inline const ResolvedBag::Entry* end(const ResolvedBag* bag) {
583   return bag->entries + bag->entry_count;
584 }
585 
586 }  // namespace android
587 
588 #endif /* ANDROIDFW_ASSETMANAGER2_H_ */
589