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