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