Home | History | Annotate | Download | only in androidfw
      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     // Which ApkAssets this entry came from.
     53     ApkAssetsCookie cookie;
     54 
     55     ResStringPool* key_pool;
     56     ResStringPool* type_pool;
     57   };
     58 
     59   // Denotes the configuration axis that this bag varies with.
     60   // If a configuration changes with respect to one of these axis,
     61   // the bag should be reloaded.
     62   uint32_t type_spec_flags;
     63 
     64   // The number of entries in this bag. Access them by indexing into `entries`.
     65   uint32_t entry_count;
     66 
     67   // The array of entries for this bag. An empty array is a neat trick to force alignment
     68   // of the Entry structs that follow this structure and avoids a bunch of casts.
     69   Entry entries[0];
     70 };
     71 
     72 struct FindEntryResult;
     73 
     74 // AssetManager2 is the main entry point for accessing assets and resources.
     75 // AssetManager2 provides caching of resources retrieved via the underlying ApkAssets.
     76 class AssetManager2 {
     77  public:
     78   struct ResourceName {
     79     const char* package = nullptr;
     80     size_t package_len = 0u;
     81 
     82     const char* type = nullptr;
     83     const char16_t* type16 = nullptr;
     84     size_t type_len = 0u;
     85 
     86     const char* entry = nullptr;
     87     const char16_t* entry16 = nullptr;
     88     size_t entry_len = 0u;
     89   };
     90 
     91   AssetManager2();
     92 
     93   // Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets
     94   // are not owned by the AssetManager, and must have a longer lifetime.
     95   //
     96   // Only pass invalidate_caches=false when it is known that the structure
     97   // change in ApkAssets is due to a safe addition of resources with completely
     98   // new resource IDs.
     99   bool SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, bool invalidate_caches = true);
    100 
    101   inline const std::vector<const ApkAssets*> GetApkAssets() const {
    102     return apk_assets_;
    103   }
    104 
    105   // Returns the string pool for the given asset cookie.
    106   // Use the string pool returned here with a valid Res_value object of type Res_value::TYPE_STRING.
    107   const ResStringPool* GetStringPoolForCookie(ApkAssetsCookie cookie) const;
    108 
    109   // Returns the DynamicRefTable for the given package ID.
    110   // This may be nullptr if the APK represented by `cookie` has no resource table.
    111   const DynamicRefTable* GetDynamicRefTableForPackage(uint32_t package_id) const;
    112 
    113   // Returns the DynamicRefTable for the ApkAssets represented by the cookie.
    114   // This may be nullptr if the APK represented by `cookie` has no resource table.
    115   const DynamicRefTable* GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const;
    116 
    117   // Sets/resets the configuration for this AssetManager. This will cause all
    118   // caches that are related to the configuration change to be invalidated.
    119   void SetConfiguration(const ResTable_config& configuration);
    120 
    121   inline const ResTable_config& GetConfiguration() const {
    122     return configuration_;
    123   }
    124 
    125   // Returns all configurations for which there are resources defined. This includes resource
    126   // configurations in all the ApkAssets set for this AssetManager.
    127   // If `exclude_system` is set to true, resource configurations from system APKs
    128   // ('android' package, other libraries) will be excluded from the list.
    129   // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap'
    130   // will be excluded from the list.
    131   std::set<ResTable_config> GetResourceConfigurations(bool exclude_system = false,
    132                                                       bool exclude_mipmap = false) const;
    133 
    134   // Returns all the locales for which there are resources defined. This includes resource
    135   // locales in all the ApkAssets set for this AssetManager.
    136   // If `exclude_system` is set to true, resource locales from system APKs
    137   // ('android' package, other libraries) will be excluded from the list.
    138   // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized
    139   // and de-duped in the resulting list.
    140   std::set<std::string> GetResourceLocales(bool exclude_system = false,
    141                                            bool merge_equivalent_languages = false) const;
    142 
    143   // Searches the set of APKs loaded by this AssetManager and opens the first one found located
    144   // in the assets/ directory.
    145   // `mode` controls how the file is opened.
    146   //
    147   // NOTE: The loaded APKs are searched in reverse order.
    148   std::unique_ptr<Asset> Open(const std::string& filename, Asset::AccessMode mode) const;
    149 
    150   // Opens a file within the assets/ directory of the APK specified by `cookie`.
    151   // `mode` controls how the file is opened.
    152   std::unique_ptr<Asset> Open(const std::string& filename, ApkAssetsCookie cookie,
    153                               Asset::AccessMode mode) const;
    154 
    155   // Opens the directory specified by `dirname`. The result is an AssetDir that is the combination
    156   // of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded.
    157   // The entries are sorted by their ASCII name.
    158   std::unique_ptr<AssetDir> OpenDir(const std::string& dirname) const;
    159 
    160   // Searches the set of APKs loaded by this AssetManager and opens the first one found.
    161   // `mode` controls how the file is opened.
    162   // `out_cookie` is populated with the cookie of the APK this file was found in.
    163   //
    164   // NOTE: The loaded APKs are searched in reverse order.
    165   std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, Asset::AccessMode mode,
    166                                       ApkAssetsCookie* out_cookie = nullptr) const;
    167 
    168   // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened.
    169   // This is typically used to open a specific AndroidManifest.xml, or a binary XML file
    170   // referenced by a resource lookup with GetResource().
    171   std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie,
    172                                       Asset::AccessMode mode) const;
    173 
    174   // Populates the `out_name` parameter with resource name information.
    175   // Utf8 strings are preferred, and only if they are unavailable are
    176   // the Utf16 variants populated.
    177   // Returns false if the resource was not found or the name was missing/corrupt.
    178   bool GetResourceName(uint32_t resid, ResourceName* out_name) const;
    179 
    180   // Populates `out_flags` with the bitmask of configuration axis that this resource varies with.
    181   // See ResTable_config for the list of configuration axis.
    182   // Returns false if the resource was not found.
    183   bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const;
    184 
    185   // Finds the resource ID assigned to `resource_name`.
    186   // `resource_name` must be of the form '[package:][type/]entry'.
    187   // If no package is specified in `resource_name`, then `fallback_package` is used as the package.
    188   // If no type is specified in `resource_name`, then `fallback_type` is used as the type.
    189   // Returns 0x0 if no resource by that name was found.
    190   uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {},
    191                          const std::string& fallback_package = {}) const;
    192 
    193   // Retrieves the best matching resource with ID `resid`. The resource value is filled into
    194   // `out_value` and the configuration for the selected value is populated in `out_selected_config`.
    195   // `out_flags` holds the same flags as retrieved with GetResourceFlags().
    196   // If `density_override` is non-zero, the configuration to match against is overridden with that
    197   // density.
    198   //
    199   // Returns a valid cookie if the resource was found. If the resource was not found, or if the
    200   // resource was a map/bag type, then kInvalidCookie is returned. If `may_be_bag` is false,
    201   // this function logs if the resource was a map/bag type before returning kInvalidCookie.
    202   ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override,
    203                               Res_value* out_value, ResTable_config* out_selected_config,
    204                               uint32_t* out_flags) const;
    205 
    206   // Resolves the resource reference in `in_out_value` if the data type is
    207   // Res_value::TYPE_REFERENCE.
    208   // `cookie` is the ApkAssetsCookie of the reference in `in_out_value`.
    209   // `in_out_value` is the reference to resolve. The result is placed back into this object.
    210   // `in_out_flags` is the type spec flags returned from calls to GetResource() or
    211   // GetResourceFlags(). Configuration flags of the values pointed to by the reference
    212   // are OR'd together with `in_out_flags`.
    213   // `in_out_config` is populated with the configuration for which the resolved value was defined.
    214   // `out_last_reference` is populated with the last reference ID before resolving to an actual
    215   // value. This is only initialized if the passed in `in_out_value` is a reference.
    216   // Returns the cookie of the APK the resolved resource was defined in, or kInvalidCookie if
    217   // it was not found.
    218   ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
    219                                    ResTable_config* in_out_selected_config, uint32_t* in_out_flags,
    220                                    uint32_t* out_last_reference) const;
    221 
    222   // Retrieves the best matching bag/map resource with ID `resid`.
    223   // This method will resolve all parent references for this bag and merge keys with the child.
    224   // To iterate over the keys, use the following idiom:
    225   //
    226   //  const AssetManager2::ResolvedBag* bag = asset_manager->GetBag(id);
    227   //  if (bag != nullptr) {
    228   //    for (auto iter = begin(bag); iter != end(bag); ++iter) {
    229   //      ...
    230   //    }
    231   //  }
    232   const ResolvedBag* GetBag(uint32_t resid);
    233 
    234   // Creates a new Theme from this AssetManager.
    235   std::unique_ptr<Theme> NewTheme();
    236 
    237   template <typename Func>
    238   void ForEachPackage(Func func) const {
    239     for (const PackageGroup& package_group : package_groups_) {
    240       func(package_group.packages_.front().loaded_package_->GetPackageName(),
    241            package_group.dynamic_ref_table.mAssignedPackageId);
    242     }
    243   }
    244 
    245   void DumpToLog() const;
    246 
    247  private:
    248   DISALLOW_COPY_AND_ASSIGN(AssetManager2);
    249 
    250   // Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple
    251   // Res_value, or a complex map/bag type. If successful, it is available in `out_entry`.
    252   // Returns kInvalidCookie on failure. Otherwise, the return value is the cookie associated with
    253   // the ApkAssets in which the entry was found.
    254   //
    255   // `density_override` overrides the density of the current configuration when doing a search.
    256   //
    257   // When `stop_at_first_match` is true, the first match found is selected and the search
    258   // terminates. This is useful for methods that just look up the name of a resource and don't
    259   // care about the value. In this case, the value of `FindEntryResult::type_flags` is incomplete
    260   // and should not be used.
    261   //
    262   // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly
    263   // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds.
    264   ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match,
    265                             FindEntryResult* out_entry) const;
    266 
    267   // Assigns package IDs to all shared library ApkAssets.
    268   // Should be called whenever the ApkAssets are changed.
    269   void BuildDynamicRefTable();
    270 
    271   // Purge all resources that are cached and vary by the configuration axis denoted by the
    272   // bitmask `diff`.
    273   void InvalidateCaches(uint32_t diff);
    274 
    275   // Triggers the re-construction of lists of types that match the set configuration.
    276   // This should always be called when mutating the AssetManager's configuration or ApkAssets set.
    277   void RebuildFilterList();
    278 
    279   // AssetManager2::GetBag(resid) wraps this function to track which resource ids have already
    280   // been seen while traversing bag parents.
    281   const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids);
    282 
    283   // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
    284   // have a longer lifetime.
    285   std::vector<const ApkAssets*> apk_assets_;
    286 
    287   // A collection of configurations and their associated ResTable_type that match the current
    288   // AssetManager configuration.
    289   struct FilteredConfigGroup {
    290     std::vector<ResTable_config> configurations;
    291     std::vector<const ResTable_type*> types;
    292   };
    293 
    294   // Represents an single package.
    295   struct ConfiguredPackage {
    296     // A pointer to the immutable, loaded package info.
    297     const LoadedPackage* loaded_package_;
    298 
    299     // A mutable AssetManager-specific list of configurations that match the AssetManager's
    300     // current configuration. This is used as an optimization to avoid checking every single
    301     // candidate configuration when looking up resources.
    302     ByteBucketArray<FilteredConfigGroup> filtered_configs_;
    303   };
    304 
    305   // Represents a logical package, which can be made up of many individual packages. Each package
    306   // in a PackageGroup shares the same package name and package ID.
    307   struct PackageGroup {
    308     // The set of packages that make-up this group.
    309     std::vector<ConfiguredPackage> packages_;
    310 
    311     // The cookies associated with each package in the group. They share the same order as
    312     // packages_.
    313     std::vector<ApkAssetsCookie> cookies_;
    314 
    315     // A library reference table that contains build-package ID to runtime-package ID mappings.
    316     DynamicRefTable dynamic_ref_table;
    317   };
    318 
    319   // DynamicRefTables for shared library package resolution.
    320   // These are ordered according to apk_assets_. The mappings may change depending on what is
    321   // in apk_assets_, therefore they must be stored in the AssetManager and not in the
    322   // immutable ApkAssets class.
    323   std::vector<PackageGroup> package_groups_;
    324 
    325   // An array mapping package ID to index into package_groups. This keeps the lookup fast
    326   // without taking too much memory.
    327   std::array<uint8_t, std::numeric_limits<uint8_t>::max() + 1> package_ids_;
    328 
    329   // The current configuration set for this AssetManager. When this changes, cached resources
    330   // may need to be purged.
    331   ResTable_config configuration_;
    332 
    333   // Cached set of bags. These are cached because they can inherit keys from parent bags,
    334   // which involves some calculation.
    335   std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;
    336 };
    337 
    338 class Theme {
    339   friend class AssetManager2;
    340 
    341  public:
    342   ~Theme();
    343 
    344   // Applies the style identified by `resid` to this theme. This can be called
    345   // multiple times with different styles. By default, any theme attributes that
    346   // are already defined before this call are not overridden. If `force` is set
    347   // to true, this behavior is changed and all theme attributes from the style at
    348   // `resid` are applied.
    349   // Returns false if the style failed to apply.
    350   bool ApplyStyle(uint32_t resid, bool force = false);
    351 
    352   // Sets this Theme to be a copy of `o` if `o` has the same AssetManager as this Theme.
    353   // Returns false if the AssetManagers of the Themes were not compatible.
    354   bool SetTo(const Theme& o);
    355 
    356   void Clear();
    357 
    358   inline const AssetManager2* GetAssetManager() const {
    359     return asset_manager_;
    360   }
    361 
    362   inline AssetManager2* GetAssetManager() {
    363     return asset_manager_;
    364   }
    365 
    366   // Returns a bit mask of configuration changes that will impact this
    367   // theme (and thus require completely reloading it).
    368   inline uint32_t GetChangingConfigurations() const {
    369     return type_spec_flags_;
    370   }
    371 
    372   // Retrieve a value in the theme. If the theme defines this value, returns an asset cookie
    373   // indicating which ApkAssets it came from and populates `out_value` with the value.
    374   // `out_flags` is populated with a bitmask of the configuration axis with which the resource
    375   // varies.
    376   //
    377   // If the attribute is not found, returns kInvalidCookie.
    378   //
    379   // NOTE: This function does not do reference traversal. If you want to follow references to other
    380   // resources to get the "real" value to use, you need to call ResolveReference() after this
    381   // function.
    382   ApkAssetsCookie GetAttribute(uint32_t resid, Res_value* out_value, uint32_t* out_flags) const;
    383 
    384   // This is like AssetManager2::ResolveReference(), but also takes
    385   // care of resolving attribute references to the theme.
    386   ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value,
    387                                             ResTable_config* in_out_selected_config = nullptr,
    388                                             uint32_t* in_out_type_spec_flags = nullptr,
    389                                             uint32_t* out_last_ref = nullptr) const;
    390 
    391  private:
    392   DISALLOW_COPY_AND_ASSIGN(Theme);
    393 
    394   // Called by AssetManager2.
    395   explicit Theme(AssetManager2* asset_manager);
    396 
    397   AssetManager2* asset_manager_;
    398   uint32_t type_spec_flags_ = 0u;
    399 
    400   // Defined in the cpp.
    401   struct Package;
    402 
    403   constexpr static size_t kPackageCount = std::numeric_limits<uint8_t>::max() + 1;
    404   std::array<std::unique_ptr<Package>, kPackageCount> packages_;
    405 };
    406 
    407 inline const ResolvedBag::Entry* begin(const ResolvedBag* bag) {
    408   return bag->entries;
    409 }
    410 
    411 inline const ResolvedBag::Entry* end(const ResolvedBag* bag) {
    412   return bag->entries + bag->entry_count;
    413 }
    414 
    415 }  // namespace android
    416 
    417 #endif /* ANDROIDFW_ASSETMANAGER2_H_ */
    418