AssetManager2.h revision 0c40524953f3d36a880f91183302a2ea5c722930
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
33namespace android {
34
35class Theme;
36
37using ApkAssetsCookie = int32_t;
38
39enum : ApkAssetsCookie {
40  kInvalidCookie = -1,
41};
42
43// Holds a bag that has been merged with its parent, if one exists.
44struct 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// AssetManager2 is the main entry point for accessing assets and resources.
73// AssetManager2 provides caching of resources retrieved via the underlying
74// ApkAssets.
75class AssetManager2 : public ::AAssetManager {
76 public:
77  struct ResourceName {
78    const char* package = nullptr;
79    size_t package_len = 0u;
80
81    const char* type = nullptr;
82    const char16_t* type16 = nullptr;
83    size_t type_len = 0u;
84
85    const char* entry = nullptr;
86    const char16_t* entry16 = nullptr;
87    size_t entry_len = 0u;
88  };
89
90  AssetManager2();
91
92  // Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets
93  // are not owned by the AssetManager, and must have a longer lifetime.
94  //
95  // Only pass invalidate_caches=false when it is known that the structure
96  // change in ApkAssets is due to a safe addition of resources with completely
97  // new resource IDs.
98  bool SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, bool invalidate_caches = true);
99
100  inline const std::vector<const ApkAssets*> GetApkAssets() const { return apk_assets_; }
101
102  // Returns the string pool for the given asset cookie.
103  // Use the string pool returned here with a valid Res_value object of
104  // type Res_value::TYPE_STRING.
105  const ResStringPool* GetStringPoolForCookie(ApkAssetsCookie cookie) const;
106
107  // Returns the DynamicRefTable for the given package ID.
108  const DynamicRefTable* GetDynamicRefTableForPackage(uint32_t package_id) const;
109
110  // Sets/resets the configuration for this AssetManager. This will cause all
111  // caches that are related to the configuration change to be invalidated.
112  void SetConfiguration(const ResTable_config& configuration);
113
114  inline const ResTable_config& GetConfiguration() const { return configuration_; }
115
116  // Returns all configurations for which there are resources defined. This includes resource
117  // configurations in all the ApkAssets set for this AssetManager.
118  // If `exclude_system` is set to true, resource configurations from system APKs
119  // ('android' package, other libraries) will be excluded from the list.
120  // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap'
121  // will be excluded from the list.
122  std::set<ResTable_config> GetResourceConfigurations(bool exclude_system = false,
123                                                      bool exclude_mipmap = false);
124
125  // Returns all the locales for which there are resources defined. This includes resource
126  // locales in all the ApkAssets set for this AssetManager.
127  // If `exclude_system` is set to true, resource locales from system APKs
128  // ('android' package, other libraries) will be excluded from the list.
129  // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized
130  // and de-duped in the resulting list.
131  std::set<std::string> GetResourceLocales(bool exclude_system = false,
132                                           bool merge_equivalent_languages = false);
133
134  // Searches the set of APKs loaded by this AssetManager and opens the first one found located
135  // in the assets/ directory.
136  // `mode` controls how the file is opened.
137  //
138  // NOTE: The loaded APKs are searched in reverse order.
139  std::unique_ptr<Asset> Open(const std::string& filename, Asset::AccessMode mode);
140
141  // Opens a file within the assets/ directory of the APK specified by `cookie`.
142  // `mode` controls how the file is opened.
143  std::unique_ptr<Asset> Open(const std::string& filename, ApkAssetsCookie cookie,
144                              Asset::AccessMode mode);
145
146  // Searches the set of APKs loaded by this AssetManager and opens the first one found.
147  // `mode` controls how the file is opened.
148  // `out_cookie` is populated with the cookie of the APK this file was found in.
149  //
150  // NOTE: The loaded APKs are searched in reverse order.
151  std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, Asset::AccessMode mode,
152                                      ApkAssetsCookie* out_cookie = nullptr);
153
154  // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened.
155  // This is typically used to open a specific AndroidManifest.xml, or a binary XML file
156  // referenced by a resource lookup with GetResource().
157  std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie,
158                                      Asset::AccessMode mode);
159
160  // Populates the `out_name` parameter with resource name information.
161  // Utf8 strings are preferred, and only if they are unavailable are
162  // the Utf16 variants populated.
163  // Returns false if the resource was not found or the name was missing/corrupt.
164  bool GetResourceName(uint32_t resid, ResourceName* out_name);
165
166  // Populates `out_flags` with the bitmask of configuration axis that this resource varies with.
167  // See ResTable_config for the list of configuration axis.
168  // Returns false if the resource was not found.
169  bool GetResourceFlags(uint32_t resid, uint32_t* out_flags);
170
171  // Finds the resource ID assigned to `resource_name`.
172  // `resource_name` must be of the form '[package:][type/]entry'.
173  // If no package is specified in `resource_name`, then `fallback_package` is used as the package.
174  // If no type is specified in `resource_name`, then `fallback_type` is used as the type.
175  // Returns 0x0 if no resource by that name was found.
176  uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {},
177                         const std::string& fallback_package = {});
178
179  // Retrieves the best matching resource with ID `resid`. The resource value is filled into
180  // `out_value` and the configuration for the selected value is populated in `out_selected_config`.
181  // `out_flags` holds the same flags as retrieved with GetResourceFlags().
182  // If `density_override` is non-zero, the configuration to match against is overridden with that
183  // density.
184  //
185  // Returns a valid cookie if the resource was found. If the resource was not found, or if the
186  // resource was a map/bag type, then kInvalidCookie is returned. If `may_be_bag` is false,
187  // this function logs if the resource was a map/bag type before returning kInvalidCookie.
188  ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override,
189                              Res_value* out_value, ResTable_config* out_selected_config,
190                              uint32_t* out_flags);
191
192  // Resolves the resource reference in `in_out_value` if the data type is
193  // Res_value::TYPE_REFERENCE.
194  // `cookie` is the ApkAssetsCookie of the reference in `in_out_value`.
195  // `in_out_value` is the reference to resolve. The result is placed back into this object.
196  // `in_out_flags` is the type spec flags returned from calls to GetResource() or
197  // GetResourceFlags(). Configuration flags of the values pointed to by the reference
198  // are OR'd together with `in_out_flags`.
199  // `in_out_config` is populated with the configuration for which the resolved value was defined.
200  // `out_last_reference` is populated with the last reference ID before resolving to an actual
201  // value.
202  // Returns the cookie of the APK the resolved resource was defined in, or kInvalidCookie if
203  // it was not found.
204  ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
205                                   ResTable_config* in_out_selected_config, uint32_t* in_out_flags,
206                                   ResTable_ref* out_last_reference);
207
208  // Retrieves the best matching bag/map resource with ID `resid`.
209  // This method will resolve all parent references for this bag and merge keys with the child.
210  // To iterate over the keys, use the following idiom:
211  //
212  //  const AssetManager2::ResolvedBag* bag = asset_manager->GetBag(id);
213  //  if (bag != nullptr) {
214  //    for (auto iter = begin(bag); iter != end(bag); ++iter) {
215  //      ...
216  //    }
217  //  }
218  const ResolvedBag* GetBag(uint32_t resid);
219
220  // Creates a new Theme from this AssetManager.
221  std::unique_ptr<Theme> NewTheme();
222
223  void DumpToLog() const;
224
225 private:
226  DISALLOW_COPY_AND_ASSIGN(AssetManager2);
227
228  // Finds the best entry for `resid` amongst all the ApkAssets. The entry can be a simple
229  // Res_value, or a complex map/bag type.
230  //
231  // `density_override` overrides the density of the current configuration when doing a search.
232  //
233  // When `stop_at_first_match` is true, the first match found is selected and the search
234  // terminates. This is useful for methods that just look up the name of a resource and don't
235  // care about the value. In this case, the value of `out_flags` is incomplete and should not
236  // be used.
237  //
238  // `out_flags` stores the resulting bitmask of configuration axis with which the resource
239  // value varies.
240  ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match,
241                            LoadedArscEntry* out_entry, ResTable_config* out_selected_config,
242                            uint32_t* out_flags);
243
244  // Assigns package IDs to all shared library ApkAssets.
245  // Should be called whenever the ApkAssets are changed.
246  void BuildDynamicRefTable();
247
248  // Purge all resources that are cached and vary by the configuration axis denoted by the
249  // bitmask `diff`.
250  void InvalidateCaches(uint32_t diff);
251
252  // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
253  // have a longer lifetime.
254  std::vector<const ApkAssets*> apk_assets_;
255
256  struct PackageGroup {
257    std::vector<const LoadedPackage*> packages_;
258    std::vector<ApkAssetsCookie> cookies_;
259    DynamicRefTable dynamic_ref_table;
260  };
261
262  // DynamicRefTables for shared library package resolution.
263  // These are ordered according to apk_assets_. The mappings may change depending on what is
264  // in apk_assets_, therefore they must be stored in the AssetManager and not in the
265  // immutable ApkAssets class.
266  std::vector<PackageGroup> package_groups_;
267
268  // An array mapping package ID to index into package_groups. This keeps the lookup fast
269  // without taking too much memory.
270  std::array<uint8_t, std::numeric_limits<uint8_t>::max() + 1> package_ids_;
271
272  // The current configuration set for this AssetManager. When this changes, cached resources
273  // may need to be purged.
274  ResTable_config configuration_;
275
276  // Cached set of bags. These are cached because they can inherit keys from parent bags,
277  // which involves some calculation.
278  std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;
279};
280
281class Theme {
282  friend class AssetManager2;
283
284 public:
285  // Applies the style identified by `resid` to this theme. This can be called
286  // multiple times with different styles. By default, any theme attributes that
287  // are already defined before this call are not overridden. If `force` is set
288  // to true, this behavior is changed and all theme attributes from the style at
289  // `resid` are applied.
290  // Returns false if the style failed to apply.
291  bool ApplyStyle(uint32_t resid, bool force = false);
292
293  // Sets this Theme to be a copy of `o` if `o` has the same AssetManager as this Theme.
294  // Returns false if the AssetManagers of the Themes were not compatible.
295  bool SetTo(const Theme& o);
296
297  void Clear();
298
299  inline const AssetManager2* GetAssetManager() const { return asset_manager_; }
300
301  // Returns a bit mask of configuration changes that will impact this
302  // theme (and thus require completely reloading it).
303  inline uint32_t GetChangingConfigurations() const { return type_spec_flags_; }
304
305  // Retrieve a value in the theme. If the theme defines this value,
306  // returns an asset cookie indicating which ApkAssets it came from
307  // and populates `out_value` with the value. If `out_flags` is non-null,
308  // populates it with a bitmask of the configuration axis the resource
309  // varies with.
310  //
311  // If the attribute is not found, returns kInvalidCookie.
312  //
313  // NOTE: This function does not do reference traversal. If you want
314  // to follow references to other resources to get the "real" value to
315  // use, you need to call ResolveReference() after this function.
316  ApkAssetsCookie GetAttribute(uint32_t resid, Res_value* out_value,
317                               uint32_t* out_flags = nullptr) const;
318
319  // This is like AssetManager2::ResolveReference(), but also takes
320  // care of resolving attribute references to the theme.
321  ApkAssetsCookie ResolveAttributeReference(Res_value* in_out_value, ApkAssetsCookie src_cookie,
322                                            uint32_t* out_last_ref = nullptr,
323                                            uint32_t* in_out_type_spec_flags = nullptr,
324                                            ResTable_config* out_selected_config = nullptr) const;
325
326 private:
327  DISALLOW_COPY_AND_ASSIGN(Theme);
328
329  // Called by AssetManager2.
330  explicit inline Theme(AssetManager2* asset_manager) : asset_manager_(asset_manager) {}
331
332  struct Entry {
333    ApkAssetsCookie cookie;
334    uint32_t type_spec_flags;
335    Res_value value;
336  };
337
338  struct Type {
339    // Use uint32_t for fewer cycles when loading from memory.
340    uint32_t entry_count;
341    uint32_t entry_capacity;
342    Entry entries[0];
343  };
344
345  static constexpr const size_t kPackageCount = std::numeric_limits<uint8_t>::max() + 1;
346  static constexpr const size_t kTypeCount = std::numeric_limits<uint8_t>::max() + 1;
347
348  struct Package {
349    // Each element of Type will be a dynamically sized object
350    // allocated to have the entries stored contiguously with the Type.
351    std::array<util::unique_cptr<Type>, kTypeCount> types;
352  };
353
354  AssetManager2* asset_manager_;
355  uint32_t type_spec_flags_ = 0u;
356  std::array<std::unique_ptr<Package>, kPackageCount> packages_;
357};
358
359inline const ResolvedBag::Entry* begin(const ResolvedBag* bag) { return bag->entries; }
360
361inline const ResolvedBag::Entry* end(const ResolvedBag* bag) {
362  return bag->entries + bag->entry_count;
363}
364
365}  // namespace android
366
367#endif /* ANDROIDFW_ASSETMANAGER2_H_ */
368