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