diff options
author | 2017-01-13 20:47:26 -0800 | |
---|---|---|
committer | 2017-01-31 16:20:29 -0800 | |
commit | 0c40524953f3d36a880f91183302a2ea5c722930 (patch) | |
tree | eb7a15ddd974e1263864902f9f3dd019a47343fc | |
parent | a9285db08883dbbe7b5eb9276cb52b7e01b42aa3 (diff) |
AssetManager2: Add other support methods
- Add GetResourceConfigurations()
- Add GetResourceLocales()
- Add ResolveReference()
- Add stub for GetResourceId()
- Change LoadedArsc and ApkAssets factory method to return const
Test: make libandroidfw_tests
Change-Id: Ia797dc9381a523b1a3e7029048a413e544730379
21 files changed, 445 insertions, 48 deletions
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 9a08f638f230..fe68ec01e4b7 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -27,16 +27,17 @@ namespace android { -std::unique_ptr<ApkAssets> ApkAssets::Load(const std::string& path) { - return ApkAssets::LoadImpl(path, false /*load_as_shared_library*/); +std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system) { + return ApkAssets::LoadImpl(path, system, false /*load_as_shared_library*/); } -std::unique_ptr<ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path) { - return ApkAssets::LoadImpl(path, true /*load_as_shared_library*/); +std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path, + bool system) { + return ApkAssets::LoadImpl(path, system, true /*load_as_shared_library*/); } -std::unique_ptr<ApkAssets> ApkAssets::LoadImpl(const std::string& path, - bool load_as_shared_library) { +std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(const std::string& path, bool system, + bool load_as_shared_library) { ATRACE_CALL(); ::ZipArchiveHandle unmanaged_handle; int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle); @@ -70,11 +71,13 @@ std::unique_ptr<ApkAssets> ApkAssets::LoadImpl(const std::string& path, loaded_apk->path_ = path; loaded_apk->loaded_arsc_ = LoadedArsc::Load(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/), - loaded_apk->resources_asset_->getLength(), load_as_shared_library); + loaded_apk->resources_asset_->getLength(), system, load_as_shared_library); if (loaded_apk->loaded_arsc_ == nullptr) { return {}; } - return loaded_apk; + + // Need to force a move for mingw32. + return std::move(loaded_apk); } std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode /*mode*/) const { diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index d2eff65106cd..542a125f018b 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -18,6 +18,8 @@ #include "androidfw/AssetManager2.h" +#include <set> + #include "android-base/logging.h" #include "android-base/stringprintf.h" #include "utils/ByteOrder.h" @@ -143,6 +145,36 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) { } } +std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system, + bool exclude_mipmap) { + ATRACE_CALL(); + std::set<ResTable_config> configurations; + for (const PackageGroup& package_group : package_groups_) { + for (const LoadedPackage* package : package_group.packages_) { + if (exclude_system && package->IsSystem()) { + continue; + } + package->CollectConfigurations(exclude_mipmap, &configurations); + } + } + return configurations; +} + +std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system, + bool merge_equivalent_languages) { + ATRACE_CALL(); + std::set<std::string> locales; + for (const PackageGroup& package_group : package_groups_) { + for (const LoadedPackage* package : package_group.packages_) { + if (exclude_system && package->IsSystem()) { + continue; + } + package->CollectLocales(merge_equivalent_languages, &locales); + } + } + return locales; +} + std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, mode); @@ -325,8 +357,15 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, if (dtohl(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) { if (!may_be_bag) { LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid); + return kInvalidCookie; } - return kInvalidCookie; + + // Create a reference since we can't represent this complex type as a Res_value. + out_value->dataType = Res_value::TYPE_REFERENCE; + out_value->data = resid; + *out_selected_config = config; + *out_flags = flags; + return cookie; } const Res_value* device_value = reinterpret_cast<const Res_value*>( @@ -341,6 +380,37 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, return cookie; } +ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, + ResTable_config* in_out_selected_config, + uint32_t* in_out_flags, + ResTable_ref* out_last_reference) { + ATRACE_CALL(); + constexpr const int kMaxIterations = 20; + + out_last_reference->ident = 0u; + for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && + in_out_value->data != 0u && iteration < kMaxIterations; + iteration++) { + if (out_last_reference != nullptr) { + out_last_reference->ident = in_out_value->data; + } + uint32_t new_flags = 0u; + cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/, + in_out_value, in_out_selected_config, &new_flags); + if (cookie == kInvalidCookie) { + return kInvalidCookie; + } + if (in_out_flags != nullptr) { + *in_out_flags |= new_flags; + } + if (out_last_reference->ident == in_out_value->data) { + // This reference can't be resolved, so exit now and let the caller deal with it. + return cookie; + } + } + return cookie; +} + const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { ATRACE_CALL(); @@ -501,6 +571,15 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { return result; } +uint32_t AssetManager2::GetResourceId(const std::string& resource_name, + const std::string& fallback_type, + const std::string& fallback_package) { + (void)resource_name; + (void)fallback_type; + (void)fallback_package; + return 0u; +} + void AssetManager2::InvalidateCaches(uint32_t diff) { if (diff == 0xffffffffu) { // Everything must go. diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index c7d0fa5b0cf3..cb589ec41034 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -321,6 +321,57 @@ static bool VerifyType(const Chunk& chunk) { return true; } +void LoadedPackage::CollectConfigurations(bool exclude_mipmap, + std::set<ResTable_config>* out_configs) const { + const static std::u16string kMipMap = u"mipmap"; + const size_t type_count = type_specs_.size(); + for (size_t i = 0; i < type_count; i++) { + const util::unique_cptr<TypeSpec>& type_spec = type_specs_[i]; + if (type_spec != nullptr) { + if (exclude_mipmap) { + const int type_idx = type_spec->type_spec->id - 1; + size_t type_name_len; + const char16_t* type_name16 = type_string_pool_.stringAt(type_idx, &type_name_len); + if (type_name16 != nullptr) { + if (kMipMap.compare(0, std::u16string::npos, type_name16, type_name_len) == 0) { + // This is a mipmap type, skip collection. + continue; + } + } + const char* type_name = type_string_pool_.string8At(type_idx, &type_name_len); + if (type_name != nullptr) { + if (strncmp(type_name, "mipmap", type_name_len) == 0) { + // This is a mipmap type, skip collection. + continue; + } + } + } + + for (size_t j = 0; j < type_spec->type_count; j++) { + out_configs->insert(type_spec->types[j].configuration); + } + } + } +} + +void LoadedPackage::CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const { + char temp_locale[RESTABLE_MAX_LOCALE_LEN]; + const size_t type_count = type_specs_.size(); + for (size_t i = 0; i < type_count; i++) { + const util::unique_cptr<TypeSpec>& type_spec = type_specs_[i]; + if (type_spec != nullptr) { + for (size_t j = 0; j < type_spec->type_count; j++) { + const ResTable_config& configuration = type_spec->types[j].configuration; + if (configuration.locale != 0) { + configuration.getBcp47Locale(temp_locale, canonicalize); + std::string locale(temp_locale); + out_locales->insert(std::move(locale)); + } + } + } + } +} + std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk) { ATRACE_CALL(); std::unique_ptr<LoadedPackage> loaded_package{new LoadedPackage()}; @@ -574,6 +625,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, bool load_as_shared_library) { if (loaded_package->package_id_ == kAppPackageId) { loaded_package->dynamic_ = load_as_shared_library; } + loaded_package->system_ = system_; packages_.push_back(std::move(loaded_package)); } break; @@ -590,12 +642,13 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, bool load_as_shared_library) { return true; } -std::unique_ptr<LoadedArsc> LoadedArsc::Load(const void* data, size_t len, - bool load_as_shared_library) { +std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const void* data, size_t len, bool system, + bool load_as_shared_library) { ATRACE_CALL(); // Not using make_unique because the constructor is private. std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc()); + loaded_arsc->system_ = system; ChunkIterator iter(data, len); while (iter.HasNext()) { @@ -617,7 +670,9 @@ std::unique_ptr<LoadedArsc> LoadedArsc::Load(const void* data, size_t len, LOG(ERROR) << iter.GetLastError(); return {}; } - return loaded_arsc; + + // Need to force a move for mingw32. + return std::move(loaded_arsc); } } // namespace android diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h index 9d4fd29d30d7..6d1578c2d50e 100644 --- a/libs/androidfw/include/androidfw/ApkAssets.h +++ b/libs/androidfw/include/androidfw/ApkAssets.h @@ -31,8 +31,9 @@ namespace android { // Holds an APK. class ApkAssets { public: - static std::unique_ptr<ApkAssets> Load(const std::string& path); - static std::unique_ptr<ApkAssets> LoadAsSharedLibrary(const std::string& path); + static std::unique_ptr<const ApkAssets> Load(const std::string& path, bool system = false); + static std::unique_ptr<const ApkAssets> LoadAsSharedLibrary(const std::string& path, + bool system = false); std::unique_ptr<Asset> Open(const std::string& path, Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const; @@ -44,7 +45,8 @@ class ApkAssets { private: DISALLOW_COPY_AND_ASSIGN(ApkAssets); - static std::unique_ptr<ApkAssets> LoadImpl(const std::string& path, bool load_as_shared_library); + static std::unique_ptr<const ApkAssets> LoadImpl(const std::string& path, bool system, + bool load_as_shared_library); ApkAssets() = default; @@ -57,7 +59,7 @@ class ApkAssets { ZipArchivePtr zip_handle_; std::string path_; std::unique_ptr<Asset> resources_asset_; - std::unique_ptr<LoadedArsc> loaded_arsc_; + std::unique_ptr<const LoadedArsc> loaded_arsc_; }; } // namespace android diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 86553390dadf..81cdc46be0b4 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -21,6 +21,7 @@ #include <array> #include <limits> +#include <set> #include <unordered_map> #include "androidfw/ApkAssets.h" @@ -112,6 +113,24 @@ class AssetManager2 : public ::AAssetManager { inline const ResTable_config& GetConfiguration() const { return configuration_; } + // Returns all configurations for which there are resources defined. This includes resource + // configurations in all the ApkAssets set for this AssetManager. + // If `exclude_system` is set to true, resource configurations from system APKs + // ('android' package, other libraries) will be excluded from the list. + // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap' + // will be excluded from the list. + std::set<ResTable_config> GetResourceConfigurations(bool exclude_system = false, + bool exclude_mipmap = false); + + // Returns all the locales for which there are resources defined. This includes resource + // locales in all the ApkAssets set for this AssetManager. + // If `exclude_system` is set to true, resource locales from system APKs + // ('android' package, other libraries) will be excluded from the list. + // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized + // and de-duped in the resulting list. + std::set<std::string> GetResourceLocales(bool exclude_system = false, + bool merge_equivalent_languages = false); + // Searches the set of APKs loaded by this AssetManager and opens the first one found located // in the assets/ directory. // `mode` controls how the file is opened. @@ -149,6 +168,14 @@ class AssetManager2 : public ::AAssetManager { // Returns false if the resource was not found. bool GetResourceFlags(uint32_t resid, uint32_t* out_flags); + // Finds the resource ID assigned to `resource_name`. + // `resource_name` must be of the form '[package:][type/]entry'. + // If no package is specified in `resource_name`, then `fallback_package` is used as the package. + // If no type is specified in `resource_name`, then `fallback_type` is used as the type. + // Returns 0x0 if no resource by that name was found. + uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {}, + const std::string& fallback_package = {}); + // Retrieves the best matching resource with ID `resid`. The resource value is filled into // `out_value` and the configuration for the selected value is populated in `out_selected_config`. // `out_flags` holds the same flags as retrieved with GetResourceFlags(). @@ -162,6 +189,22 @@ class AssetManager2 : public ::AAssetManager { Res_value* out_value, ResTable_config* out_selected_config, uint32_t* out_flags); + // Resolves the resource reference in `in_out_value` if the data type is + // Res_value::TYPE_REFERENCE. + // `cookie` is the ApkAssetsCookie of the reference in `in_out_value`. + // `in_out_value` is the reference to resolve. The result is placed back into this object. + // `in_out_flags` is the type spec flags returned from calls to GetResource() or + // GetResourceFlags(). Configuration flags of the values pointed to by the reference + // are OR'd together with `in_out_flags`. + // `in_out_config` is populated with the configuration for which the resolved value was defined. + // `out_last_reference` is populated with the last reference ID before resolving to an actual + // value. + // Returns the cookie of the APK the resolved resource was defined in, or kInvalidCookie if + // it was not found. + ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, + ResTable_config* in_out_selected_config, uint32_t* in_out_flags, + ResTable_ref* out_last_reference); + // Retrieves the best matching bag/map resource with ID `resid`. // This method will resolve all parent references for this bag and merge keys with the child. // To iterate over the keys, use the following idiom: diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index e8cb164ce201..91a7cb7f45f9 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -18,6 +18,7 @@ #define LOADEDARSC_H_ #include <memory> +#include <set> #include <vector> #include "android-base/macros.h" @@ -68,20 +69,38 @@ class LoadedPackage { LoadedArscEntry* out_entry, ResTable_config* out_selected_config, uint32_t* out_flags) const; + // Returns the string pool where type names are stored. inline const ResStringPool* GetTypeStringPool() const { return &type_string_pool_; } + // Returns the string pool where the names of resource entries are stored. inline const ResStringPool* GetKeyStringPool() const { return &key_string_pool_; } inline const std::string& GetPackageName() const { return package_name_; } inline int GetPackageId() const { return package_id_; } + // Returns true if this package is dynamic (shared library) and needs to have an ID assigned. inline bool IsDynamic() const { return dynamic_; } + // Returns true if this package originates from a system provided resource. + inline bool IsSystem() const { return system_; } + + // Returns the map of package name to package ID used in this LoadedPackage. At runtime, a + // package could have been assigned a different package ID than what this LoadedPackage was + // compiled with. AssetManager rewrites the package IDs so that they are compatible at runtime. inline const std::vector<DynamicPackageEntry>& GetDynamicPackageMap() const { return dynamic_package_map_; } + // Populates a set of ResTable_config structs, possibly excluding configurations defined for + // the mipmap type. + void CollectConfigurations(bool exclude_mipmap, std::set<ResTable_config>* out_configs) const; + + // Populates a set of strings representing locales. + // If `canonicalize` is set to true, each locale is transformed into its canonical format + // before being inserted into the set. This may cause some equivalent locales to de-dupe. + void CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const; + private: DISALLOW_COPY_AND_ASSIGN(LoadedPackage); @@ -93,8 +112,9 @@ class LoadedPackage { ResStringPool key_string_pool_; std::string package_name_; int package_id_ = -1; - bool dynamic_ = false; int type_id_offset_ = 0; + bool dynamic_ = false; + bool system_ = false; ByteBucketArray<util::unique_cptr<TypeSpec>> type_specs_; std::vector<DynamicPackageEntry> dynamic_package_map_; @@ -104,10 +124,14 @@ class LoadedPackage { // when loading, including offsets and lengths. class LoadedArsc { public: - // Load the resource table from memory. The data's lifetime must out-live the - // object returned from this method. - static std::unique_ptr<LoadedArsc> Load(const void* data, size_t len, - bool load_as_shared_library = false); + // Load a resource table from memory pointed to by `data` of size `len`. + // The lifetime of `data` must out-live the LoadedArsc returned from this method. + // If `system` is set to true, the LoadedArsc is considered as a system provided resource. + // If `load_as_shared_library` is set to true, the application package (0x7f) is treated + // as a shared library (0x00). When loaded into an AssetManager, the package will be assigned an + // ID. + static std::unique_ptr<const LoadedArsc> Load(const void* data, size_t len, bool system = false, + bool load_as_shared_library = false); ~LoadedArsc(); @@ -125,6 +149,10 @@ class LoadedArsc { // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist. const LoadedPackage* GetPackageForId(uint32_t resid) const; + // Returns true if this is a system provided resource. + inline bool IsSystem() const { return system_; } + + // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. inline const std::vector<std::unique_ptr<const LoadedPackage>>& GetPackages() const { return packages_; } @@ -137,6 +165,7 @@ class LoadedArsc { ResStringPool global_string_pool_; std::vector<std::unique_ptr<const LoadedPackage>> packages_; + bool system_ = false; }; } // namespace android diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 56c22e60e30b..04a5d958c614 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -1188,6 +1188,8 @@ struct ResTable_config int compare(const ResTable_config& o) const; int compareLogical(const ResTable_config& o) const; + inline bool operator<(const ResTable_config& o) const { return compare(o) < 0; } + // Flags indicating a set of config values. These flag constants must // match the corresponding ones in android.content.pm.ActivityInfo and // attrs_manifest.xml. diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h index fd96730d2398..96b42bf45a08 100644 --- a/libs/androidfw/include/androidfw/Util.h +++ b/libs/androidfw/include/androidfw/Util.h @@ -94,8 +94,12 @@ class unique_cptr { inline bool operator==(const unique_cptr& o) const { return ptr_ == o.ptr_; } + inline bool operator!=(const unique_cptr& o) const { return ptr_ != o.ptr_; } + inline bool operator==(std::nullptr_t) const { return ptr_ == nullptr; } + inline bool operator!=(std::nullptr_t) const { return ptr_ != nullptr; } + private: DISALLOW_COPY_AND_ASSIGN(unique_cptr); diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index 02037120f098..6b4a7199eba4 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -24,7 +24,8 @@ using com::android::basic::R; namespace android { TEST(ApkAssetsTest, LoadApk) { - std::unique_ptr<ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); + std::unique_ptr<const ApkAssets> loaded_apk = + ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); ASSERT_NE(nullptr, loaded_apk); EXPECT_NE(nullptr, loaded_apk->GetLoadedArsc()); @@ -33,7 +34,7 @@ TEST(ApkAssetsTest, LoadApk) { } TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { - std::unique_ptr<ApkAssets> loaded_apk = + std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); ASSERT_NE(nullptr, loaded_apk); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp index b3c2dc34cdcf..273290a26050 100644 --- a/libs/androidfw/tests/AssetManager2_bench.cpp +++ b/libs/androidfw/tests/AssetManager2_bench.cpp @@ -38,7 +38,7 @@ constexpr const static char* kFrameworkPath = "/system/framework/framework-res.a static void BM_AssetManagerLoadAssets(benchmark::State& state) { std::string path = GetTestDataPath() + "/basic/basic.apk"; while (state.KeepRunning()) { - std::unique_ptr<ApkAssets> apk = ApkAssets::Load(path); + std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(path); AssetManager2 assets; assets.SetApkAssets({apk.get()}); } @@ -61,7 +61,7 @@ BENCHMARK(BM_AssetManagerLoadAssetsOld); static void BM_AssetManagerLoadFrameworkAssets(benchmark::State& state) { std::string path = kFrameworkPath; while (state.KeepRunning()) { - std::unique_ptr<ApkAssets> apk = ApkAssets::Load(path); + std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(path); AssetManager2 assets; assets.SetApkAssets({apk.get()}); } @@ -84,10 +84,10 @@ BENCHMARK(BM_AssetManagerLoadFrameworkAssetsOld); static void GetResourceBenchmark(const std::vector<std::string>& paths, const ResTable_config* config, uint32_t resid, benchmark::State& state) { - std::vector<std::unique_ptr<ApkAssets>> apk_assets; + std::vector<std::unique_ptr<const ApkAssets>> apk_assets; std::vector<const ApkAssets*> apk_assets_ptrs; for (const std::string& path : paths) { - std::unique_ptr<ApkAssets> apk = ApkAssets::Load(path); + std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(path); if (apk == nullptr) { state.SkipWithError(base::StringPrintf("Failed to load assets %s", path.c_str()).c_str()); return; @@ -187,7 +187,7 @@ static void BM_AssetManagerGetResourceFrameworkLocaleOld(benchmark::State& state BENCHMARK(BM_AssetManagerGetResourceFrameworkLocaleOld); static void BM_AssetManagerGetBag(benchmark::State& state) { - std::unique_ptr<ApkAssets> apk = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); if (apk == nullptr) { state.SkipWithError("Failed to load assets"); return; @@ -234,4 +234,40 @@ static void BM_AssetManagerGetBagOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerGetBagOld); +static void BM_AssetManagerGetResourceLocales(benchmark::State& state) { + std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(kFrameworkPath); + if (apk == nullptr) { + state.SkipWithError("Failed to load assets"); + return; + } + + AssetManager2 assets; + assets.SetApkAssets({apk.get()}); + + while (state.KeepRunning()) { + std::set<std::string> locales = + assets.GetResourceLocales(false /*exclude_system*/, true /*merge_equivalent_languages*/); + benchmark::DoNotOptimize(locales); + } +} +BENCHMARK(BM_AssetManagerGetResourceLocales); + +static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) { + AssetManager assets; + if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/, + false /*isSystemAssets*/)) { + state.SkipWithError("Failed to load assets"); + return; + } + + const ResTable& table = assets.getResources(true); + + while (state.KeepRunning()) { + Vector<String8> locales; + table.getLocales(&locales, true /*includeSystemLocales*/, true /*mergeEquivalentLangs*/); + benchmark::DoNotOptimize(locales); + } +} +BENCHMARK(BM_AssetManagerGetResourceLocalesOld); + } // namespace android diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 543456afa2f4..557d8d40dfc2 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -26,6 +26,7 @@ #include "data/lib_two/R.h" #include "data/libclient/R.h" #include "data/styles/R.h" +#include "data/system/R.h" namespace app = com::android::app; namespace appaslib = com::android::appaslib::app; @@ -59,16 +60,20 @@ class AssetManager2Test : public ::testing::Test { appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); ASSERT_NE(nullptr, appaslib_assets_); + + system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/); + ASSERT_NE(nullptr, system_assets_); } protected: - std::unique_ptr<ApkAssets> basic_assets_; - std::unique_ptr<ApkAssets> basic_de_fr_assets_; - std::unique_ptr<ApkAssets> style_assets_; - std::unique_ptr<ApkAssets> lib_one_assets_; - std::unique_ptr<ApkAssets> lib_two_assets_; - std::unique_ptr<ApkAssets> libclient_assets_; - std::unique_ptr<ApkAssets> appaslib_assets_; + std::unique_ptr<const ApkAssets> basic_assets_; + std::unique_ptr<const ApkAssets> basic_de_fr_assets_; + std::unique_ptr<const ApkAssets> style_assets_; + std::unique_ptr<const ApkAssets> lib_one_assets_; + std::unique_ptr<const ApkAssets> lib_two_assets_; + std::unique_ptr<const ApkAssets> libclient_assets_; + std::unique_ptr<const ApkAssets> appaslib_assets_; + std::unique_ptr<const ApkAssets> system_assets_; }; TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) { @@ -291,6 +296,131 @@ TEST_F(AssetManager2Test, MergesStylesWithParentFromSingleApkAssets) { EXPECT_EQ(0, bag_two->entries[4].cookie); } +TEST_F(AssetManager2Test, ResolveReferenceToResource) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({basic_assets_.get()}); + + Res_value value; + ResTable_config selected_config; + uint32_t flags; + ApkAssetsCookie cookie = + assetmanager.GetResource(basic::R::integer::ref1, false /*may_be_bag*/, + 0u /*density_override*/, &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); + + EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); + EXPECT_EQ(basic::R::integer::ref2, value.data); + + ResTable_ref last_ref; + cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(12000u, value.data); + EXPECT_EQ(basic::R::integer::ref2, last_ref.ident); +} + +TEST_F(AssetManager2Test, ResolveReferenceToBag) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({basic_assets_.get()}); + + Res_value value; + ResTable_config selected_config; + uint32_t flags; + ApkAssetsCookie cookie = + assetmanager.GetResource(basic::R::integer::number2, true /*may_be_bag*/, + 0u /*density_override*/, &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); + + EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); + EXPECT_EQ(basic::R::array::integerArray1, value.data); + + ResTable_ref last_ref; + cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); + EXPECT_EQ(basic::R::array::integerArray1, value.data); + EXPECT_EQ(basic::R::array::integerArray1, last_ref.ident); +} + +static bool IsConfigurationPresent(const std::set<ResTable_config>& configurations, + const ResTable_config& configuration) { + return configurations.count(configuration) > 0; +} + +TEST_F(AssetManager2Test, GetResourceConfigurations) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({system_assets_.get(), basic_de_fr_assets_.get()}); + + std::set<ResTable_config> configurations = assetmanager.GetResourceConfigurations(); + + // We expect the locale sv from the system assets, and de and fr from basic_de_fr assets. + // And one extra for the default configuration. + EXPECT_EQ(4u, configurations.size()); + + ResTable_config expected_config; + memset(&expected_config, 0, sizeof(expected_config)); + expected_config.language[0] = 's'; + expected_config.language[1] = 'v'; + EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); + + expected_config.language[0] = 'd'; + expected_config.language[1] = 'e'; + EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); + + expected_config.language[0] = 'f'; + expected_config.language[1] = 'r'; + EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); + + // Take out the system assets. + configurations = assetmanager.GetResourceConfigurations(true /* exclude_system */); + + // We expect de and fr from basic_de_fr assets. + EXPECT_EQ(2u, configurations.size()); + + expected_config.language[0] = 's'; + expected_config.language[1] = 'v'; + EXPECT_FALSE(IsConfigurationPresent(configurations, expected_config)); + + expected_config.language[0] = 'd'; + expected_config.language[1] = 'e'; + EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); + + expected_config.language[0] = 'f'; + expected_config.language[1] = 'r'; + EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); +} + +TEST_F(AssetManager2Test, GetResourceLocales) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({system_assets_.get(), basic_de_fr_assets_.get()}); + + std::set<std::string> locales = assetmanager.GetResourceLocales(); + + // We expect the locale sv from the system assets, and de and fr from basic_de_fr assets. + EXPECT_EQ(3u, locales.size()); + EXPECT_GT(locales.count("sv"), 0u); + EXPECT_GT(locales.count("de"), 0u); + EXPECT_GT(locales.count("fr"), 0u); + + locales = assetmanager.GetResourceLocales(true /*exclude_system*/); + // We expect the de and fr locales from basic_de_fr assets. + EXPECT_EQ(2u, locales.size()); + EXPECT_GT(locales.count("de"), 0u); + EXPECT_GT(locales.count("fr"), 0u); +} + +TEST_F(AssetManager2Test, GetResourceId) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({basic_assets_.get()}); + + EXPECT_EQ(basic::R::layout::main, + assetmanager.GetResourceId("com.android.basic:layout/main", "", "")); + EXPECT_EQ(basic::R::layout::main, + assetmanager.GetResourceId("layout/main", "", "com.android.basic")); + EXPECT_EQ(basic::R::layout::main, + assetmanager.GetResourceId("main", "layout", "com.android.basic")); +} + TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {} TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {} diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index f8aa61aa588a..756869f6377d 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -32,7 +32,8 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents)); - std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size()); + std::unique_ptr<const LoadedArsc> loaded_arsc = + LoadedArsc::Load(contents.data(), contents.size()); ASSERT_NE(nullptr, loaded_arsc); const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages(); @@ -58,7 +59,8 @@ TEST(LoadedArscTest, FindDefaultEntry) { ASSERT_TRUE( ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); - std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size()); + std::unique_ptr<const LoadedArsc> loaded_arsc = + LoadedArsc::Load(contents.data(), contents.size()); ASSERT_NE(nullptr, loaded_arsc); ResTable_config desired_config; @@ -80,7 +82,8 @@ TEST(LoadedArscTest, LoadSharedLibrary) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc", &contents)); - std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size()); + std::unique_ptr<const LoadedArsc> loaded_arsc = + LoadedArsc::Load(contents.data(), contents.size()); ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); @@ -101,7 +104,8 @@ TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/libclient/libclient.apk", "resources.arsc", &contents)); - std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size()); + std::unique_ptr<const LoadedArsc> loaded_arsc = + LoadedArsc::Load(contents.data(), contents.size()); ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); @@ -128,8 +132,8 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/appaslib/appaslib.apk", "resources.arsc", &contents)); - std::unique_ptr<LoadedArsc> loaded_arsc = - LoadedArsc::Load(contents.data(), contents.size(), true /*load_as_shared_library*/); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load( + contents.data(), contents.size(), false /*system*/, true /*load_as_shared_library*/); ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); @@ -143,7 +147,8 @@ TEST(LoadedArscTest, LoadFeatureSplit) { std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc", &contents)); - std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size()); + std::unique_ptr<const LoadedArsc> loaded_arsc = + LoadedArsc::Load(contents.data(), contents.size()); ASSERT_NE(nullptr, loaded_arsc); ResTable_config desired_config; diff --git a/libs/androidfw/tests/Theme_bench.cpp b/libs/androidfw/tests/Theme_bench.cpp index c471be6cd09d..594c39eb682f 100644 --- a/libs/androidfw/tests/Theme_bench.cpp +++ b/libs/androidfw/tests/Theme_bench.cpp @@ -28,7 +28,7 @@ constexpr const static uint32_t kStyleId = 0x01030237u; // android:style/Theme. constexpr const static uint32_t kAttrId = 0x01010030u; // android:attr/colorForeground static void BM_ThemeApplyStyleFramework(benchmark::State& state) { - std::unique_ptr<ApkAssets> apk = ApkAssets::Load(kFrameworkPath); + std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(kFrameworkPath); if (apk == nullptr) { state.SkipWithError("Failed to load assets"); return; @@ -62,7 +62,7 @@ static void BM_ThemeApplyStyleFrameworkOld(benchmark::State& state) { BENCHMARK(BM_ThemeApplyStyleFrameworkOld); static void BM_ThemeGetAttribute(benchmark::State& state) { - std::unique_ptr<ApkAssets> apk = ApkAssets::Load(kFrameworkPath); + std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(kFrameworkPath); AssetManager2 assets; assets.SetApkAssets({apk.get()}); diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp index 59cb18a36e6f..daed28b01a4f 100644 --- a/libs/androidfw/tests/Theme_test.cpp +++ b/libs/androidfw/tests/Theme_test.cpp @@ -46,10 +46,10 @@ class ThemeTest : public ::testing::Test { } protected: - std::unique_ptr<ApkAssets> style_assets_; - std::unique_ptr<ApkAssets> libclient_assets_; - std::unique_ptr<ApkAssets> lib_one_assets_; - std::unique_ptr<ApkAssets> lib_two_assets_; + std::unique_ptr<const ApkAssets> style_assets_; + std::unique_ptr<const ApkAssets> libclient_assets_; + std::unique_ptr<const ApkAssets> lib_one_assets_; + std::unique_ptr<const ApkAssets> lib_two_assets_; }; TEST_F(ThemeTest, EmptyTheme) { diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h index 9352b5c6629e..8e9741efd2a3 100644 --- a/libs/androidfw/tests/data/basic/R.h +++ b/libs/androidfw/tests/data/basic/R.h @@ -53,6 +53,8 @@ struct R { enum : uint32_t { number1 = 0x7f040000, number2 = 0x7f040001, + ref1 = 0x7f040002, + ref2 = 0x7f040003, // From feature number3 = 0x7f090000, diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk Binary files differindex 2c9771b18934..7ee6734e0d6a 100644 --- a/libs/androidfw/tests/data/basic/basic.apk +++ b/libs/androidfw/tests/data/basic/basic.apk diff --git a/libs/androidfw/tests/data/basic/basic_de_fr.apk b/libs/androidfw/tests/data/basic/basic_de_fr.apk Binary files differindex 04814440e0f8..e45258c6a005 100644 --- a/libs/androidfw/tests/data/basic/basic_de_fr.apk +++ b/libs/androidfw/tests/data/basic/basic_de_fr.apk diff --git a/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk b/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk Binary files differindex a8d06e7f3c19..4ae1a7c87a70 100644 --- a/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk +++ b/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk diff --git a/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk b/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk Binary files differindex d1dfb143f91b..a240d4c06c1d 100644 --- a/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk +++ b/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk diff --git a/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk b/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk Binary files differindex dca6f2fbc0ca..fd3d9b233084 100644 --- a/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk +++ b/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml index 11f6b8adbdba..638c9832ab4c 100644 --- a/libs/androidfw/tests/data/basic/res/values/values.xml +++ b/libs/androidfw/tests/data/basic/res/values/values.xml @@ -37,6 +37,12 @@ <public type="integer" name="number2" id="0x7f040001" /> <integer name="number2">@array/integerArray1</integer> + <public type="integer" name="ref1" id="0x7f040002" /> + <integer name="ref1">@integer/ref2</integer> + + <public type="integer" name="ref2" id="0x7f040003" /> + <integer name="ref2">12000</integer> + <public type="style" name="Theme1" id="0x7f050000" /> <style name="Theme1"> <item name="com.android.basic:attr1">100</item> |