bdc0ae1247
Fabricated RROs do not provide assets but are stored on disk. Ensure that the path to the frro is returned when querying for the ApkAssets path (which is mostly for debug purposes). Bug: 181338216 Test: enable frro, use cmd overlay lookup to see path in resolution Change-Id: Ibf9b1bf0a995325affbf084c71b1e87c5682e734
838 lines
31 KiB
C++
838 lines
31 KiB
C++
/*
|
|
* Copyright (C) 2016 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.
|
|
*/
|
|
|
|
#include "androidfw/AssetManager2.h"
|
|
#include "androidfw/AssetManager.h"
|
|
|
|
#include "TestHelpers.h"
|
|
#include "android-base/file.h"
|
|
#include "android-base/logging.h"
|
|
#include "androidfw/ResourceUtils.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"
|
|
#include "data/system/R.h"
|
|
|
|
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;
|
|
|
|
using ::testing::Eq;
|
|
using ::testing::NotNull;
|
|
using ::testing::StrEq;
|
|
|
|
namespace android {
|
|
|
|
class AssetManager2Test : public ::testing::Test {
|
|
public:
|
|
void SetUp() override {
|
|
// Move to the test data directory so the idmap can locate the overlay APK.
|
|
std::string original_path = base::GetExecutableDirectory();
|
|
chdir(GetTestDataPath().c_str());
|
|
|
|
basic_assets_ = ApkAssets::Load("basic/basic.apk");
|
|
ASSERT_NE(nullptr, basic_assets_);
|
|
|
|
basic_de_fr_assets_ = ApkAssets::Load("basic/basic_de_fr.apk");
|
|
ASSERT_NE(nullptr, basic_de_fr_assets_);
|
|
|
|
basic_xhdpi_assets_ = ApkAssets::Load("basic/basic_xhdpi-v4.apk");
|
|
ASSERT_NE(nullptr, basic_de_fr_assets_);
|
|
|
|
basic_xxhdpi_assets_ = ApkAssets::Load("basic/basic_xxhdpi-v4.apk");
|
|
ASSERT_NE(nullptr, basic_de_fr_assets_);
|
|
|
|
style_assets_ = ApkAssets::Load("styles/styles.apk");
|
|
ASSERT_NE(nullptr, style_assets_);
|
|
|
|
lib_one_assets_ = ApkAssets::Load("lib_one/lib_one.apk");
|
|
ASSERT_NE(nullptr, lib_one_assets_);
|
|
|
|
lib_two_assets_ = ApkAssets::Load("lib_two/lib_two.apk");
|
|
ASSERT_NE(nullptr, lib_two_assets_);
|
|
|
|
libclient_assets_ = ApkAssets::Load("libclient/libclient.apk");
|
|
ASSERT_NE(nullptr, libclient_assets_);
|
|
|
|
appaslib_assets_ = ApkAssets::Load("appaslib/appaslib.apk", PROPERTY_DYNAMIC);
|
|
ASSERT_NE(nullptr, appaslib_assets_);
|
|
|
|
system_assets_ = ApkAssets::Load("system/system.apk", PROPERTY_SYSTEM);
|
|
ASSERT_NE(nullptr, system_assets_);
|
|
|
|
app_assets_ = ApkAssets::Load("app/app.apk");
|
|
ASSERT_THAT(app_assets_, NotNull());
|
|
|
|
overlay_assets_ = ApkAssets::LoadOverlay("overlay/overlay.idmap");
|
|
ASSERT_NE(nullptr, overlay_assets_);
|
|
|
|
overlayable_assets_ = ApkAssets::Load("overlayable/overlayable.apk");
|
|
ASSERT_THAT(overlayable_assets_, NotNull());
|
|
chdir(original_path.c_str());
|
|
}
|
|
|
|
protected:
|
|
std::unique_ptr<const ApkAssets> basic_assets_;
|
|
std::unique_ptr<const ApkAssets> basic_de_fr_assets_;
|
|
std::unique_ptr<const ApkAssets> basic_xhdpi_assets_;
|
|
std::unique_ptr<const ApkAssets> basic_xxhdpi_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_;
|
|
std::unique_ptr<const ApkAssets> app_assets_;
|
|
std::unique_ptr<const ApkAssets> overlay_assets_;
|
|
std::unique_ptr<const ApkAssets> overlayable_assets_;
|
|
};
|
|
|
|
TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) {
|
|
ResTable_config desired_config;
|
|
memset(&desired_config, 0, sizeof(desired_config));
|
|
desired_config.language[0] = 'd';
|
|
desired_config.language[1] = 'e';
|
|
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetConfiguration(desired_config);
|
|
assetmanager.SetApkAssets({basic_assets_.get()});
|
|
|
|
auto value = assetmanager.GetResource(basic::R::string::test1);
|
|
ASSERT_TRUE(value.has_value());
|
|
|
|
// Came from our ApkAssets.
|
|
EXPECT_EQ(0, value->cookie);
|
|
|
|
// It is the default config.
|
|
EXPECT_EQ(0, value->config.language[0]);
|
|
EXPECT_EQ(0, value->config.language[1]);
|
|
|
|
// It is a string.
|
|
EXPECT_EQ(Res_value::TYPE_STRING, value->type);
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, FindsResourceFromMultipleApkAssets) {
|
|
ResTable_config desired_config;
|
|
memset(&desired_config, 0, sizeof(desired_config));
|
|
desired_config.language[0] = 'd';
|
|
desired_config.language[1] = 'e';
|
|
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetConfiguration(desired_config);
|
|
assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()});
|
|
|
|
auto value = assetmanager.GetResource(basic::R::string::test1);
|
|
ASSERT_TRUE(value.has_value());
|
|
|
|
// Came from our de_fr ApkAssets.
|
|
EXPECT_EQ(1, value->cookie);
|
|
|
|
// The configuration is German.
|
|
EXPECT_EQ('d', value->config.language[0]);
|
|
EXPECT_EQ('e', value->config.language[1]);
|
|
|
|
// It is a string.
|
|
EXPECT_EQ(Res_value::TYPE_STRING, value->type);
|
|
}
|
|
|
|
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()});
|
|
|
|
auto value = assetmanager.GetResource(libclient::R::string::foo_one);
|
|
ASSERT_TRUE(value.has_value());
|
|
|
|
// Reference comes from libclient.
|
|
EXPECT_EQ(2, value->cookie);
|
|
EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
|
|
|
|
// Lookup the reference.
|
|
value = assetmanager.GetResource(value->data);
|
|
ASSERT_TRUE(value.has_value());
|
|
EXPECT_EQ(1, value->cookie);
|
|
EXPECT_EQ(Res_value::TYPE_STRING, value->type);
|
|
EXPECT_EQ(std::string("Foo from lib_one"),
|
|
GetStringFromPool(assetmanager.GetStringPoolForCookie(value->cookie), value->data));
|
|
|
|
value = assetmanager.GetResource(libclient::R::string::foo_two);
|
|
ASSERT_TRUE(value.has_value());
|
|
|
|
// Reference comes from libclient.
|
|
EXPECT_EQ(2, value->cookie);
|
|
EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
|
|
|
|
// Lookup the reference.
|
|
value = assetmanager.GetResource(value->data);
|
|
ASSERT_TRUE(value.has_value());
|
|
EXPECT_EQ(0, value->cookie);
|
|
EXPECT_EQ(Res_value::TYPE_STRING, value->type);
|
|
EXPECT_EQ(std::string("Foo from lib_two"),
|
|
GetStringFromPool(assetmanager.GetStringPoolForCookie(value->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.
|
|
auto value = assetmanager.GetResource(fix_package_id(appaslib::R::integer::number1, 0x02));
|
|
ASSERT_TRUE(value.has_value());
|
|
EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
|
|
EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value->data);
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, AssignsOverlayPackageIdLast) {
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetApkAssets(
|
|
{overlayable_assets_.get(), overlay_assets_.get(), lib_one_assets_.get()});
|
|
|
|
auto apk_assets = assetmanager.GetApkAssets();
|
|
ASSERT_EQ(3, apk_assets.size());
|
|
ASSERT_EQ(overlayable_assets_.get(), apk_assets[0]);
|
|
ASSERT_EQ(overlay_assets_.get(), apk_assets[1]);
|
|
ASSERT_EQ(lib_one_assets_.get(), apk_assets[2]);
|
|
|
|
auto get_first_package_id = [&assetmanager](const ApkAssets* apkAssets) -> uint8_t {
|
|
return assetmanager.GetAssignedPackageId(apkAssets->GetLoadedArsc()->GetPackages()[0].get());
|
|
};
|
|
|
|
ASSERT_EQ(0x7f, get_first_package_id(overlayable_assets_.get()));
|
|
ASSERT_EQ(0x03, get_first_package_id(overlay_assets_.get()));
|
|
ASSERT_EQ(0x02, get_first_package_id(lib_one_assets_.get()));
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, GetSharedLibraryResourceName) {
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetApkAssets({lib_one_assets_.get()});
|
|
|
|
auto name = assetmanager.GetResourceName(lib_one::R::string::foo);
|
|
ASSERT_TRUE(name.has_value());
|
|
ASSERT_EQ("com.android.lib_one:string/foo", ToFormattedResourceString(*name));
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, GetResourceNameNonMatchingConfig) {
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetApkAssets({basic_de_fr_assets_.get()});
|
|
|
|
auto value = assetmanager.GetResourceName(basic::R::string::test1);
|
|
ASSERT_TRUE(value.has_value());
|
|
EXPECT_EQ("com.android.basic:string/test1", ToFormattedResourceString(*value));
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, GetResourceTypeSpecFlags) {
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetApkAssets({basic_de_fr_assets_.get()});
|
|
|
|
auto value = assetmanager.GetResourceTypeSpecFlags(basic::R::string::test1);
|
|
ASSERT_TRUE(value.has_value());
|
|
EXPECT_EQ(ResTable_typeSpec::SPEC_PUBLIC | ResTable_config::CONFIG_LOCALE, *value);
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, FindsBagResourceFromSingleApkAssets) {
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetApkAssets({basic_assets_.get()});
|
|
|
|
auto bag = assetmanager.GetBag(basic::R::array::integerArray1);
|
|
ASSERT_TRUE(bag.has_value());
|
|
|
|
ASSERT_EQ(3u, (*bag)->entry_count);
|
|
|
|
EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), (*bag)->entries[0].value.dataType);
|
|
EXPECT_EQ(1u, (*bag)->entries[0].value.data);
|
|
EXPECT_EQ(0, (*bag)->entries[0].cookie);
|
|
|
|
EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), (*bag)->entries[1].value.dataType);
|
|
EXPECT_EQ(2u, (*bag)->entries[1].value.data);
|
|
EXPECT_EQ(0, (*bag)->entries[1].cookie);
|
|
|
|
EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), (*bag)->entries[2].value.dataType);
|
|
EXPECT_EQ(3u, (*bag)->entries[2].value.data);
|
|
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()});
|
|
|
|
auto bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03));
|
|
ASSERT_TRUE(bag.has_value());
|
|
|
|
ASSERT_GE((*bag)->entry_count, 2u);
|
|
|
|
// First two attributes come from lib_one.
|
|
EXPECT_EQ(1, (*bag)->entries[0].cookie);
|
|
EXPECT_EQ(0x03, get_package_id((*bag)->entries[0].key));
|
|
EXPECT_EQ(1, (*bag)->entries[1].cookie);
|
|
EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key));
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, FindsBagResourceFromMultipleSharedLibraries) {
|
|
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()});
|
|
|
|
auto bag = assetmanager.GetBag(libclient::R::style::ThemeMultiLib);
|
|
ASSERT_TRUE(bag.has_value());
|
|
ASSERT_EQ((*bag)->entry_count, 2u);
|
|
|
|
// First attribute comes from lib_two.
|
|
EXPECT_EQ(2, (*bag)->entries[0].cookie);
|
|
EXPECT_EQ(0x02, get_package_id((*bag)->entries[0].key));
|
|
|
|
// The next two attributes come from lib_one.
|
|
EXPECT_EQ(2, (*bag)->entries[1].cookie);
|
|
EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key));
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) {
|
|
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()});
|
|
|
|
auto bag = assetmanager.GetBag(libclient::R::style::Theme);
|
|
ASSERT_TRUE(bag.has_value());
|
|
ASSERT_GE((*bag)->entry_count, 2u);
|
|
|
|
// First two attributes come from lib_one.
|
|
EXPECT_EQ(1, (*bag)->entries[0].cookie);
|
|
EXPECT_EQ(0x03, get_package_id((*bag)->entries[0].key));
|
|
EXPECT_EQ(1, (*bag)->entries[1].cookie);
|
|
EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key));
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, MergesStylesWithParentFromSingleApkAssets) {
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetApkAssets({style_assets_.get()});
|
|
|
|
auto bag_one = assetmanager.GetBag(app::R::style::StyleOne);
|
|
ASSERT_TRUE(bag_one.has_value());
|
|
ASSERT_EQ(2u, (*bag_one)->entry_count);
|
|
|
|
EXPECT_EQ(app::R::attr::attr_one, (*bag_one)->entries[0].key);
|
|
EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_one)->entries[0].value.dataType);
|
|
EXPECT_EQ(1u, (*bag_one)->entries[0].value.data);
|
|
EXPECT_EQ(0, (*bag_one)->entries[0].cookie);
|
|
|
|
EXPECT_EQ(app::R::attr::attr_two, (*bag_one)->entries[1].key);
|
|
EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_one)->entries[1].value.dataType);
|
|
EXPECT_EQ(2u, (*bag_one)->entries[1].value.data);
|
|
EXPECT_EQ(0, (*bag_one)->entries[1].cookie);
|
|
|
|
auto bag_two = assetmanager.GetBag(app::R::style::StyleTwo);
|
|
ASSERT_TRUE(bag_two.has_value());
|
|
ASSERT_EQ(6u, (*bag_two)->entry_count);
|
|
|
|
// attr_one is inherited from StyleOne.
|
|
EXPECT_EQ(app::R::attr::attr_one, (*bag_two)->entries[0].key);
|
|
EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_two)->entries[0].value.dataType);
|
|
EXPECT_EQ(1u, (*bag_two)->entries[0].value.data);
|
|
EXPECT_EQ(0, (*bag_two)->entries[0].cookie);
|
|
EXPECT_EQ(app::R::style::StyleOne, (*bag_two)->entries[0].style);
|
|
|
|
// attr_two should be overridden from StyleOne by StyleTwo.
|
|
EXPECT_EQ(app::R::attr::attr_two, (*bag_two)->entries[1].key);
|
|
EXPECT_EQ(Res_value::TYPE_STRING, (*bag_two)->entries[1].value.dataType);
|
|
EXPECT_EQ(0, (*bag_two)->entries[1].cookie);
|
|
EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[1].style);
|
|
EXPECT_EQ(std::string("string"), GetStringFromPool(assetmanager.GetStringPoolForCookie(0),
|
|
(*bag_two)->entries[1].value.data));
|
|
|
|
// The rest are new attributes.
|
|
|
|
EXPECT_EQ(app::R::attr::attr_three, (*bag_two)->entries[2].key);
|
|
EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, (*bag_two)->entries[2].value.dataType);
|
|
EXPECT_EQ(app::R::attr::attr_indirect, (*bag_two)->entries[2].value.data);
|
|
EXPECT_EQ(0, (*bag_two)->entries[2].cookie);
|
|
EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[2].style);
|
|
|
|
EXPECT_EQ(app::R::attr::attr_five, (*bag_two)->entries[3].key);
|
|
EXPECT_EQ(Res_value::TYPE_REFERENCE, (*bag_two)->entries[3].value.dataType);
|
|
EXPECT_EQ(app::R::string::string_one, (*bag_two)->entries[3].value.data);
|
|
EXPECT_EQ(0, (*bag_two)->entries[3].cookie);
|
|
EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[3].style);
|
|
|
|
EXPECT_EQ(app::R::attr::attr_indirect, (*bag_two)->entries[4].key);
|
|
EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_two)->entries[4].value.dataType);
|
|
EXPECT_EQ(3u, (*bag_two)->entries[4].value.data);
|
|
EXPECT_EQ(0, (*bag_two)->entries[4].cookie);
|
|
EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[4].style);
|
|
|
|
EXPECT_EQ(app::R::attr::attr_empty, (*bag_two)->entries[5].key);
|
|
EXPECT_EQ(Res_value::TYPE_NULL, (*bag_two)->entries[5].value.dataType);
|
|
EXPECT_EQ(Res_value::DATA_NULL_EMPTY, (*bag_two)->entries[5].value.data);
|
|
EXPECT_EQ(0, (*bag_two)->entries[5].cookie);
|
|
EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[5].style);
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, MergeStylesCircularDependency) {
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetApkAssets({style_assets_.get()});
|
|
|
|
// GetBag should stop traversing the parents of styles when a circular
|
|
// dependency is detected
|
|
auto bag = assetmanager.GetBag(app::R::style::StyleFour);
|
|
ASSERT_TRUE(bag.has_value());
|
|
ASSERT_EQ(3u, (*bag)->entry_count);
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, ResolveReferenceToResource) {
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetApkAssets({basic_assets_.get()});
|
|
|
|
auto value = assetmanager.GetResource(basic::R::integer::ref1);
|
|
ASSERT_TRUE(value.has_value());
|
|
EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
|
|
EXPECT_EQ(basic::R::integer::ref2, value->data);
|
|
|
|
auto result = assetmanager.ResolveReference(*value);
|
|
ASSERT_TRUE(result.has_value());
|
|
EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
|
|
EXPECT_EQ(12000u, value->data);
|
|
EXPECT_EQ(basic::R::integer::ref2, value->resid);
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, ResolveReferenceToBag) {
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetApkAssets({basic_assets_.get()});
|
|
|
|
auto value = assetmanager.GetResource(basic::R::integer::number2, true /*may_be_bag*/);
|
|
ASSERT_TRUE(value.has_value());
|
|
EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
|
|
EXPECT_EQ(basic::R::array::integerArray1, value->data);
|
|
|
|
auto result = assetmanager.ResolveReference(*value);
|
|
ASSERT_TRUE(result.has_value());
|
|
EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
|
|
EXPECT_EQ(basic::R::array::integerArray1, value->data);
|
|
EXPECT_EQ(basic::R::array::integerArray1, value->resid);
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, ResolveDeepIdReference) {
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetApkAssets({basic_assets_.get()});
|
|
|
|
// Set up the resource ids
|
|
auto high_ref = assetmanager.GetResourceId("@id/high_ref", "values", "com.android.basic");
|
|
ASSERT_TRUE(high_ref.has_value());
|
|
|
|
auto middle_ref = assetmanager.GetResourceId("@id/middle_ref", "values", "com.android.basic");
|
|
ASSERT_TRUE(middle_ref.has_value());
|
|
|
|
auto low_ref = assetmanager.GetResourceId("@id/low_ref", "values", "com.android.basic");
|
|
ASSERT_TRUE(low_ref.has_value());
|
|
|
|
// Retrieve the most shallow resource
|
|
auto value = assetmanager.GetResource(*high_ref);
|
|
ASSERT_TRUE(value.has_value());
|
|
EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
|
|
EXPECT_EQ(*middle_ref, value->data);;
|
|
|
|
// Check that resolving the reference resolves to the deepest id
|
|
auto result = assetmanager.ResolveReference(*value);
|
|
ASSERT_TRUE(result.has_value());
|
|
EXPECT_EQ(*low_ref, value->resid);
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, DensityOverride) {
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetApkAssets({basic_assets_.get(), basic_xhdpi_assets_.get(),
|
|
basic_xxhdpi_assets_.get()});
|
|
assetmanager.SetConfiguration({
|
|
.density = ResTable_config::DENSITY_XHIGH,
|
|
.sdkVersion = 21,
|
|
});
|
|
|
|
auto value = assetmanager.GetResource(basic::R::string::density, false /*may_be_bag*/);
|
|
ASSERT_TRUE(value.has_value());
|
|
EXPECT_EQ(Res_value::TYPE_STRING, value->type);
|
|
EXPECT_EQ("xhdpi", GetStringFromPool(assetmanager.GetStringPoolForCookie(value->cookie),
|
|
value->data));
|
|
|
|
value = assetmanager.GetResource(basic::R::string::density, false /*may_be_bag*/,
|
|
ResTable_config::DENSITY_XXHIGH);
|
|
ASSERT_TRUE(value.has_value());
|
|
EXPECT_EQ(Res_value::TYPE_STRING, value->type);
|
|
EXPECT_EQ("xxhdpi", GetStringFromPool(assetmanager.GetStringPoolForCookie(value->cookie),
|
|
value->data));
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) {
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetApkAssets({basic_assets_.get()});
|
|
|
|
// Create some kind of value that is NOT a reference.
|
|
AssetManager2::SelectedValue value{};
|
|
value.cookie = 1;
|
|
value.type = Res_value::TYPE_STRING;
|
|
value.resid = basic::R::string::test1;
|
|
|
|
auto result = assetmanager.ResolveReference(value);
|
|
ASSERT_TRUE(result.has_value());
|
|
EXPECT_EQ(1, value.cookie);
|
|
EXPECT_EQ(basic::R::string::test1, value.resid);
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, ResolveReferenceMissingResourceDoNotCacheFlags) {
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetApkAssets({basic_assets_.get()});
|
|
{
|
|
AssetManager2::SelectedValue value{};
|
|
value.data = basic::R::string::test1;
|
|
value.type = Res_value::TYPE_REFERENCE;
|
|
value.flags = ResTable_config::CONFIG_KEYBOARD;
|
|
|
|
auto result = assetmanager.ResolveReference(value);
|
|
ASSERT_TRUE(result.has_value());
|
|
EXPECT_EQ(Res_value::TYPE_STRING, value.type);
|
|
EXPECT_EQ(0, value.cookie);
|
|
EXPECT_EQ(basic::R::string::test1, value.resid);
|
|
EXPECT_EQ(ResTable_typeSpec::SPEC_PUBLIC | ResTable_config::CONFIG_KEYBOARD, value.flags);
|
|
}
|
|
{
|
|
AssetManager2::SelectedValue value{};
|
|
value.data = basic::R::string::test1;
|
|
value.type = Res_value::TYPE_REFERENCE;
|
|
value.flags = ResTable_config::CONFIG_COLOR_MODE;
|
|
|
|
auto result = assetmanager.ResolveReference(value);
|
|
ASSERT_TRUE(result.has_value());
|
|
EXPECT_EQ(Res_value::TYPE_STRING, value.type);
|
|
EXPECT_EQ(0, value.cookie);
|
|
EXPECT_EQ(basic::R::string::test1, value.resid);
|
|
EXPECT_EQ(ResTable_typeSpec::SPEC_PUBLIC | ResTable_config::CONFIG_COLOR_MODE, value.flags);
|
|
}
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, ResolveReferenceMissingResource) {
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetApkAssets({basic_assets_.get()});
|
|
|
|
const uint32_t kMissingResId = 0x8001ffff;
|
|
AssetManager2::SelectedValue value{};
|
|
value.type = Res_value::TYPE_REFERENCE;
|
|
value.data = kMissingResId;
|
|
|
|
auto result = assetmanager.ResolveReference(value);
|
|
ASSERT_FALSE(result.has_value());
|
|
EXPECT_EQ(Res_value::TYPE_REFERENCE, value.type);
|
|
EXPECT_EQ(kMissingResId, value.data);
|
|
EXPECT_EQ(kMissingResId, value.resid);
|
|
EXPECT_EQ(-1, value.cookie);
|
|
EXPECT_EQ(0, value.flags);
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, ResolveReferenceMissingResourceLib) {
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetApkAssets({libclient_assets_.get()});
|
|
|
|
AssetManager2::SelectedValue value{};
|
|
value.type = Res_value::TYPE_REFERENCE;
|
|
value.data = libclient::R::string::foo_one;
|
|
|
|
auto result = assetmanager.ResolveReference(value);
|
|
ASSERT_TRUE(result.has_value());
|
|
EXPECT_EQ(Res_value::TYPE_DYNAMIC_REFERENCE, value.type);
|
|
EXPECT_EQ(lib_one::R::string::foo, value.data);
|
|
EXPECT_EQ(libclient::R::string::foo_one, value.resid);
|
|
EXPECT_EQ(0, value.cookie);
|
|
EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value.flags);
|
|
}
|
|
|
|
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()});
|
|
|
|
auto configurations = assetmanager.GetResourceConfigurations();
|
|
ASSERT_TRUE(configurations.has_value());
|
|
|
|
// 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 */);
|
|
ASSERT_TRUE(configurations.has_value());
|
|
|
|
// 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()});
|
|
|
|
auto resid = assetmanager.GetResourceId("com.android.basic:layout/main", "", "");
|
|
ASSERT_TRUE(resid.has_value());
|
|
EXPECT_EQ(basic::R::layout::main, *resid);
|
|
|
|
resid = assetmanager.GetResourceId("layout/main", "", "com.android.basic");
|
|
ASSERT_TRUE(resid.has_value());
|
|
EXPECT_EQ(basic::R::layout::main, *resid);
|
|
|
|
resid = assetmanager.GetResourceId("main", "layout", "com.android.basic");
|
|
ASSERT_TRUE(resid.has_value());
|
|
EXPECT_EQ(basic::R::layout::main, *resid);
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetApkAssets({system_assets_.get()});
|
|
|
|
std::unique_ptr<Asset> asset = assetmanager.Open("file.txt", Asset::ACCESS_BUFFER);
|
|
ASSERT_THAT(asset, NotNull());
|
|
|
|
const char* data = reinterpret_cast<const char*>(asset->getBuffer(false /*wordAligned*/));
|
|
ASSERT_THAT(data, NotNull());
|
|
EXPECT_THAT(std::string(data, asset->getLength()), StrEq("file\n"));
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetApkAssets({system_assets_.get(), app_assets_.get()});
|
|
|
|
std::unique_ptr<Asset> asset = assetmanager.Open("file.txt", Asset::ACCESS_BUFFER);
|
|
ASSERT_THAT(asset, NotNull());
|
|
|
|
const char* data = reinterpret_cast<const char*>(asset->getBuffer(false /*wordAligned*/));
|
|
ASSERT_THAT(data, NotNull());
|
|
EXPECT_THAT(std::string(data, asset->getLength()), StrEq("app override file\n"));
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, OpenDir) {
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetApkAssets({system_assets_.get()});
|
|
|
|
std::unique_ptr<AssetDir> asset_dir = assetmanager.OpenDir("");
|
|
ASSERT_THAT(asset_dir, NotNull());
|
|
ASSERT_THAT(asset_dir->getFileCount(), Eq(2u));
|
|
|
|
EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("file.txt")));
|
|
EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular));
|
|
|
|
EXPECT_THAT(asset_dir->getFileName(1), Eq(String8("subdir")));
|
|
EXPECT_THAT(asset_dir->getFileType(1), Eq(FileType::kFileTypeDirectory));
|
|
|
|
asset_dir = assetmanager.OpenDir("subdir");
|
|
ASSERT_THAT(asset_dir, NotNull());
|
|
ASSERT_THAT(asset_dir->getFileCount(), Eq(1u));
|
|
|
|
EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("subdir_file.txt")));
|
|
EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular));
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, OpenDirFromManyApks) {
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetApkAssets({system_assets_.get(), app_assets_.get()});
|
|
|
|
std::unique_ptr<AssetDir> asset_dir = assetmanager.OpenDir("");
|
|
ASSERT_THAT(asset_dir, NotNull());
|
|
ASSERT_THAT(asset_dir->getFileCount(), Eq(3u));
|
|
|
|
EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("app_file.txt")));
|
|
EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular));
|
|
|
|
EXPECT_THAT(asset_dir->getFileName(1), Eq(String8("file.txt")));
|
|
EXPECT_THAT(asset_dir->getFileType(1), Eq(FileType::kFileTypeRegular));
|
|
|
|
EXPECT_THAT(asset_dir->getFileName(2), Eq(String8("subdir")));
|
|
EXPECT_THAT(asset_dir->getFileType(2), Eq(FileType::kFileTypeDirectory));
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, GetLastPathWithoutEnablingReturnsEmpty) {
|
|
ResTable_config desired_config;
|
|
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetConfiguration(desired_config);
|
|
assetmanager.SetApkAssets({basic_assets_.get()});
|
|
assetmanager.SetResourceResolutionLoggingEnabled(false);
|
|
|
|
auto value = assetmanager.GetResource(basic::R::string::test1);
|
|
ASSERT_TRUE(value.has_value());
|
|
|
|
auto result = assetmanager.GetLastResourceResolution();
|
|
EXPECT_EQ("", result);
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, GetLastPathWithoutResolutionReturnsEmpty) {
|
|
ResTable_config desired_config;
|
|
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetConfiguration(desired_config);
|
|
assetmanager.SetApkAssets({basic_assets_.get()});
|
|
|
|
auto result = assetmanager.GetLastResourceResolution();
|
|
EXPECT_EQ("", result);
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, GetLastPathWithSingleApkAssets) {
|
|
ResTable_config desired_config;
|
|
memset(&desired_config, 0, sizeof(desired_config));
|
|
desired_config.language[0] = 'd';
|
|
desired_config.language[1] = 'e';
|
|
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetResourceResolutionLoggingEnabled(true);
|
|
assetmanager.SetConfiguration(desired_config);
|
|
assetmanager.SetApkAssets({basic_assets_.get()});
|
|
|
|
auto value = assetmanager.GetResource(basic::R::string::test1);
|
|
ASSERT_TRUE(value.has_value());
|
|
|
|
auto result = assetmanager.GetLastResourceResolution();
|
|
EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n"
|
|
"\tFor config - de\n"
|
|
"\tFound initial: basic/basic.apk", result);
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) {
|
|
ResTable_config desired_config;
|
|
memset(&desired_config, 0, sizeof(desired_config));
|
|
desired_config.language[0] = 'd';
|
|
desired_config.language[1] = 'e';
|
|
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetResourceResolutionLoggingEnabled(true);
|
|
assetmanager.SetConfiguration(desired_config);
|
|
assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()});
|
|
|
|
auto value = assetmanager.GetResource(basic::R::string::test1);
|
|
ASSERT_TRUE(value.has_value());
|
|
|
|
auto result = assetmanager.GetLastResourceResolution();
|
|
EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n"
|
|
"\tFor config - de\n"
|
|
"\tFound initial: basic/basic.apk\n"
|
|
"\tFound better: basic/basic_de_fr.apk - de", result);
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) {
|
|
ResTable_config desired_config;
|
|
memset(&desired_config, 0, sizeof(desired_config));
|
|
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetResourceResolutionLoggingEnabled(true);
|
|
assetmanager.SetConfiguration(desired_config);
|
|
assetmanager.SetApkAssets({basic_assets_.get()});
|
|
|
|
auto value = assetmanager.GetResource(basic::R::string::test1);
|
|
ASSERT_TRUE(value.has_value());
|
|
|
|
auto resultEnabled = assetmanager.GetLastResourceResolution();
|
|
ASSERT_NE("", resultEnabled);
|
|
|
|
assetmanager.SetResourceResolutionLoggingEnabled(false);
|
|
|
|
auto resultDisabled = assetmanager.GetLastResourceResolution();
|
|
EXPECT_EQ("", resultDisabled);
|
|
}
|
|
|
|
TEST_F(AssetManager2Test, GetOverlayablesToString) {
|
|
ResTable_config desired_config;
|
|
memset(&desired_config, 0, sizeof(desired_config));
|
|
|
|
AssetManager2 assetmanager;
|
|
assetmanager.SetResourceResolutionLoggingEnabled(true);
|
|
assetmanager.SetConfiguration(desired_config);
|
|
assetmanager.SetApkAssets({overlayable_assets_.get()});
|
|
|
|
const auto map = assetmanager.GetOverlayableMapForPackage(0x7f);
|
|
ASSERT_NE(nullptr, map);
|
|
ASSERT_EQ(3, map->size());
|
|
ASSERT_EQ(map->at("OverlayableResources1"), "overlay://theme");
|
|
ASSERT_EQ(map->at("OverlayableResources2"), "overlay://com.android.overlayable");
|
|
ASSERT_EQ(map->at("OverlayableResources3"), "");
|
|
|
|
std::string api;
|
|
ASSERT_TRUE(assetmanager.GetOverlayablesToString("com.android.overlayable", &api));
|
|
ASSERT_EQ(api.find("not_overlayable"), std::string::npos);
|
|
ASSERT_NE(api.find("resource='com.android.overlayable:string/overlayable2' overlayable='OverlayableResources1' actor='overlay://theme' policy='0x0000000a'\n"),
|
|
std::string::npos);
|
|
}
|
|
|
|
} // namespace android
|