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 // 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 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 289class 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 307 inline const AssetManager2* GetAssetManager() const { return asset_manager_; } 308 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). 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. 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 369inline const ResolvedBag::Entry* begin(const ResolvedBag* bag) { return bag->entries; } 370 371inline 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