From da431a22da38f9c4085b5d71ed9a9c6122c6a5a6 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Thu, 29 Dec 2016 16:08:16 -0500 Subject: libandroidfw: Add new support for shared libraries This adds support for shared resource libraries in the new ResTable/AssetManager implementation. The dynamic package map encoded in resources.arsc is parsed and stored with LoadedArsc, and combined to form a resolved table in AssetManager2. Benchmarks show that this implementation is an order of magnitude faster on angler-userdebug (make libandroidfw_benchmarks). Test: libandroidfw_tests Change-Id: I57c80248728b63b162bf8269ac9495b53c3e7fa0 --- libs/androidfw/tests/ApkAssets_test.cpp | 19 +++ libs/androidfw/tests/AssetManager2_bench.cpp | 133 +++++++++++---------- libs/androidfw/tests/AssetManager2_test.cpp | 122 +++++++++++++++++-- libs/androidfw/tests/LoadedArsc_test.cpp | 81 +++++++++++-- libs/androidfw/tests/ResTable_test.cpp | 6 +- libs/androidfw/tests/Theme_test.cpp | 46 +++++++ libs/androidfw/tests/data/lib/AndroidManifest.xml | 20 ---- libs/androidfw/tests/data/lib/R.h | 45 ------- libs/androidfw/tests/data/lib/build | 20 ---- libs/androidfw/tests/data/lib/lib.apk | Bin 1221 -> 0 bytes .../androidfw/tests/data/lib/res/values/values.xml | 25 ---- .../tests/data/lib_one/AndroidManifest.xml | 20 ++++ libs/androidfw/tests/data/lib_one/R.h | 51 ++++++++ libs/androidfw/tests/data/lib_one/build | 20 ++++ libs/androidfw/tests/data/lib_one/lib_one.apk | Bin 0 -> 1414 bytes .../tests/data/lib_one/res/values/values.xml | 32 +++++ .../tests/data/lib_two/AndroidManifest.xml | 19 +++ libs/androidfw/tests/data/lib_two/R.h | 39 ++++++ libs/androidfw/tests/data/lib_two/build | 20 ++++ libs/androidfw/tests/data/lib_two/lib_two.apk | Bin 0 -> 1106 bytes .../tests/data/lib_two/res/values/values.xml | 23 ++++ .../tests/data/libclient/AndroidManifest.xml | 19 +++ libs/androidfw/tests/data/libclient/R.h | 52 ++++++++ libs/androidfw/tests/data/libclient/build | 30 +++++ libs/androidfw/tests/data/libclient/libclient.apk | Bin 0 -> 1982 bytes .../tests/data/libclient/res/values/values.xml | 35 ++++++ 26 files changed, 687 insertions(+), 190 deletions(-) delete mode 100644 libs/androidfw/tests/data/lib/AndroidManifest.xml delete mode 100644 libs/androidfw/tests/data/lib/R.h delete mode 100755 libs/androidfw/tests/data/lib/build delete mode 100644 libs/androidfw/tests/data/lib/lib.apk delete mode 100644 libs/androidfw/tests/data/lib/res/values/values.xml create mode 100644 libs/androidfw/tests/data/lib_one/AndroidManifest.xml create mode 100644 libs/androidfw/tests/data/lib_one/R.h create mode 100755 libs/androidfw/tests/data/lib_one/build create mode 100644 libs/androidfw/tests/data/lib_one/lib_one.apk create mode 100644 libs/androidfw/tests/data/lib_one/res/values/values.xml create mode 100644 libs/androidfw/tests/data/lib_two/AndroidManifest.xml create mode 100644 libs/androidfw/tests/data/lib_two/R.h create mode 100755 libs/androidfw/tests/data/lib_two/build create mode 100644 libs/androidfw/tests/data/lib_two/lib_two.apk create mode 100644 libs/androidfw/tests/data/lib_two/res/values/values.xml create mode 100644 libs/androidfw/tests/data/libclient/AndroidManifest.xml create mode 100644 libs/androidfw/tests/data/libclient/R.h create mode 100755 libs/androidfw/tests/data/libclient/build create mode 100644 libs/androidfw/tests/data/libclient/libclient.apk create mode 100644 libs/androidfw/tests/data/libclient/res/values/values.xml (limited to 'libs/androidfw/tests') diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index 3a1fc8fa0d3f..02037120f098 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -26,9 +26,28 @@ namespace android { TEST(ApkAssetsTest, LoadApk) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); ASSERT_NE(nullptr, loaded_apk); + EXPECT_NE(nullptr, loaded_apk->GetLoadedArsc()); std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); ASSERT_NE(nullptr, asset); } +TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { + std::unique_ptr loaded_apk = + ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); + ASSERT_NE(nullptr, loaded_apk); + const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); + ASSERT_NE(nullptr, loaded_arsc); + ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); + EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic()); + + loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); + ASSERT_NE(nullptr, loaded_apk); + + loaded_arsc = loaded_apk->GetLoadedArsc(); + ASSERT_NE(nullptr, loaded_arsc); + ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); + EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic()); +} + } // namespace android diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp index 9ff947807a1e..b3c2dc34cdcf 100644 --- a/libs/androidfw/tests/AssetManager2_bench.cpp +++ b/libs/androidfw/tests/AssetManager2_bench.cpp @@ -16,6 +16,7 @@ #include "benchmark/benchmark.h" +#include "android-base/stringprintf.h" #include "androidfw/ApkAssets.h" #include "androidfw/AssetManager.h" #include "androidfw/AssetManager2.h" @@ -23,10 +24,12 @@ #include "TestHelpers.h" #include "data/basic/R.h" +#include "data/libclient/R.h" #include "data/styles/R.h" -namespace basic = com::android::basic; namespace app = com::android::app; +namespace basic = com::android::basic; +namespace libclient = com::android::libclient; namespace android { @@ -78,101 +81,108 @@ static void BM_AssetManagerLoadFrameworkAssetsOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerLoadFrameworkAssetsOld); -static void BM_AssetManagerGetResource(benchmark::State& state) { - std::unique_ptr apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - if (apk == nullptr) { - state.SkipWithError("Failed to load assets"); - return; +static void GetResourceBenchmark(const std::vector& paths, + const ResTable_config* config, uint32_t resid, + benchmark::State& state) { + std::vector> apk_assets; + std::vector apk_assets_ptrs; + for (const std::string& path : paths) { + std::unique_ptr apk = ApkAssets::Load(path); + if (apk == nullptr) { + state.SkipWithError(base::StringPrintf("Failed to load assets %s", path.c_str()).c_str()); + return; + } + apk_assets_ptrs.push_back(apk.get()); + apk_assets.push_back(std::move(apk)); } - AssetManager2 assets; - assets.SetApkAssets({apk.get()}); + AssetManager2 assetmanager; + assetmanager.SetApkAssets(apk_assets_ptrs); + if (config != nullptr) { + assetmanager.SetConfiguration(*config); + } Res_value value; ResTable_config selected_config; uint32_t flags; while (state.KeepRunning()) { - assets.GetResource(basic::R::integer::number1, false /* may_be_bag */, - 0u /* density_override */, &value, &selected_config, &flags); + assetmanager.GetResource(resid, false /* may_be_bag */, 0u /* density_override */, &value, + &selected_config, &flags); } } -BENCHMARK(BM_AssetManagerGetResource); -static void BM_AssetManagerGetResourceOld(benchmark::State& state) { - AssetManager assets; - if (!assets.addAssetPath(String8((GetTestDataPath() + "/basic/basic.apk").data()), - nullptr /* cookie */, false /* appAsLib */, - false /* isSystemAssets */)) { - state.SkipWithError("Failed to load assets"); - return; +static void GetResourceBenchmarkOld(const std::vector& paths, + const ResTable_config* config, uint32_t resid, + benchmark::State& state) { + AssetManager assetmanager; + for (const std::string& path : paths) { + if (!assetmanager.addAssetPath(String8(path.c_str()), nullptr /* cookie */, + false /* appAsLib */, false /* isSystemAssets */)) { + state.SkipWithError(base::StringPrintf("Failed to load assets %s", path.c_str()).c_str()); + return; + } } - const ResTable& table = assets.getResources(true); + if (config != nullptr) { + assetmanager.setConfiguration(*config); + } + + const ResTable& table = assetmanager.getResources(true); Res_value value; ResTable_config selected_config; uint32_t flags; while (state.KeepRunning()) { - table.getResource(basic::R::integer::number1, &value, false /* may_be_bag */, - 0u /* density_override */, &flags, &selected_config); + table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags, + &selected_config); } } + +static void BM_AssetManagerGetResource(benchmark::State& state) { + GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, + basic::R::integer::number1, state); +} +BENCHMARK(BM_AssetManagerGetResource); + +static void BM_AssetManagerGetResourceOld(benchmark::State& state) { + GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, + basic::R::integer::number1, state); +} BENCHMARK(BM_AssetManagerGetResourceOld); -constexpr static const uint32_t kStringOkId = 0x0104000au; +static void BM_AssetManagerGetLibraryResource(benchmark::State& state) { + GetResourceBenchmark( + {GetTestDataPath() + "/lib_two/lib_two.apk", GetTestDataPath() + "/lib_one/lib_one.apk", + GetTestDataPath() + "/libclient/libclient.apk"}, + nullptr /*config*/, libclient::R::string::foo_one, state); +} +BENCHMARK(BM_AssetManagerGetLibraryResource); -static void BM_AssetManagerGetResourceFrameworkLocale(benchmark::State& state) { - std::unique_ptr apk = ApkAssets::Load(kFrameworkPath); - if (apk == nullptr) { - state.SkipWithError("Failed to load assets"); - return; - } +static void BM_AssetManagerGetLibraryResourceOld(benchmark::State& state) { + GetResourceBenchmarkOld( + {GetTestDataPath() + "/lib_two/lib_two.apk", GetTestDataPath() + "/lib_one/lib_one.apk", + GetTestDataPath() + "/libclient/libclient.apk"}, + nullptr /*config*/, libclient::R::string::foo_one, state); +} +BENCHMARK(BM_AssetManagerGetLibraryResourceOld); - AssetManager2 assets; - assets.SetApkAssets({apk.get()}); +constexpr static const uint32_t kStringOkId = 0x0104000au; +static void BM_AssetManagerGetResourceFrameworkLocale(benchmark::State& state) { ResTable_config config; memset(&config, 0, sizeof(config)); memcpy(config.language, "fr", 2); - assets.SetConfiguration(config); - - Res_value value; - ResTable_config selected_config; - uint32_t flags; - - while (state.KeepRunning()) { - assets.GetResource(kStringOkId, false /* may_be_bag */, 0u /* density_override */, &value, - &selected_config, &flags); - } + GetResourceBenchmark({kFrameworkPath}, &config, kStringOkId, state); } BENCHMARK(BM_AssetManagerGetResourceFrameworkLocale); static void BM_AssetManagerGetResourceFrameworkLocaleOld(benchmark::State& state) { - AssetManager assets; - if (!assets.addAssetPath(String8((GetTestDataPath() + "/basic/basic.apk").data()), - nullptr /* cookie */, false /* appAsLib */, - false /* isSystemAssets */)) { - state.SkipWithError("Failed to load assets"); - return; - } - ResTable_config config; memset(&config, 0, sizeof(config)); memcpy(config.language, "fr", 2); - assets.setConfiguration(config, nullptr); - - const ResTable& table = assets.getResources(true); - - Res_value value; - ResTable_config selected_config; - uint32_t flags; - - while (state.KeepRunning()) { - table.getResource(kStringOkId, &value, false /* may_be_bag */, 0u /* density_override */, - &flags, &selected_config); - } + GetResourceBenchmarkOld({kFrameworkPath}, &config, kStringOkId, state); } BENCHMARK(BM_AssetManagerGetResourceFrameworkLocaleOld); @@ -202,8 +212,7 @@ BENCHMARK(BM_AssetManagerGetBag); static void BM_AssetManagerGetBagOld(benchmark::State& state) { AssetManager assets; if (!assets.addAssetPath(String8((GetTestDataPath() + "/styles/styles.apk").data()), - nullptr /* cookie */, false /* appAsLib */, - false /* isSystemAssets */)) { + nullptr /*cookie*/, false /*appAsLib*/, false /*isSystemAssets*/)) { state.SkipWithError("Failed to load assets"); return; } diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 39c5381feb04..543456afa2f4 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -20,11 +20,19 @@ #include "android-base/logging.h" #include "TestHelpers.h" +#include "data/appaslib/R.h" #include "data/basic/R.h" +#include "data/lib_one/R.h" +#include "data/lib_two/R.h" +#include "data/libclient/R.h" #include "data/styles/R.h" -namespace basic = com::android::basic; namespace app = com::android::app; +namespace appaslib = com::android::appaslib::app; +namespace basic = com::android::basic; +namespace lib_one = com::android::lib_one; +namespace lib_two = com::android::lib_two; +namespace libclient = com::android::libclient; namespace android { @@ -39,15 +47,31 @@ class AssetManager2Test : public ::testing::Test { style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); ASSERT_NE(nullptr, style_assets_); + + lib_one_assets_ = ApkAssets::Load(GetTestDataPath() + "/lib_one/lib_one.apk"); + ASSERT_NE(nullptr, lib_one_assets_); + + lib_two_assets_ = ApkAssets::Load(GetTestDataPath() + "/lib_two/lib_two.apk"); + ASSERT_NE(nullptr, lib_two_assets_); + + libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk"); + ASSERT_NE(nullptr, libclient_assets_); + + appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); + ASSERT_NE(nullptr, appaslib_assets_); } protected: std::unique_ptr basic_assets_; std::unique_ptr basic_de_fr_assets_; std::unique_ptr style_assets_; + std::unique_ptr lib_one_assets_; + std::unique_ptr lib_two_assets_; + std::unique_ptr libclient_assets_; + std::unique_ptr appaslib_assets_; }; -TEST_F(AssetManager2Test, FindsResourcesFromSingleApkAssets) { +TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) { ResTable_config desired_config; memset(&desired_config, 0, sizeof(desired_config)); desired_config.language[0] = 'd'; @@ -77,7 +101,7 @@ TEST_F(AssetManager2Test, FindsResourcesFromSingleApkAssets) { EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); } -TEST_F(AssetManager2Test, FindsResourcesFromMultipleApkAssets) { +TEST_F(AssetManager2Test, FindsResourceFromMultipleApkAssets) { ResTable_config desired_config; memset(&desired_config, 0, sizeof(desired_config)); desired_config.language[0] = 'd'; @@ -99,7 +123,7 @@ TEST_F(AssetManager2Test, FindsResourcesFromMultipleApkAssets) { // Came from our de_fr ApkAssets. EXPECT_EQ(1, cookie); - // The configuration is german. + // The configuration is German. EXPECT_EQ('d', selected_config.language[0]); EXPECT_EQ('e', selected_config.language[1]); @@ -107,7 +131,72 @@ TEST_F(AssetManager2Test, FindsResourcesFromMultipleApkAssets) { EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); } -TEST_F(AssetManager2Test, FindsBagResourcesFromSingleApkAssets) { +TEST_F(AssetManager2Test, FindsResourceFromSharedLibrary) { + AssetManager2 assetmanager; + + // libclient is built with lib_one and then lib_two in order. + // Reverse the order to test that proper package ID re-assignment is happening. + assetmanager.SetApkAssets( + {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); + + Res_value value; + ResTable_config selected_config; + uint32_t flags; + + ApkAssetsCookie cookie = + assetmanager.GetResource(libclient::R::string::foo_one, false /*may_be_bag*/, + 0 /*density_override*/, &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); + + // Reference comes from libclient. + EXPECT_EQ(2, cookie); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); + + // Lookup the reference. + cookie = assetmanager.GetResource(value.data, false /* may_be_bag */, 0 /* density_override*/, + &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(1, cookie); + EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); + EXPECT_EQ(std::string("Foo from lib_one"), + GetStringFromPool(assetmanager.GetStringPoolForCookie(cookie), value.data)); + + cookie = assetmanager.GetResource(libclient::R::string::foo_two, false /*may_be_bag*/, + 0 /*density_override*/, &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); + + // Reference comes from libclient. + EXPECT_EQ(2, cookie); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); + + // Lookup the reference. + cookie = assetmanager.GetResource(value.data, false /* may_be_bag */, 0 /* density_override*/, + &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(0, cookie); + EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); + EXPECT_EQ(std::string("Foo from lib_two"), + GetStringFromPool(assetmanager.GetStringPoolForCookie(cookie), value.data)); +} + +TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({appaslib_assets_.get()}); + + // The appaslib package will have been assigned the package ID 0x02. + + Res_value value; + ResTable_config selected_config; + uint32_t flags; + ApkAssetsCookie cookie = assetmanager.GetResource( + util::fix_package_id(appaslib::R::integer::number1, 0x02), 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(util::fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data); +} + +TEST_F(AssetManager2Test, FindsBagResourceFromSingleApkAssets) { AssetManager2 assetmanager; assetmanager.SetApkAssets({basic_assets_.get()}); @@ -128,6 +217,27 @@ TEST_F(AssetManager2Test, FindsBagResourcesFromSingleApkAssets) { EXPECT_EQ(0, bag->entries[2].cookie); } +TEST_F(AssetManager2Test, FindsBagResourceFromMultipleApkAssets) {} + +TEST_F(AssetManager2Test, FindsBagResourceFromSharedLibrary) { + AssetManager2 assetmanager; + + // libclient is built with lib_one and then lib_two in order. + // Reverse the order to test that proper package ID re-assignment is happening. + assetmanager.SetApkAssets( + {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); + + const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::Theme); + ASSERT_NE(nullptr, bag); + ASSERT_GE(bag->entry_count, 2u); + + // First two attributes come from lib_one. + EXPECT_EQ(1, bag->entries[0].cookie); + EXPECT_EQ(0x03, util::get_package_id(bag->entries[0].key)); + EXPECT_EQ(1, bag->entries[1].cookie); + EXPECT_EQ(0x03, util::get_package_id(bag->entries[1].key)); +} + TEST_F(AssetManager2Test, MergesStylesWithParentFromSingleApkAssets) { AssetManager2 assetmanager; assetmanager.SetApkAssets({style_assets_.get()}); @@ -181,8 +291,6 @@ TEST_F(AssetManager2Test, MergesStylesWithParentFromSingleApkAssets) { EXPECT_EQ(0, bag_two->entries[4].cookie); } -TEST_F(AssetManager2Test, FindsBagResourcesFromMultipleApkAssets) {} - 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 47b3894f0398..045507e2e277 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -16,21 +16,18 @@ #include "androidfw/LoadedArsc.h" -#include "android-base/file.h" -#include "android-base/logging.h" -#include "android-base/macros.h" - #include "TestHelpers.h" #include "data/basic/R.h" +#include "data/libclient/R.h" #include "data/styles/R.h" namespace app = com::android::app; namespace basic = com::android::basic; +namespace libclient = com::android::libclient; namespace android { TEST(LoadedArscTest, LoadSinglePackageArsc) { - base::ScopedLogSeverity _log(base::LogSeverity::DEBUG); std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents)); @@ -38,11 +35,16 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), contents.size()); ASSERT_NE(nullptr, loaded_arsc); + const std::vector>& packages = loaded_arsc->GetPackages(); + ASSERT_EQ(1u, packages.size()); + EXPECT_EQ(std::string("com.android.app"), packages[0]->GetPackageName()); + EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + ResTable_config config; memset(&config, 0, sizeof(config)); config.sdkVersion = 24; - LoadedArsc::Entry entry; + LoadedArscEntry entry; ResTable_config selected_config; uint32_t flags; @@ -52,7 +54,6 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { } TEST(LoadedArscTest, FindDefaultEntry) { - base::ScopedLogSeverity _log(base::LogSeverity::DEBUG); std::string contents; ASSERT_TRUE( ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); @@ -65,7 +66,7 @@ TEST(LoadedArscTest, FindDefaultEntry) { desired_config.language[0] = 'd'; desired_config.language[1] = 'e'; - LoadedArsc::Entry entry; + LoadedArscEntry entry; ResTable_config selected_config; uint32_t flags; @@ -74,6 +75,70 @@ TEST(LoadedArscTest, FindDefaultEntry) { ASSERT_NE(nullptr, entry.entry); } +TEST(LoadedArscTest, LoadSharedLibrary) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc", + &contents)); + + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), contents.size()); + ASSERT_NE(nullptr, loaded_arsc); + + const auto& packages = loaded_arsc->GetPackages(); + ASSERT_EQ(1u, packages.size()); + + EXPECT_TRUE(packages[0]->IsDynamic()); + EXPECT_EQ(std::string("com.android.lib_one"), packages[0]->GetPackageName()); + EXPECT_EQ(0, packages[0]->GetPackageId()); + + const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); + + // The library has no dependencies. + ASSERT_TRUE(dynamic_pkg_map.empty()); +} + +TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/libclient/libclient.apk", + "resources.arsc", &contents)); + + std::unique_ptr loaded_arsc = LoadedArsc::Load(contents.data(), contents.size()); + ASSERT_NE(nullptr, loaded_arsc); + + const auto& packages = loaded_arsc->GetPackages(); + ASSERT_EQ(1u, packages.size()); + + EXPECT_FALSE(packages[0]->IsDynamic()); + EXPECT_EQ(std::string("com.android.libclient"), packages[0]->GetPackageName()); + EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + + const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); + + // The library has two dependencies. + ASSERT_EQ(2u, dynamic_pkg_map.size()); + + EXPECT_EQ(std::string("com.android.lib_one"), dynamic_pkg_map[0].package_name); + EXPECT_EQ(0x02, dynamic_pkg_map[0].package_id); + + EXPECT_EQ(std::string("com.android.lib_two"), dynamic_pkg_map[1].package_name); + EXPECT_EQ(0x03, dynamic_pkg_map[1].package_id); +} + +TEST(LoadedArscTest, LoadAppAsSharedLibrary) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/appaslib/appaslib.apk", + "resources.arsc", &contents)); + + std::unique_ptr loaded_arsc = + LoadedArsc::Load(contents.data(), contents.size(), true /*load_as_shared_library*/); + ASSERT_NE(nullptr, loaded_arsc); + + const auto& packages = loaded_arsc->GetPackages(); + ASSERT_EQ(1u, packages.size()); + + EXPECT_TRUE(packages[0]->IsDynamic()); + EXPECT_EQ(0x7f, packages[0]->GetPackageId()); +} + // structs with size fields (like Res_value, ResTable_entry) should be // backwards and forwards compatible (aka checking the size field against // sizeof(Res_value) might not be backwards compatible. diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp index b151f3f96496..ad1cd2b289d6 100644 --- a/libs/androidfw/tests/ResTable_test.cpp +++ b/libs/androidfw/tests/ResTable_test.cpp @@ -25,10 +25,10 @@ #include "TestHelpers.h" #include "data/basic/R.h" -#include "data/lib/R.h" +#include "data/lib_one/R.h" namespace basic = com::android::basic; -namespace lib = com::android::lib; +namespace lib = com::android::lib_one; namespace android { @@ -119,7 +119,7 @@ TEST(ResTableTest, ParentThemeIsAppliedCorrectly) { TEST(ResTableTest, LibraryThemeIsAppliedCorrectly) { std::string contents; - ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib/lib.apk", + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc", &contents)); ResTable table; diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp index c0011b6d6e89..59cb18a36e6f 100644 --- a/libs/androidfw/tests/Theme_test.cpp +++ b/libs/androidfw/tests/Theme_test.cpp @@ -19,9 +19,13 @@ #include "android-base/logging.h" #include "TestHelpers.h" +#include "data/lib_one/R.h" +#include "data/libclient/R.h" #include "data/styles/R.h" namespace app = com::android::app; +namespace lib_one = com::android::lib_one; +namespace libclient = com::android::libclient; namespace android { @@ -30,10 +34,22 @@ class ThemeTest : public ::testing::Test { void SetUp() override { style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); ASSERT_NE(nullptr, style_assets_); + + libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk"); + ASSERT_NE(nullptr, libclient_assets_); + + lib_one_assets_ = ApkAssets::Load(GetTestDataPath() + "/lib_one/lib_one.apk"); + ASSERT_NE(nullptr, lib_one_assets_); + + lib_two_assets_ = ApkAssets::Load(GetTestDataPath() + "/lib_two/lib_two.apk"); + ASSERT_NE(nullptr, lib_two_assets_); } protected: std::unique_ptr style_assets_; + std::unique_ptr libclient_assets_; + std::unique_ptr lib_one_assets_; + std::unique_ptr lib_two_assets_; }; TEST_F(ThemeTest, EmptyTheme) { @@ -174,6 +190,36 @@ TEST_F(ThemeTest, MultipleThemesOverlaidForced) { EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); } +TEST_F(ThemeTest, ResolveDynamicAttributesAndReferencesToSharedLibrary) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets( + {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); + + std::unique_ptr theme = assetmanager.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(libclient::R::style::Theme, false /*force*/)); + + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie; + + // The attribute should be resolved to the final value. + cookie = theme->GetAttribute(libclient::R::attr::foo, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(700u, value.data); + EXPECT_EQ(static_cast(ResTable_typeSpec::SPEC_PUBLIC), flags); + + // The reference should be resolved to a TYPE_REFERENCE. + cookie = theme->GetAttribute(libclient::R::attr::bar, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); + + // lib_one is assigned package ID 0x03. + EXPECT_EQ(3u, util::get_package_id(value.data)); + EXPECT_EQ(util::get_type_id(lib_one::R::string::foo), util::get_type_id(value.data)); + EXPECT_EQ(util::get_entry_id(lib_one::R::string::foo), util::get_entry_id(value.data)); +} + TEST_F(ThemeTest, CopyThemeSameAssetManager) { AssetManager2 assetmanager; assetmanager.SetApkAssets({style_assets_.get()}); diff --git a/libs/androidfw/tests/data/lib/AndroidManifest.xml b/libs/androidfw/tests/data/lib/AndroidManifest.xml deleted file mode 100644 index 02f5d3efabea..000000000000 --- a/libs/androidfw/tests/data/lib/AndroidManifest.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - diff --git a/libs/androidfw/tests/data/lib/R.h b/libs/androidfw/tests/data/lib/R.h deleted file mode 100644 index bb22d22f90e1..000000000000 --- a/libs/androidfw/tests/data/lib/R.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TEST_DATA_LIB_R_H_ -#define TEST_DATA_LIB_R_H_ - -#include - -namespace com { -namespace android { -namespace lib { - -struct R { - struct attr { - enum : uint32_t { - attr1 = 0x02010000, // default - attr2 = 0x02010001, // default - }; - }; - - struct style { - enum : uint32_t { - Theme = 0x02020000, // default - }; - }; -}; - -} // namespace lib -} // namespace android -} // namespace com - -#endif // TEST_DATA_R_H_ diff --git a/libs/androidfw/tests/data/lib/build b/libs/androidfw/tests/data/lib/build deleted file mode 100755 index 5c3d02c850bf..000000000000 --- a/libs/androidfw/tests/data/lib/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2014 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -set -e - -aapt package -M AndroidManifest.xml -S res -F lib.apk -f --shared-lib diff --git a/libs/androidfw/tests/data/lib/lib.apk b/libs/androidfw/tests/data/lib/lib.apk deleted file mode 100644 index 44c27c79ae7c..000000000000 Binary files a/libs/androidfw/tests/data/lib/lib.apk and /dev/null differ diff --git a/libs/androidfw/tests/data/lib/res/values/values.xml b/libs/androidfw/tests/data/lib/res/values/values.xml deleted file mode 100644 index 51e3a407c538..000000000000 --- a/libs/androidfw/tests/data/lib/res/values/values.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - diff --git a/libs/androidfw/tests/data/lib_one/AndroidManifest.xml b/libs/androidfw/tests/data/lib_one/AndroidManifest.xml new file mode 100644 index 000000000000..860adf758f92 --- /dev/null +++ b/libs/androidfw/tests/data/lib_one/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/libs/androidfw/tests/data/lib_one/R.h b/libs/androidfw/tests/data/lib_one/R.h new file mode 100644 index 000000000000..fcaeb8dceffb --- /dev/null +++ b/libs/androidfw/tests/data/lib_one/R.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TEST_DATA_LIB_ONE_R_H_ +#define TEST_DATA_LIB_ONE_R_H_ + +#include + +namespace com { +namespace android { +namespace lib_one { + +struct R { + struct attr { + enum : uint32_t { + attr1 = 0x02010000, // default + attr2 = 0x02010001, // default + }; + }; + + struct style { + enum : uint32_t { + Theme = 0x02020000, // default + }; + }; + + struct string { + enum : uint32_t { + foo = 0x02030000, // default + }; + }; +}; + +} // namespace lib_one +} // namespace android +} // namespace com + +#endif // TEST_DATA_LIB_ONE_R_H_ diff --git a/libs/androidfw/tests/data/lib_one/build b/libs/androidfw/tests/data/lib_one/build new file mode 100755 index 000000000000..c6adf0b1dda6 --- /dev/null +++ b/libs/androidfw/tests/data/lib_one/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +aapt package -M AndroidManifest.xml -S res -F lib_one.apk -f --shared-lib diff --git a/libs/androidfw/tests/data/lib_one/lib_one.apk b/libs/androidfw/tests/data/lib_one/lib_one.apk new file mode 100644 index 000000000000..f287554d674d Binary files /dev/null and b/libs/androidfw/tests/data/lib_one/lib_one.apk differ diff --git a/libs/androidfw/tests/data/lib_one/res/values/values.xml b/libs/androidfw/tests/data/lib_one/res/values/values.xml new file mode 100644 index 000000000000..752b7e912ab6 --- /dev/null +++ b/libs/androidfw/tests/data/lib_one/res/values/values.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + Foo from lib_one + diff --git a/libs/androidfw/tests/data/lib_two/AndroidManifest.xml b/libs/androidfw/tests/data/lib_two/AndroidManifest.xml new file mode 100644 index 000000000000..4b131e55a8a4 --- /dev/null +++ b/libs/androidfw/tests/data/lib_two/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/libs/androidfw/tests/data/lib_two/R.h b/libs/androidfw/tests/data/lib_two/R.h new file mode 100644 index 000000000000..c04a9d3b4de0 --- /dev/null +++ b/libs/androidfw/tests/data/lib_two/R.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TEST_DATA_LIB_TWO_R_H_ +#define TEST_DATA_LIB_TWO_R_H_ + +#include + +namespace com { +namespace android { +namespace lib_two { + +struct R { + struct string { + enum : uint32_t { + LibraryString = 0x02020000, // default + foo = 0x02020001, // default + }; + }; +}; + +} // namespace lib_two +} // namespace android +} // namespace com + +#endif // TEST_DATA_LIB_TWO_R_H_ diff --git a/libs/androidfw/tests/data/lib_two/build b/libs/androidfw/tests/data/lib_two/build new file mode 100755 index 000000000000..fd75e1dc7428 --- /dev/null +++ b/libs/androidfw/tests/data/lib_two/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +aapt package -M AndroidManifest.xml -S res -F lib_two.apk -f --shared-lib diff --git a/libs/androidfw/tests/data/lib_two/lib_two.apk b/libs/androidfw/tests/data/lib_two/lib_two.apk new file mode 100644 index 000000000000..ad44f9c21e31 Binary files /dev/null and b/libs/androidfw/tests/data/lib_two/lib_two.apk differ diff --git a/libs/androidfw/tests/data/lib_two/res/values/values.xml b/libs/androidfw/tests/data/lib_two/res/values/values.xml new file mode 100644 index 000000000000..f4eea2610cab --- /dev/null +++ b/libs/androidfw/tests/data/lib_two/res/values/values.xml @@ -0,0 +1,23 @@ + + + + + + Hi from library two + + + Foo from lib_two + diff --git a/libs/androidfw/tests/data/libclient/AndroidManifest.xml b/libs/androidfw/tests/data/libclient/AndroidManifest.xml new file mode 100644 index 000000000000..8436383dbd81 --- /dev/null +++ b/libs/androidfw/tests/data/libclient/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/libs/androidfw/tests/data/libclient/R.h b/libs/androidfw/tests/data/libclient/R.h new file mode 100644 index 000000000000..43d1f9bb68e7 --- /dev/null +++ b/libs/androidfw/tests/data/libclient/R.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TEST_DATA_LIB_R_H_ +#define TEST_DATA_LIB_R_H_ + +#include + +namespace com { +namespace android { +namespace libclient { + +struct R { + struct attr { + enum : uint32_t { + foo = 0x7f010000, // default + bar = 0x7f010001, // default + }; + }; + + struct style { + enum : uint32_t { + Theme = 0x7f020000, // default + }; + }; + + struct string { + enum : uint32_t { + foo_one = 0x7f030000, // default + foo_two = 0x7f030001, // default + }; + }; +}; + +} // namespace libclient +} // namespace android +} // namespace com + +#endif // TEST_DATA_R_H_ diff --git a/libs/androidfw/tests/data/libclient/build b/libs/androidfw/tests/data/libclient/build new file mode 100755 index 000000000000..08310e3c6eb8 --- /dev/null +++ b/libs/androidfw/tests/data/libclient/build @@ -0,0 +1,30 @@ +#!/bin/bash +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +PATH_TO_FRAMEWORK_RES=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/android.jar +PATH_TO_LIB_ONE=../lib_one/lib_one.apk +PATH_TO_LIB_TWO=../lib_two/lib_two.apk + +aapt package \ + -M AndroidManifest.xml \ + -S res \ + -I $PATH_TO_FRAMEWORK_RES \ + -I $PATH_TO_LIB_ONE \ + -I $PATH_TO_LIB_TWO \ + -F libclient.apk -f diff --git a/libs/androidfw/tests/data/libclient/libclient.apk b/libs/androidfw/tests/data/libclient/libclient.apk new file mode 100644 index 000000000000..17990248e862 Binary files /dev/null and b/libs/androidfw/tests/data/libclient/libclient.apk differ diff --git a/libs/androidfw/tests/data/libclient/res/values/values.xml b/libs/androidfw/tests/data/libclient/res/values/values.xml new file mode 100644 index 000000000000..fead7c323767 --- /dev/null +++ b/libs/androidfw/tests/data/libclient/res/values/values.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + @com.android.lib_one:string/foo + + + @com.android.lib_two:string/foo + -- cgit v1.2.3-59-g8ed1b