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 
98   // Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets
99   // are not owned by the AssetManager, and must have a longer lifetime.
100   //
101   // Only pass invalidate_caches=false when it is known that the structure
102   // change in ApkAssets is due to a safe addition of resources with completely
103   // new resource IDs.
104   //
105   // Only pass in filter_incompatible_configs=false when you want to load all
106   // configurations (including incompatible ones) such as when constructing an
107   // idmap.
108   bool SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, bool invalidate_caches = true,
109           bool filter_incompatible_configs = true);
110 
GetApkAssets()111   inline const std::vector<const ApkAssets*> GetApkAssets() const {
112     return apk_assets_;
113   }
114 
115   // Returns the string pool for the given asset cookie.
116   // Use the string pool returned here with a valid Res_value object of type Res_value::TYPE_STRING.
117   const ResStringPool* GetStringPoolForCookie(ApkAssetsCookie cookie) const;
118 
119   // Returns the DynamicRefTable for the given package ID.
120   // This may be nullptr if the APK represented by `cookie` has no resource table.
121   const DynamicRefTable* GetDynamicRefTableForPackage(uint32_t package_id) const;
122 
123   // Returns the DynamicRefTable for the ApkAssets represented by the cookie.
124   // This may be nullptr if the APK represented by `cookie` has no resource table.
125   std::shared_ptr<const DynamicRefTable> GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const;
126 
127   // Retrieve the assigned package id of the package if loaded into this AssetManager
128   uint8_t GetAssignedPackageId(const LoadedPackage* package) const;
129 
130   // Returns a string representation of the overlayable API of a package.
131   bool GetOverlayablesToString(const android::StringPiece& package_name,
132                                std::string* out) const;
133 
134   const std::unordered_map<std::string, std::string>*
135     GetOverlayableMapForPackage(uint32_t package_id) const;
136 
137   // Returns whether the resources.arsc of any loaded apk assets is allocated in RAM (not mmapped).
138   bool ContainsAllocatedTable() const;
139 
140   // Sets/resets the configuration for this AssetManager. This will cause all
141   // caches that are related to the configuration change to be invalidated.
142   void SetConfiguration(const ResTable_config& configuration);
143 
GetConfiguration()144   inline const ResTable_config& GetConfiguration() const {
145     return configuration_;
146   }
147 
148   // Returns all configurations for which there are resources defined. This includes resource
149   // configurations in all the ApkAssets set for this AssetManager.
150   // If `exclude_system` is set to true, resource configurations from system APKs
151   // ('android' package, other libraries) will be excluded from the list.
152   // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap'
153   // will be excluded from the list.
154   std::set<ResTable_config> GetResourceConfigurations(bool exclude_system = false,
155                                                       bool exclude_mipmap = false) const;
156 
157   // Returns all the locales for which there are resources defined. This includes resource
158   // locales in all the ApkAssets set for this AssetManager.
159   // If `exclude_system` is set to true, resource locales from system APKs
160   // ('android' package, other libraries) will be excluded from the list.
161   // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized
162   // and de-duped in the resulting list.
163   std::set<std::string> GetResourceLocales(bool exclude_system = false,
164                                            bool merge_equivalent_languages = false) const;
165 
166   // Searches the set of APKs loaded by this AssetManager and opens the first one found located
167   // in the assets/ directory.
168   // `mode` controls how the file is opened.
169   //
170   // NOTE: The loaded APKs are searched in reverse order.
171   std::unique_ptr<Asset> Open(const std::string& filename, Asset::AccessMode mode) const;
172 
173   // Opens a file within the assets/ directory of the APK specified by `cookie`.
174   // `mode` controls how the file is opened.
175   std::unique_ptr<Asset> Open(const std::string& filename, ApkAssetsCookie cookie,
176                               Asset::AccessMode mode) const;
177 
178   // Opens the directory specified by `dirname`. The result is an AssetDir that is the combination
179   // of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded.
180   // The entries are sorted by their ASCII name.
181   std::unique_ptr<AssetDir> OpenDir(const std::string& dirname) const;
182 
183   // Searches the set of APKs loaded by this AssetManager and opens the first one found.
184   // `mode` controls how the file is opened.
185   // `out_cookie` is populated with the cookie of the APK this file was found in.
186   //
187   // NOTE: The loaded APKs are searched in reverse order.
188   std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, Asset::AccessMode mode,
189                                       ApkAssetsCookie* out_cookie = nullptr) const;
190 
191   // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened.
192   // This is typically used to open a specific AndroidManifest.xml, or a binary XML file
193   // referenced by a resource lookup with GetResource().
194   std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie,
195                                       Asset::AccessMode mode) const;
196 
197   // Populates the `out_name` parameter with resource name information.
198   // Utf8 strings are preferred, and only if they are unavailable are
199   // the Utf16 variants populated.
200   // Returns false if the resource was not found or the name was missing/corrupt.
201   bool GetResourceName(uint32_t resid, ResourceName* out_name) const;
202 
203   // Populates `out_flags` with the bitmask of configuration axis that this resource varies with.
204   // See ResTable_config for the list of configuration axis.
205   // Returns false if the resource was not found.
206   bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const;
207 
208   // Finds the resource ID assigned to `resource_name`.
209   // `resource_name` must be of the form '[package:][type/]entry'.
210   // If no package is specified in `resource_name`, then `fallback_package` is used as the package.
211   // If no type is specified in `resource_name`, then `fallback_type` is used as the type.
212   // Returns 0x0 if no resource by that name was found.
213   uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {},
214                          const std::string& fallback_package = {}) const;
215 
216   // Retrieves the best matching resource with ID `resid`. The resource value is filled into
217   // `out_value` and the configuration for the selected value is populated in `out_selected_config`.
218   // `out_flags` holds the same flags as retrieved with GetResourceFlags().
219   // If `density_override` is non-zero, the configuration to match against is overridden with that
220   // density.
221   //
222   // Returns a valid cookie if the resource was found. If the resource was not found, or if the
223   // resource was a map/bag type, then kInvalidCookie is returned. If `may_be_bag` is false,
224   // this function logs if the resource was a map/bag type before returning kInvalidCookie.
225   ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override,
226                               Res_value* out_value, ResTable_config* out_selected_config,
227                               uint32_t* out_flags) const;
228 
229   // Resolves the resource reference in `in_out_value` if the data type is
230   // Res_value::TYPE_REFERENCE.
231   // `cookie` is the ApkAssetsCookie of the reference in `in_out_value`.
232   // `in_out_value` is the reference to resolve. The result is placed back into this object.
233   // `in_out_flags` is the type spec flags returned from calls to GetResource() or
234   // GetResourceFlags(). Configuration flags of the values pointed to by the reference
235   // are OR'd together with `in_out_flags`.
236   // `in_out_config` is populated with the configuration for which the resolved value was defined.
237   // `out_last_reference` is populated with the last reference ID before resolving to an actual
238   // value. This is only initialized if the passed in `in_out_value` is a reference.
239   // Returns the cookie of the APK the resolved resource was defined in, or kInvalidCookie if
240   // it was not found.
241   ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
242                                    ResTable_config* in_out_selected_config, uint32_t* in_out_flags,
243                                    uint32_t* out_last_reference) const;
244 
245   // Resets the resource resolution structures in preparation for the next resource retrieval.
246   void ResetResourceResolution() const;
247 
248   // Enables or disables resource resolution logging. Clears stored steps when disabled.
249   void SetResourceResolutionLoggingEnabled(bool enabled);
250 
251   // Returns formatted log of last resource resolution path, or empty if no resource has been
252   // resolved yet.
253   std::string GetLastResourceResolution() const;
254 
255   const std::vector<uint32_t> GetBagResIdStack(uint32_t resid);
256 
257   // Retrieves the best matching bag/map resource with ID `resid`.
258   // This method will resolve all parent references for this bag and merge keys with the child.
259   // To iterate over the keys, use the following idiom:
260   //
261   //  const AssetManager2::ResolvedBag* bag = asset_manager->GetBag(id);
262   //  if (bag != nullptr) {
263   //    for (auto iter = begin(bag); iter != end(bag); ++iter) {
264   //      ...
265   //    }
266   //  }
267   const ResolvedBag* GetBag(uint32_t resid);
268 
269   // Creates a new Theme from this AssetManager.
270   std::unique_ptr<Theme> NewTheme();
271 
272   void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func,
273                       package_property_t excluded_property_flags = 0U) const {
274     for (const PackageGroup& package_group : package_groups_) {
275       const auto loaded_package = package_group.packages_.front().loaded_package_;
276       if ((loaded_package->GetPropertyFlags() & excluded_property_flags) == 0U
277           && !func(loaded_package->GetPackageName(),
278                    package_group.dynamic_ref_table->mAssignedPackageId)) {
279         return;
280       }
281     }
282   }
283 
284   void DumpToLog() const;
285 
286  private:
287   DISALLOW_COPY_AND_ASSIGN(AssetManager2);
288 
289   // A collection of configurations and their associated ResTable_type that match the current
290   // AssetManager configuration.
291   struct FilteredConfigGroup {
292       std::vector<ResTable_config> configurations;
293       std::vector<const ResTable_type*> types;
294   };
295 
296   // Represents an single package.
297   struct ConfiguredPackage {
298       // A pointer to the immutable, loaded package info.
299       const LoadedPackage* loaded_package_;
300 
301       // A mutable AssetManager-specific list of configurations that match the AssetManager's
302       // current configuration. This is used as an optimization to avoid checking every single
303       // candidate configuration when looking up resources.
304       ByteBucketArray<FilteredConfigGroup> filtered_configs_;
305   };
306 
307   // Represents a Runtime Resource Overlay that overlays resources in the logical package.
308   struct ConfiguredOverlay {
309       // The set of package groups that overlay this package group.
310       IdmapResMap overlay_res_maps_;
311 
312       // The cookie of the overlay assets.
313       ApkAssetsCookie cookie;
314   };
315 
316   // Represents a logical package, which can be made up of many individual packages. Each package
317   // in a PackageGroup shares the same package name and package ID.
318   struct PackageGroup {
319       // The set of packages that make-up this group.
320       std::vector<ConfiguredPackage> packages_;
321 
322       // The cookies associated with each package in the group. They share the same order as
323       // packages_.
324       std::vector<ApkAssetsCookie> cookies_;
325 
326       // Runtime Resource Overlays that overlay resources in this package group.
327       std::vector<ConfiguredOverlay> overlays_;
328 
329       // A library reference table that contains build-package ID to runtime-package ID mappings.
330       std::shared_ptr<DynamicRefTable> dynamic_ref_table = std::make_shared<DynamicRefTable>();
331   };
332 
333   // Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple
334   // Res_value, or a complex map/bag type. If successful, it is available in `out_entry`.
335   // Returns kInvalidCookie on failure. Otherwise, the return value is the cookie associated with
336   // the ApkAssets in which the entry was found.
337   //
338   // `density_override` overrides the density of the current configuration when doing a search.
339   //
340   // When `stop_at_first_match` is true, the first match found is selected and the search
341   // terminates. This is useful for methods that just look up the name of a resource and don't
342   // care about the value. In this case, the value of `FindEntryResult::type_flags` is incomplete
343   // and should not be used.
344   //
345   // When `ignore_configuration` is true, FindEntry will return always select the first entry in
346   // for the type seen regardless of its configuration.
347   //
348   // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly
349   // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds.
350   ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match,
351                             bool ignore_configuration, FindEntryResult* out_entry) const;
352 
353   ApkAssetsCookie FindEntryInternal(const PackageGroup& package_group, uint8_t type_idx,
354                                     uint16_t entry_idx, const ResTable_config& desired_config,
355                                     bool /*stop_at_first_match*/,
356                                     bool ignore_configuration, FindEntryResult* out_entry) const;
357 
358   // Assigns package IDs to all shared library ApkAssets.
359   // Should be called whenever the ApkAssets are changed.
360   void BuildDynamicRefTable();
361 
362   // Purge all resources that are cached and vary by the configuration axis denoted by the
363   // bitmask `diff`.
364   void InvalidateCaches(uint32_t diff);
365 
366   // Triggers the re-construction of lists of types that match the set configuration.
367   // This should always be called when mutating the AssetManager's configuration or ApkAssets set.
368   void RebuildFilterList(bool filter_incompatible_configs = true);
369 
370   // Retrieves the APK paths of overlays that overlay non-system packages.
371   std::set<std::string> GetNonSystemOverlayPaths() const;
372 
373   // AssetManager2::GetBag(resid) wraps this function to track which resource ids have already
374   // been seen while traversing bag parents.
375   const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids);
376 
377   // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
378   // have a longer lifetime.
379   std::vector<const ApkAssets*> apk_assets_;
380 
381   // DynamicRefTables for shared library package resolution.
382   // These are ordered according to apk_assets_. The mappings may change depending on what is
383   // in apk_assets_, therefore they must be stored in the AssetManager and not in the
384   // immutable ApkAssets class.
385   std::vector<PackageGroup> package_groups_;
386 
387   // An array mapping package ID to index into package_groups. This keeps the lookup fast
388   // without taking too much memory.
389   std::array<uint8_t, std::numeric_limits<uint8_t>::max() + 1> package_ids_;
390 
391   // The current configuration set for this AssetManager. When this changes, cached resources
392   // may need to be purged.
393   ResTable_config configuration_;
394 
395   // Cached set of bags. These are cached because they can inherit keys from parent bags,
396   // which involves some calculation.
397   std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;
398 
399   // Cached set of bag resid stacks for each bag. These are cached because they might be requested
400   // a number of times for each view during View inspection.
401   std::unordered_map<uint32_t, std::vector<uint32_t>> cached_bag_resid_stacks_;
402 
403   // Whether or not to save resource resolution steps
404   bool resource_resolution_logging_enabled_ = false;
405 
406   struct Resolution {
407 
408     struct Step {
409 
410       enum class Type {
411         INITIAL,
412         BETTER_MATCH,
413         BETTER_MATCH_LOADER,
414         OVERLAID,
415         OVERLAID_LOADER,
416         SKIPPED,
417         SKIPPED_LOADER,
418         NO_ENTRY,
419         NO_ENTRY_LOADER,
420       };
421 
422       // Marks what kind of override this step was.
423       Type type;
424 
425       // Built name of configuration for this step.
426       String8 config_name;
427 
428       // Marks the package name of the better resource found in this step.
429       const std::string* package_name;
430     };
431 
432     // Last resolved resource ID.
433     uint32_t resid;
434 
435     // Last resolved resource result cookie.
436     ApkAssetsCookie cookie = kInvalidCookie;
437 
438     // Last resolved resource type.
439     StringPoolRef type_string_ref;
440 
441     // Last resolved resource entry.
442     StringPoolRef entry_string_ref;
443 
444     // Steps taken to resolve last resource.
445     std::vector<Step> steps;
446   };
447 
448   // Record of the last resolved resource's resolution path.
449   mutable Resolution last_resolution_;
450 };
451 
452 class Theme {
453   friend class AssetManager2;
454 
455  public:
456   ~Theme();
457 
458   // Applies the style identified by `resid` to this theme. This can be called
459   // multiple times with different styles. By default, any theme attributes that
460   // are already defined before this call are not overridden. If `force` is set
461   // to true, this behavior is changed and all theme attributes from the style at
462   // `resid` are applied.
463   // Returns false if the style failed to apply.
464   bool ApplyStyle(uint32_t resid, bool force = false);
465 
466   // Sets this Theme to be a copy of `o` if `o` has the same AssetManager as this Theme.
467   // If `o` does not have the same AssetManager as this theme, only attributes from ApkAssets loaded
468   // into both AssetManagers will be copied to this theme.
469   void SetTo(const Theme& o);
470 
471   void Clear();
472 
473   void Dump() const;
474 
GetAssetManager()475   inline const AssetManager2* GetAssetManager() const {
476     return asset_manager_;
477   }
478 
GetAssetManager()479   inline AssetManager2* GetAssetManager() {
480     return asset_manager_;
481   }
482 
483   // Returns a bit mask of configuration changes that will impact this
484   // theme (and thus require completely reloading it).
GetChangingConfigurations()485   inline uint32_t GetChangingConfigurations() const {
486     return type_spec_flags_;
487   }
488 
489   // Retrieve a value in the theme. If the theme defines this value, returns an asset cookie
490   // indicating which ApkAssets it came from and populates `out_value` with the value.
491   // `out_flags` is populated with a bitmask of the configuration axis with which the resource
492   // varies.
493   //
494   // If the attribute is not found, returns kInvalidCookie.
495   //
496   // NOTE: This function does not do reference traversal. If you want to follow references to other
497   // resources to get the "real" value to use, you need to call ResolveReference() after this
498   // function.
499   ApkAssetsCookie GetAttribute(uint32_t resid, Res_value* out_value, uint32_t* out_flags) const;
500 
501   // This is like AssetManager2::ResolveReference(), but also takes
502   // care of resolving attribute references to the theme.
503   ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value,
504                                             ResTable_config* in_out_selected_config = nullptr,
505                                             uint32_t* in_out_type_spec_flags = nullptr,
506                                             uint32_t* out_last_ref = nullptr) const;
507 
508  private:
509   DISALLOW_COPY_AND_ASSIGN(Theme);
510 
511   // Called by AssetManager2.
512   explicit Theme(AssetManager2* asset_manager);
513 
514   AssetManager2* asset_manager_;
515   uint32_t type_spec_flags_ = 0u;
516 
517   // Defined in the cpp.
518   struct Package;
519 
520   constexpr static size_t kPackageCount = std::numeric_limits<uint8_t>::max() + 1;
521   std::array<std::unique_ptr<Package>, kPackageCount> packages_;
522 };
523 
begin(const ResolvedBag * bag)524 inline const ResolvedBag::Entry* begin(const ResolvedBag* bag) {
525   return bag->entries;
526 }
527 
end(const ResolvedBag * bag)528 inline const ResolvedBag::Entry* end(const ResolvedBag* bag) {
529   return bag->entries + bag->entry_count;
530 }
531 
532 }  // namespace android
533 
534 #endif /* ANDROIDFW_ASSETMANAGER2_H_ */
535