diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index eb8d26adc7d74..b1f327c94f8eb 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -211,6 +211,8 @@ cc_test { "tests/data/**/*.apk", "tests/data/**/*.arsc", "tests/data/**/*.idmap", + ":FrameworkResourcesSparseTestApp", + ":FrameworkResourcesNotSparseTestApp", ], test_suites: ["device-tests"], } diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index d214e2dfef7bc..c90ec197b5efb 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -71,62 +71,6 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { ASSERT_TRUE(LoadedPackage::GetEntry(type.type, entry_index).has_value()); } -TEST(LoadedArscTest, LoadSparseEntryApp) { - std::string contents; - ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc", - &contents)); - - std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), - contents.length()); - ASSERT_THAT(loaded_arsc, NotNull()); - - const LoadedPackage* package = - loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9)); - ASSERT_THAT(package, NotNull()); - - const uint8_t type_index = get_type_id(sparse::R::integer::foo_9) - 1; - const uint16_t entry_index = get_entry_id(sparse::R::integer::foo_9); - - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_entries.size(), Ge(1u)); - - auto type = type_spec->type_entries[0]; - ASSERT_TRUE(LoadedPackage::GetEntry(type.type, entry_index).has_value()); -} - -TEST(LoadedArscTest, FindSparseEntryApp) { - std::string contents; - ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc", - &contents)); - - std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), - contents.length()); - ASSERT_THAT(loaded_arsc, NotNull()); - - const LoadedPackage* package = - loaded_arsc->GetPackageById(get_package_id(sparse::R::string::only_v26)); - ASSERT_THAT(package, NotNull()); - - const uint8_t type_index = get_type_id(sparse::R::string::only_v26) - 1; - const uint16_t entry_index = get_entry_id(sparse::R::string::only_v26); - - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_entries.size(), Ge(1u)); - - // Ensure that AAPT2 sparsely encoded the v26 config as expected. - auto type_entry = std::find_if( - type_spec->type_entries.begin(), type_spec->type_entries.end(), - [](const TypeSpec::TypeEntry& x) { return x.config.sdkVersion == 26; }); - ASSERT_NE(type_entry, type_spec->type_entries.end()); - ASSERT_NE(type_entry->type->flags & ResTable_type::FLAG_SPARSE, 0); - - // Test fetching a resource with only sparsely encoded configs by name. - auto id = package->FindEntryByName(u"string", u"only_v26"); - ASSERT_EQ(id.value(), fix_package_id(sparse::R::string::only_v26, 0)); -} - TEST(LoadedArscTest, LoadSharedLibrary) { std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc", @@ -404,4 +348,84 @@ TEST(LoadedArscTest, LoadCustomLoader) { // sizeof(Res_value) might not be backwards compatible. // TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); } +class LoadedArscParameterizedTest : + public testing::TestWithParam { +}; + +TEST_P(LoadedArscParameterizedTest, LoadSparseEntryApp) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetParam(), "resources.arsc", &contents)); + + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); + ASSERT_THAT(loaded_arsc, NotNull()); + + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9)); + ASSERT_THAT(package, NotNull()); + + const uint8_t type_index = get_type_id(sparse::R::integer::foo_9) - 1; + const uint16_t entry_index = get_entry_id(sparse::R::integer::foo_9); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_entries.size(), Ge(1u)); + + auto type = type_spec->type_entries[0]; + ASSERT_TRUE(LoadedPackage::GetEntry(type.type, entry_index).has_value()); +} + +TEST_P(LoadedArscParameterizedTest, FindSparseEntryApp) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetParam(), "resources.arsc", &contents)); + + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), + contents.length()); + ASSERT_THAT(loaded_arsc, NotNull()); + + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(sparse::R::string::only_land)); + ASSERT_THAT(package, NotNull()); + + const uint8_t type_index = get_type_id(sparse::R::string::only_land) - 1; + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_entries.size(), Ge(1u)); + + // Type Entry with default orientation is not sparse encoded because the ratio of + // populated entries to total entries is above threshold. + // Only find out default locale because Soong build system will introduce pseudo + // locales for the apk generated at runtime. + auto type_entry_default = std::find_if( + type_spec->type_entries.begin(), type_spec->type_entries.end(), + [] (const TypeSpec::TypeEntry& x) { return x.config.orientation == 0 && + x.config.locale == 0; }); + ASSERT_NE(type_entry_default, type_spec->type_entries.end()); + ASSERT_EQ(type_entry_default->type->flags & ResTable_type::FLAG_SPARSE, 0); + + // Type Entry with land orientation is sparse encoded as expected. + // Only find out default locale because Soong build system will introduce pseudo + // locales for the apk generated at runtime. + auto type_entry_land = std::find_if( + type_spec->type_entries.begin(), type_spec->type_entries.end(), + [](const TypeSpec::TypeEntry& x) { return x.config.orientation == + ResTable_config::ORIENTATION_LAND && + x.config.locale == 0; }); + ASSERT_NE(type_entry_land, type_spec->type_entries.end()); + ASSERT_NE(type_entry_land->type->flags & ResTable_type::FLAG_SPARSE, 0); + + // Test fetching a resource with only sparsely encoded configs by name. + auto id = package->FindEntryByName(u"string", u"only_land"); + ASSERT_EQ(id.value(), fix_package_id(sparse::R::string::only_land, 0)); +} + +INSTANTIATE_TEST_SUITE_P( + FrameWorkResourcesLoadedArscTests, + LoadedArscParameterizedTest, + ::testing::Values( + base::GetExecutableDirectory() + "/tests/data/sparse/sparse.apk", + base::GetExecutableDirectory() + "/FrameworkResourcesSparseTestApp.apk" + )); + } // namespace android diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp index 9aeb00c47e63f..fbf70981f2dec 100644 --- a/libs/androidfw/tests/ResTable_test.cpp +++ b/libs/androidfw/tests/ResTable_test.cpp @@ -15,6 +15,7 @@ */ #include "androidfw/ResourceTypes.h" +#include "android-base/file.h" #include #include @@ -41,34 +42,6 @@ TEST(ResTableTest, ShouldLoadSuccessfully) { ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); } -TEST(ResTableTest, ShouldLoadSparseEntriesSuccessfully) { - std::string contents; - ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc", - &contents)); - - ResTable table; - ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); - - ResTable_config config; - memset(&config, 0, sizeof(config)); - config.sdkVersion = 26; - table.setParameters(&config); - - String16 name(u"com.android.sparse:integer/foo_9"); - uint32_t flags; - uint32_t resid = - table.identifierForName(name.string(), name.size(), nullptr, 0, nullptr, 0, &flags); - ASSERT_NE(0u, resid); - - Res_value val; - ResTable_config selected_config; - ASSERT_GE( - table.getResource(resid, &val, false /*mayBeBag*/, 0u /*density*/, &flags, &selected_config), - 0); - EXPECT_EQ(Res_value::TYPE_INT_DEC, val.dataType); - EXPECT_EQ(900u, val.data); -} - TEST(ResTableTest, SimpleTypeIsRetrievedCorrectly) { std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", @@ -476,4 +449,43 @@ TEST(ResTableTest, TruncatedEncodeLength) { ASSERT_FALSE(invalid_pool->stringAt(invalid_val.data).has_value()); } +class ResTableParameterizedTest : + public testing::TestWithParam { +}; + +TEST_P(ResTableParameterizedTest, ShouldLoadSparseEntriesSuccessfully) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetParam(), "resources.arsc", &contents)); + + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); + + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.orientation = ResTable_config::ORIENTATION_LAND; + table.setParameters(&config); + + String16 name(u"com.android.sparse:integer/foo_9"); + uint32_t flags; + uint32_t resid = + table.identifierForName(name.string(), name.size(), nullptr, 0, nullptr, 0, &flags); + ASSERT_NE(0u, resid); + + Res_value val; + ResTable_config selected_config; + ASSERT_GE( + table.getResource(resid, &val, false /*mayBeBag*/, 0u /*density*/, &flags, &selected_config), + 0); + EXPECT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + EXPECT_EQ(900u, val.data); +} + +INSTANTIATE_TEST_SUITE_P( + FrameWorkResourcesResTableTests, + ResTableParameterizedTest, + ::testing::Values( + base::GetExecutableDirectory() + "/tests/data/sparse/sparse.apk", + base::GetExecutableDirectory() + "/FrameworkResourcesSparseTestApp.apk" + )); + } // namespace android diff --git a/libs/androidfw/tests/SparseEntry_bench.cpp b/libs/androidfw/tests/SparseEntry_bench.cpp index c9b4ad8af2780..fffeeb802873d 100644 --- a/libs/androidfw/tests/SparseEntry_bench.cpp +++ b/libs/androidfw/tests/SparseEntry_bench.cpp @@ -16,6 +16,7 @@ #include "androidfw/AssetManager.h" #include "androidfw/ResourceTypes.h" +#include "android-base/file.h" #include "BenchmarkHelpers.h" #include "data/sparse/R.h" @@ -24,40 +25,74 @@ namespace sparse = com::android::sparse; namespace android { +static void BM_SparseEntryGetResourceHelper(const std::vector& paths, + uint32_t resid, benchmark::State& state, void (*GetResourceBenchmarkFunc)( + const std::vector&, const ResTable_config*, + uint32_t, benchmark::State&)){ + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.orientation = ResTable_config::ORIENTATION_LAND; + GetResourceBenchmarkFunc(paths, &config, resid, state); +} + static void BM_SparseEntryGetResourceOldSparse(benchmark::State& state, uint32_t resid) { - ResTable_config config; - memset(&config, 0, sizeof(config)); - config.sdkVersion = 26; - GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/sparse.apk"}, &config, resid, state); + BM_SparseEntryGetResourceHelper({GetTestDataPath() + "/sparse/sparse.apk"}, resid, + state, &GetResourceBenchmarkOld); } BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldSparse, Small, sparse::R::integer::foo_9); BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldSparse, Large, sparse::R::string::foo_999); static void BM_SparseEntryGetResourceOldNotSparse(benchmark::State& state, uint32_t resid) { - ResTable_config config; - memset(&config, 0, sizeof(config)); - config.sdkVersion = 26; - GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/not_sparse.apk"}, &config, resid, state); + BM_SparseEntryGetResourceHelper({GetTestDataPath() + "/sparse/not_sparse.apk"}, resid, + state, &GetResourceBenchmarkOld); } BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldNotSparse, Small, sparse::R::integer::foo_9); BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldNotSparse, Large, sparse::R::string::foo_999); static void BM_SparseEntryGetResourceSparse(benchmark::State& state, uint32_t resid) { - ResTable_config config; - memset(&config, 0, sizeof(config)); - config.sdkVersion = 26; - GetResourceBenchmark({GetTestDataPath() + "/sparse/sparse.apk"}, &config, resid, state); + BM_SparseEntryGetResourceHelper({GetTestDataPath() + "/sparse/sparse.apk"}, resid, + state, &GetResourceBenchmark); } BENCHMARK_CAPTURE(BM_SparseEntryGetResourceSparse, Small, sparse::R::integer::foo_9); BENCHMARK_CAPTURE(BM_SparseEntryGetResourceSparse, Large, sparse::R::string::foo_999); static void BM_SparseEntryGetResourceNotSparse(benchmark::State& state, uint32_t resid) { - ResTable_config config; - memset(&config, 0, sizeof(config)); - config.sdkVersion = 26; - GetResourceBenchmark({GetTestDataPath() + "/sparse/not_sparse.apk"}, &config, resid, state); + BM_SparseEntryGetResourceHelper({GetTestDataPath() + "/sparse/not_sparse.apk"}, resid, + state, &GetResourceBenchmark); } BENCHMARK_CAPTURE(BM_SparseEntryGetResourceNotSparse, Small, sparse::R::integer::foo_9); BENCHMARK_CAPTURE(BM_SparseEntryGetResourceNotSparse, Large, sparse::R::string::foo_999); +static void BM_SparseEntryGetResourceOldSparseRuntime(benchmark::State& state, uint32_t resid) { + BM_SparseEntryGetResourceHelper({base::GetExecutableDirectory() + + "/FrameworkResourcesSparseTestApp.apk"}, resid, state, + &GetResourceBenchmarkOld); +} +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldSparseRuntime, Small, sparse::R::integer::foo_9); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldSparseRuntime, Large, sparse::R::string::foo_999); + +static void BM_SparseEntryGetResourceOldNotSparseRuntime(benchmark::State& state, uint32_t resid) { + BM_SparseEntryGetResourceHelper({base::GetExecutableDirectory() + + "/FrameworkResourcesNotSparseTestApp.apk"}, resid, state, + &GetResourceBenchmarkOld); +} +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldNotSparseRuntime, Small, sparse::R::integer::foo_9); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldNotSparseRuntime, Large, sparse::R::string::foo_999); + +static void BM_SparseEntryGetResourceSparseRuntime(benchmark::State& state, uint32_t resid) { + BM_SparseEntryGetResourceHelper({base::GetExecutableDirectory() + + "/FrameworkResourcesSparseTestApp.apk"}, resid, state, + &GetResourceBenchmark); +} +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceSparseRuntime, Small, sparse::R::integer::foo_9); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceSparseRuntime, Large, sparse::R::string::foo_999); + +static void BM_SparseEntryGetResourceNotSparseRuntime(benchmark::State& state, uint32_t resid) { + BM_SparseEntryGetResourceHelper({base::GetExecutableDirectory() + + "/FrameworkResourcesNotSparseTestApp.apk"}, resid, state, + &GetResourceBenchmark); +} +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceNotSparseRuntime, Small, sparse::R::integer::foo_9); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceNotSparseRuntime, Large, sparse::R::string::foo_999); + } // namespace android diff --git a/libs/androidfw/tests/data/sparse/Android.bp b/libs/androidfw/tests/data/sparse/Android.bp new file mode 100644 index 0000000000000..0fed79e397844 --- /dev/null +++ b/libs/androidfw/tests/data/sparse/Android.bp @@ -0,0 +1,14 @@ +android_test_helper_app { + name: "FrameworkResourcesSparseTestApp", + sdk_version: "current", + min_sdk_version: "32", + aaptflags: [ + "--enable-sparse-encoding", + ], +} + +android_test_helper_app { + name: "FrameworkResourcesNotSparseTestApp", + sdk_version: "current", + min_sdk_version: "32", +} diff --git a/libs/androidfw/tests/data/sparse/AndroidManifest.xml b/libs/androidfw/tests/data/sparse/AndroidManifest.xml index 27911b62447a9..9c23a7227631e 100644 --- a/libs/androidfw/tests/data/sparse/AndroidManifest.xml +++ b/libs/androidfw/tests/data/sparse/AndroidManifest.xml @@ -17,4 +17,5 @@ + diff --git a/libs/androidfw/tests/data/sparse/R.h b/libs/androidfw/tests/data/sparse/R.h index 2492dbf33f4a1..a66e1af150c4a 100644 --- a/libs/androidfw/tests/data/sparse/R.h +++ b/libs/androidfw/tests/data/sparse/R.h @@ -42,7 +42,7 @@ struct R { struct string { enum : uint32_t { foo_999 = 0x7f0203e7, - only_v26 = 0x7f0203e8 + only_land = 0x7f0203e8 }; }; }; diff --git a/libs/androidfw/tests/data/sparse/gen_strings.sh b/libs/androidfw/tests/data/sparse/gen_strings.sh index 4ea5468c7df9f..114ecbb7d8606 100755 --- a/libs/androidfw/tests/data/sparse/gen_strings.sh +++ b/libs/androidfw/tests/data/sparse/gen_strings.sh @@ -1,20 +1,20 @@ #!/bin/bash OUTPUT_default=res/values/strings.xml -OUTPUT_v26=res/values-v26/strings.xml +OUTPUT_land=res/values-land/strings.xml echo "" > $OUTPUT_default -echo "" > $OUTPUT_v26 +echo "" > $OUTPUT_land for i in {0..999} do echo " $i" >> $OUTPUT_default if [ "$(($i % 3))" -eq "0" ] then - echo " $(($i * 10))" >> $OUTPUT_v26 + echo " $(($i * 10))" >> $OUTPUT_land fi done echo "" >> $OUTPUT_default -echo " only v26" >> $OUTPUT_v26 -echo "" >> $OUTPUT_v26 +echo " only land" >> $OUTPUT_land +echo "" >> $OUTPUT_land diff --git a/libs/androidfw/tests/data/sparse/not_sparse.apk b/libs/androidfw/tests/data/sparse/not_sparse.apk index b08a621195c0a..4d4d4a8490337 100644 Binary files a/libs/androidfw/tests/data/sparse/not_sparse.apk and b/libs/androidfw/tests/data/sparse/not_sparse.apk differ diff --git a/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml b/libs/androidfw/tests/data/sparse/res/values-land/strings.xml similarity index 99% rename from libs/androidfw/tests/data/sparse/res/values-v26/strings.xml rename to libs/androidfw/tests/data/sparse/res/values-land/strings.xml index d116087ec3c06..66222c327416c 100644 --- a/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml +++ b/libs/androidfw/tests/data/sparse/res/values-land/strings.xml @@ -333,5 +333,5 @@ 9930 9960 9990 - only v26 + only land diff --git a/libs/androidfw/tests/data/sparse/res/values-v26/values.xml b/libs/androidfw/tests/data/sparse/res/values-land/values.xml similarity index 100% rename from libs/androidfw/tests/data/sparse/res/values-v26/values.xml rename to libs/androidfw/tests/data/sparse/res/values-land/values.xml diff --git a/libs/androidfw/tests/data/sparse/sparse.apk b/libs/androidfw/tests/data/sparse/sparse.apk index 9fd01fbf2941e..0f2d75a62b96a 100644 Binary files a/libs/androidfw/tests/data/sparse/sparse.apk and b/libs/androidfw/tests/data/sparse/sparse.apk differ