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