Allow for RRO internal referencing
This change allows RROs to reference their own internal resources as expected. Overlays are loaded as shared libraries so they can have their own resource id space that does not conflict with the resource id space of the target or other overlays. References to overlay resources that override target resources now appear as references to the target resources. Overlay values that are inlined into the xml file specified using android:overlayResources are now able to be used at runtime. See go/rro-references for more information. Bug: 135943783 Test: idmap2_tests Test: libandroidfw_tests Change-Id: Ie349c56d7fd3f7d94b7d595ed6d01dc6b59b6178
This commit is contained in:
@@ -88,8 +88,4 @@ TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) {
|
||||
ASSERT_EQ(overlay_entries1[2].target_id, overlay_entries2[2].target_id);
|
||||
}
|
||||
|
||||
TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) {
|
||||
// TODO(135943783): Removed temporarily until libandroidfw idmap loading is fixed.
|
||||
}
|
||||
|
||||
} // namespace android::idmap2
|
||||
|
||||
@@ -280,7 +280,54 @@ TEST_F(Idmap2BinaryTests, Scan) {
|
||||
}
|
||||
|
||||
TEST_F(Idmap2BinaryTests, Lookup) {
|
||||
// TODO(135943783): Removed temporarily until libandroidfw idmap loading is fixed.
|
||||
SKIP_TEST_IF_CANT_EXEC_IDMAP2;
|
||||
|
||||
// clang-format off
|
||||
auto result = ExecuteBinary({"idmap2",
|
||||
"create",
|
||||
"--target-apk-path", GetTargetApkPath(),
|
||||
"--overlay-apk-path", GetOverlayApkPath(),
|
||||
"--idmap-path", GetIdmapPath()});
|
||||
// clang-format on
|
||||
ASSERT_THAT(result, NotNull());
|
||||
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
|
||||
|
||||
// clang-format off
|
||||
result = ExecuteBinary({"idmap2",
|
||||
"lookup",
|
||||
"--idmap-path", GetIdmapPath(),
|
||||
"--config", "",
|
||||
"--resid", "0x7f02000c"}); // string/str1
|
||||
// clang-format on
|
||||
ASSERT_THAT(result, NotNull());
|
||||
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
|
||||
ASSERT_NE(result->stdout.find("overlay-1"), std::string::npos);
|
||||
ASSERT_EQ(result->stdout.find("overlay-1-sv"), std::string::npos);
|
||||
|
||||
// clang-format off
|
||||
result = ExecuteBinary({"idmap2",
|
||||
"lookup",
|
||||
"--idmap-path", GetIdmapPath(),
|
||||
"--config", "",
|
||||
"--resid", "test.target:string/str1"});
|
||||
// clang-format on
|
||||
ASSERT_THAT(result, NotNull());
|
||||
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
|
||||
ASSERT_NE(result->stdout.find("overlay-1"), std::string::npos);
|
||||
ASSERT_EQ(result->stdout.find("overlay-1-sv"), std::string::npos);
|
||||
|
||||
// clang-format off
|
||||
result = ExecuteBinary({"idmap2",
|
||||
"lookup",
|
||||
"--idmap-path", GetIdmapPath(),
|
||||
"--config", "sv",
|
||||
"--resid", "test.target:string/str1"});
|
||||
// clang-format on
|
||||
ASSERT_THAT(result, NotNull());
|
||||
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
|
||||
ASSERT_NE(result->stdout.find("overlay-1-sv"), std::string::npos);
|
||||
|
||||
unlink(GetIdmapPath().c_str());
|
||||
}
|
||||
|
||||
TEST_F(Idmap2BinaryTests, InvalidCommandLineOptions) {
|
||||
|
||||
@@ -248,7 +248,7 @@ TEST(IdmapTests, CreateIdmapDataDoNotRewriteNonOverlayResourceId) {
|
||||
OverlayManifestInfo info{};
|
||||
info.target_package = "test.target";
|
||||
info.target_name = "TestResources";
|
||||
info.resource_mapping = 0x7f030002; // xml/overlays_different_packages
|
||||
info.resource_mapping = 0x7f030001; // xml/overlays_different_packages
|
||||
auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", info,
|
||||
PolicyFlags::POLICY_PUBLIC,
|
||||
/* enforce_overlayable */ false);
|
||||
@@ -272,7 +272,7 @@ TEST(IdmapTests, CreateIdmapDataInlineResources) {
|
||||
OverlayManifestInfo info{};
|
||||
info.target_package = "test.target";
|
||||
info.target_name = "TestResources";
|
||||
info.resource_mapping = 0x7f030000; // xml/overlays_inline
|
||||
info.resource_mapping = 0x7f030002; // xml/overlays_inline
|
||||
auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", info,
|
||||
PolicyFlags::POLICY_PUBLIC,
|
||||
/* enforce_overlayable */ false);
|
||||
|
||||
@@ -49,7 +49,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
|
||||
ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos);
|
||||
ASSERT_NE(stream.str().find("00000004: 00000002 version\n"), std::string::npos);
|
||||
ASSERT_NE(stream.str().find("00000008: 76a20829 target crc\n"), std::string::npos);
|
||||
ASSERT_NE(stream.str().find("0000000c: e3c188b6 overlay crc\n"), std::string::npos);
|
||||
ASSERT_NE(stream.str().find("0000000c: c054fb26 overlay crc\n"), std::string::npos);
|
||||
ASSERT_NE(stream.str().find("00000210: 7f target package id\n"), std::string::npos);
|
||||
ASSERT_NE(stream.str().find("00000211: 7f overlay package id\n"), std::string::npos);
|
||||
ASSERT_NE(stream.str().find("00000212: 00000004 target entry count\n"), std::string::npos);
|
||||
|
||||
@@ -750,9 +750,11 @@ static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint
|
||||
}
|
||||
|
||||
// May be nullptr.
|
||||
const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie);
|
||||
std::shared_ptr<const DynamicRefTable> dynamic_ref_table =
|
||||
assetmanager->GetDynamicRefTableForCookie(cookie);
|
||||
|
||||
std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(dynamic_ref_table);
|
||||
std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(
|
||||
std::move(dynamic_ref_table));
|
||||
status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
|
||||
asset.reset();
|
||||
|
||||
@@ -785,9 +787,11 @@ static jlong NativeOpenXmlAssetFd(JNIEnv* env, jobject /*clazz*/, jlong ptr, int
|
||||
ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
|
||||
|
||||
// May be nullptr.
|
||||
const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie);
|
||||
std::shared_ptr<const DynamicRefTable> dynamic_ref_table =
|
||||
assetmanager->GetDynamicRefTableForCookie(cookie);
|
||||
|
||||
std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(dynamic_ref_table);
|
||||
std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(
|
||||
std::move(dynamic_ref_table));
|
||||
status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
|
||||
asset.reset();
|
||||
|
||||
|
||||
@@ -167,8 +167,9 @@ cc_test {
|
||||
},
|
||||
},
|
||||
data: [
|
||||
"tests/data/**/*.apk",
|
||||
"tests/data/**/*.arsc",
|
||||
"tests/data/**/*.apk",
|
||||
"tests/data/**/*.arsc",
|
||||
"tests/data/**/*.idmap",
|
||||
],
|
||||
test_suites: ["device-tests"],
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle,
|
||||
time_t last_mod_time,
|
||||
bool for_loader)
|
||||
: zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time),
|
||||
for_loader(for_loader) {
|
||||
for_loader_(for_loader) {
|
||||
}
|
||||
|
||||
std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system,
|
||||
@@ -75,7 +75,7 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap
|
||||
return {};
|
||||
}
|
||||
return LoadImpl({} /*fd*/, loaded_idmap->OverlayApkPath(), std::move(idmap_asset),
|
||||
std::move(loaded_idmap), system, false /*load_as_shared_library*/);
|
||||
std::move(loaded_idmap), system, true /*load_as_shared_library*/);
|
||||
}
|
||||
|
||||
std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd,
|
||||
@@ -165,12 +165,14 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
|
||||
|
||||
// Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid.
|
||||
loaded_apk->idmap_asset_ = std::move(idmap_asset);
|
||||
loaded_apk->loaded_idmap_ = std::move(loaded_idmap);
|
||||
|
||||
const StringPiece data(
|
||||
reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
|
||||
loaded_apk->resources_asset_->getLength());
|
||||
loaded_apk->loaded_arsc_ =
|
||||
LoadedArsc::Load(data, loaded_idmap.get(), system, load_as_shared_library, for_loader);
|
||||
LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(), system, load_as_shared_library,
|
||||
for_loader);
|
||||
if (loaded_apk->loaded_arsc_ == nullptr) {
|
||||
LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
|
||||
return {};
|
||||
@@ -319,7 +321,7 @@ bool ApkAssets::ForEachFile(const std::string& root_path,
|
||||
|
||||
bool ApkAssets::IsUpToDate() const {
|
||||
// Loaders are invalidated by the app, not the system, so assume up to date
|
||||
if (for_loader) {
|
||||
if (for_loader_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "android-base/logging.h"
|
||||
#include "android-base/stringprintf.h"
|
||||
#include "androidfw/ResourceUtils.h"
|
||||
#include "androidfw/Util.h"
|
||||
#include "utils/ByteOrder.h"
|
||||
#include "utils/Trace.h"
|
||||
@@ -35,15 +36,13 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "androidfw/ResourceUtils.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
struct FindEntryResult {
|
||||
// A pointer to the resource table entry for this resource.
|
||||
// If the size of the entry is > sizeof(ResTable_entry), it can be cast to
|
||||
// a ResTable_map_entry and processed as a bag/map.
|
||||
const ResTable_entry* entry;
|
||||
ResTable_entry_handle entry;
|
||||
|
||||
// The configuration for which the resulting entry was defined. This is already swapped to host
|
||||
// endianness.
|
||||
@@ -55,6 +54,9 @@ struct FindEntryResult {
|
||||
// The dynamic package ID map for the package from which this resource came from.
|
||||
const DynamicRefTable* dynamic_ref_table;
|
||||
|
||||
// The package name of the resource.
|
||||
const std::string* package_name;
|
||||
|
||||
// The string pool reference to the type's name. This uses a different string pool than
|
||||
// the global string pool, but this is hidden from the caller.
|
||||
StringPoolRef type_string_ref;
|
||||
@@ -83,11 +85,15 @@ void AssetManager2::BuildDynamicRefTable() {
|
||||
package_groups_.clear();
|
||||
package_ids_.fill(0xff);
|
||||
|
||||
// A mapping from apk assets path to the runtime package id of its first loaded package.
|
||||
std::unordered_map<std::string, uint8_t> apk_assets_package_ids;
|
||||
|
||||
// 0x01 is reserved for the android package.
|
||||
int next_package_id = 0x02;
|
||||
const size_t apk_assets_count = apk_assets_.size();
|
||||
for (size_t i = 0; i < apk_assets_count; i++) {
|
||||
const LoadedArsc* loaded_arsc = apk_assets_[i]->GetLoadedArsc();
|
||||
const ApkAssets* apk_assets = apk_assets_[i];
|
||||
const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc();
|
||||
|
||||
for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) {
|
||||
// Get the package ID or assign one if a shared library.
|
||||
@@ -103,9 +109,37 @@ void AssetManager2::BuildDynamicRefTable() {
|
||||
if (idx == 0xff) {
|
||||
package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size());
|
||||
package_groups_.push_back({});
|
||||
DynamicRefTable& ref_table = package_groups_.back().dynamic_ref_table;
|
||||
ref_table.mAssignedPackageId = package_id;
|
||||
ref_table.mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f;
|
||||
|
||||
if (apk_assets->IsOverlay()) {
|
||||
// The target package must precede the overlay package in the apk assets paths in order
|
||||
// to take effect.
|
||||
const auto& loaded_idmap = apk_assets->GetLoadedIdmap();
|
||||
auto target_package_iter = apk_assets_package_ids.find(loaded_idmap->TargetApkPath());
|
||||
if (target_package_iter != apk_assets_package_ids.end()) {
|
||||
const uint8_t target_package_id = target_package_iter->second;
|
||||
const uint8_t target_idx = package_ids_[target_package_id];
|
||||
CHECK(target_idx != 0xff) << "overlay added to apk_assets_package_ids but does not"
|
||||
<< " have an assigned package group";
|
||||
|
||||
PackageGroup& target_package_group = package_groups_[target_idx];
|
||||
|
||||
// Create a special dynamic reference table for the overlay to rewite references to
|
||||
// overlay resources as references to the target resources they overlay.
|
||||
auto overlay_table = std::make_shared<OverlayDynamicRefTable>(
|
||||
loaded_idmap->GetOverlayDynamicRefTable(target_package_id));
|
||||
package_groups_.back().dynamic_ref_table = overlay_table;
|
||||
|
||||
// Add the overlay resource map to the target package's set of overlays.
|
||||
target_package_group.overlays_.push_back(
|
||||
ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id,
|
||||
overlay_table.get()),
|
||||
static_cast<ApkAssetsCookie>(i)});
|
||||
}
|
||||
}
|
||||
|
||||
DynamicRefTable* ref_table = package_groups_.back().dynamic_ref_table.get();
|
||||
ref_table->mAssignedPackageId = package_id;
|
||||
ref_table->mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f;
|
||||
}
|
||||
PackageGroup* package_group = &package_groups_[idx];
|
||||
|
||||
@@ -116,9 +150,11 @@ void AssetManager2::BuildDynamicRefTable() {
|
||||
// Add the package name -> build time ID mappings.
|
||||
for (const DynamicPackageEntry& entry : package->GetDynamicPackageMap()) {
|
||||
String16 package_name(entry.package_name.c_str(), entry.package_name.size());
|
||||
package_group->dynamic_ref_table.mEntries.replaceValueFor(
|
||||
package_group->dynamic_ref_table->mEntries.replaceValueFor(
|
||||
package_name, static_cast<uint8_t>(entry.package_id));
|
||||
}
|
||||
|
||||
apk_assets_package_ids.insert(std::make_pair(apk_assets->GetPath(), package_id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,8 +163,8 @@ void AssetManager2::BuildDynamicRefTable() {
|
||||
for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) {
|
||||
const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName();
|
||||
for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) {
|
||||
iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()),
|
||||
iter->dynamic_ref_table.mAssignedPackageId);
|
||||
iter2->dynamic_ref_table->addMapping(String16(package_name.c_str(), package_name.size()),
|
||||
iter->dynamic_ref_table->mAssignedPackageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,13 +197,13 @@ void AssetManager2::DumpToLog() const {
|
||||
(loaded_package->IsDynamic() ? " dynamic" : ""));
|
||||
}
|
||||
LOG(INFO) << base::StringPrintf("PG (%02x): ",
|
||||
package_group.dynamic_ref_table.mAssignedPackageId)
|
||||
package_group.dynamic_ref_table->mAssignedPackageId)
|
||||
<< list;
|
||||
|
||||
for (size_t i = 0; i < 256; i++) {
|
||||
if (package_group.dynamic_ref_table.mLookupTable[i] != 0) {
|
||||
if (package_group.dynamic_ref_table->mLookupTable[i] != 0) {
|
||||
LOG(INFO) << base::StringPrintf(" e[0x%02x] -> 0x%02x", (uint8_t) i,
|
||||
package_group.dynamic_ref_table.mLookupTable[i]);
|
||||
package_group.dynamic_ref_table->mLookupTable[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,14 +225,15 @@ const DynamicRefTable* AssetManager2::GetDynamicRefTableForPackage(uint32_t pack
|
||||
if (idx == 0xff) {
|
||||
return nullptr;
|
||||
}
|
||||
return &package_groups_[idx].dynamic_ref_table;
|
||||
return package_groups_[idx].dynamic_ref_table.get();
|
||||
}
|
||||
|
||||
const DynamicRefTable* AssetManager2::GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const {
|
||||
std::shared_ptr<const DynamicRefTable> AssetManager2::GetDynamicRefTableForCookie(
|
||||
ApkAssetsCookie cookie) const {
|
||||
for (const PackageGroup& package_group : package_groups_) {
|
||||
for (const ApkAssetsCookie& package_cookie : package_group.cookies_) {
|
||||
if (package_cookie == cookie) {
|
||||
return &package_group.dynamic_ref_table;
|
||||
return package_group.dynamic_ref_table;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -290,21 +327,45 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
|
||||
}
|
||||
}
|
||||
|
||||
std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system,
|
||||
bool exclude_mipmap) const {
|
||||
ATRACE_NAME("AssetManager::GetResourceConfigurations");
|
||||
std::set<ResTable_config> configurations;
|
||||
std::set<std::string> AssetManager2::GetNonSystemOverlayPaths() const {
|
||||
std::set<std::string> non_system_overlays;
|
||||
for (const PackageGroup& package_group : package_groups_) {
|
||||
bool found_system_package = false;
|
||||
for (const ConfiguredPackage& package : package_group.packages_) {
|
||||
if (exclude_system && package.loaded_package_->IsSystem()) {
|
||||
if (package.loaded_package_->IsSystem()) {
|
||||
found_system_package = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_system_package) {
|
||||
for (const ConfiguredOverlay& overlay : package_group.overlays_) {
|
||||
non_system_overlays.insert(apk_assets_[overlay.cookie]->GetPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return non_system_overlays;
|
||||
}
|
||||
|
||||
std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system,
|
||||
bool exclude_mipmap) const {
|
||||
ATRACE_NAME("AssetManager::GetResourceConfigurations");
|
||||
const auto non_system_overlays =
|
||||
(exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>();
|
||||
|
||||
std::set<ResTable_config> configurations;
|
||||
for (const PackageGroup& package_group : package_groups_) {
|
||||
for (size_t i = 0; i < package_group.packages_.size(); i++) {
|
||||
const ConfiguredPackage& package = package_group.packages_[i];
|
||||
if (exclude_system && package.loaded_package_->IsSystem()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) {
|
||||
// Overlays must appear after the target package to take effect. Any overlay found in the
|
||||
// same package as a system package is able to overlay system resources.
|
||||
auto apk_assets = apk_assets_[package_group.cookies_[i]];
|
||||
if (exclude_system && apk_assets->IsOverlay()
|
||||
&& non_system_overlays.find(apk_assets->GetPath()) == non_system_overlays.end()) {
|
||||
// Exclude overlays that target system resources.
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -318,17 +379,20 @@ std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system,
|
||||
bool merge_equivalent_languages) const {
|
||||
ATRACE_NAME("AssetManager::GetResourceLocales");
|
||||
std::set<std::string> locales;
|
||||
const auto non_system_overlays =
|
||||
(exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>();
|
||||
|
||||
for (const PackageGroup& package_group : package_groups_) {
|
||||
bool found_system_package = false;
|
||||
for (const ConfiguredPackage& package : package_group.packages_) {
|
||||
for (size_t i = 0; i < package_group.packages_.size(); i++) {
|
||||
const ConfiguredPackage& package = package_group.packages_[i];
|
||||
if (exclude_system && package.loaded_package_->IsSystem()) {
|
||||
found_system_package = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) {
|
||||
// Overlays must appear after the target package to take effect. Any overlay found in the
|
||||
// same package as a system package is able to overlay system resources.
|
||||
auto apk_assets = apk_assets_[package_group.cookies_[i]];
|
||||
if (exclude_system && apk_assets->IsOverlay()
|
||||
&& non_system_overlays.find(apk_assets->GetPath()) == non_system_overlays.end()) {
|
||||
// Exclude overlays that target system resources.
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -424,6 +488,12 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
|
||||
bool /*stop_at_first_match*/,
|
||||
bool ignore_configuration,
|
||||
FindEntryResult* out_entry) const {
|
||||
if (resource_resolution_logging_enabled_) {
|
||||
// Clear the last logged resource resolution.
|
||||
ResetResourceResolution();
|
||||
last_resolution_.resid = resid;
|
||||
}
|
||||
|
||||
// Might use this if density_override != 0.
|
||||
ResTable_config density_override_config;
|
||||
|
||||
@@ -435,6 +505,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
|
||||
desired_config = &density_override_config;
|
||||
}
|
||||
|
||||
// Retrieve the package group from the package id of the resource id.
|
||||
if (!is_valid_resid(resid)) {
|
||||
LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
|
||||
return kInvalidCookie;
|
||||
@@ -443,8 +514,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
|
||||
const uint32_t package_id = get_package_id(resid);
|
||||
const uint8_t type_idx = get_type_id(resid) - 1;
|
||||
const uint16_t entry_idx = get_entry_id(resid);
|
||||
|
||||
const uint8_t package_idx = package_ids_[package_id];
|
||||
uint8_t package_idx = package_ids_[package_id];
|
||||
if (package_idx == 0xff) {
|
||||
ANDROID_LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.",
|
||||
package_id, resid);
|
||||
@@ -452,8 +522,71 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
|
||||
}
|
||||
|
||||
const PackageGroup& package_group = package_groups_[package_idx];
|
||||
const size_t package_count = package_group.packages_.size();
|
||||
ApkAssetsCookie cookie = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
|
||||
false /* stop_at_first_match */,
|
||||
ignore_configuration, out_entry);
|
||||
if (UNLIKELY(cookie == kInvalidCookie)) {
|
||||
return kInvalidCookie;
|
||||
}
|
||||
|
||||
if (!apk_assets_[cookie]->IsLoader()) {
|
||||
for (const auto& id_map : package_group.overlays_) {
|
||||
auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid);
|
||||
if (!overlay_entry) {
|
||||
// No id map entry exists for this target resource.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (overlay_entry.IsTableEntry()) {
|
||||
// The target resource is overlaid by an inline value not represented by a resource.
|
||||
out_entry->entry = overlay_entry.GetTableEntry();
|
||||
out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
|
||||
cookie = id_map.cookie;
|
||||
continue;
|
||||
}
|
||||
|
||||
FindEntryResult overlay_result;
|
||||
ApkAssetsCookie overlay_cookie = FindEntry(overlay_entry.GetResourceId(), density_override,
|
||||
false /* stop_at_first_match */,
|
||||
ignore_configuration, &overlay_result);
|
||||
if (UNLIKELY(overlay_cookie == kInvalidCookie)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!overlay_result.config.isBetterThan(out_entry->config, desired_config)
|
||||
&& overlay_result.config.compare(out_entry->config) != 0) {
|
||||
// The configuration of the entry for the overlay must be equal to or better than the target
|
||||
// configuration to be chosen as the better value.
|
||||
continue;
|
||||
}
|
||||
|
||||
cookie = overlay_cookie;
|
||||
out_entry->entry = std::move(overlay_result.entry);
|
||||
out_entry->config = overlay_result.config;
|
||||
out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
|
||||
if (resource_resolution_logging_enabled_) {
|
||||
last_resolution_.steps.push_back(
|
||||
Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result.config.toString(),
|
||||
&package_group.packages_[0].loaded_package_->GetPackageName()});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (resource_resolution_logging_enabled_) {
|
||||
last_resolution_.cookie = cookie;
|
||||
last_resolution_.type_string_ref = out_entry->type_string_ref;
|
||||
last_resolution_.entry_string_ref = out_entry->entry_string_ref;
|
||||
}
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_group,
|
||||
uint8_t type_idx, uint16_t entry_idx,
|
||||
const ResTable_config& desired_config,
|
||||
bool /*stop_at_first_match*/,
|
||||
bool ignore_configuration,
|
||||
FindEntryResult* out_entry) const {
|
||||
ApkAssetsCookie best_cookie = kInvalidCookie;
|
||||
const LoadedPackage* best_package = nullptr;
|
||||
const ResTable_type* best_type = nullptr;
|
||||
@@ -462,13 +595,14 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
|
||||
uint32_t best_offset = 0u;
|
||||
uint32_t type_flags = 0u;
|
||||
|
||||
Resolution::Step::Type resolution_type;
|
||||
Resolution::Step::Type resolution_type = Resolution::Step::Type::NO_ENTRY;
|
||||
std::vector<Resolution::Step> resolution_steps;
|
||||
|
||||
// If desired_config is the same as the set configuration, then we can use our filtered list
|
||||
// and we don't need to match the configurations, since they already matched.
|
||||
const bool use_fast_path = !ignore_configuration && desired_config == &configuration_;
|
||||
const bool use_fast_path = !ignore_configuration && &desired_config == &configuration_;
|
||||
|
||||
const size_t package_count = package_group.packages_.size();
|
||||
for (size_t pi = 0; pi < package_count; pi++) {
|
||||
const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi];
|
||||
const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_;
|
||||
@@ -481,24 +615,10 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
|
||||
continue;
|
||||
}
|
||||
|
||||
uint16_t local_entry_idx = entry_idx;
|
||||
|
||||
// If there is an IDMAP supplied with this package, translate the entry ID.
|
||||
if (type_spec->idmap_entries != nullptr) {
|
||||
if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) {
|
||||
// There is no mapping, so the resource is not meant to be in this overlay package.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx);
|
||||
|
||||
|
||||
// If the package is an overlay or custom loader,
|
||||
// then even configurations that are the same MUST be chosen.
|
||||
const bool package_is_overlay = loaded_package->IsOverlay();
|
||||
const bool package_is_loader = loaded_package->IsCustomLoader();
|
||||
const bool should_overlay = package_is_overlay || package_is_loader;
|
||||
type_flags |= type_spec->GetFlagsForEntryIndex(entry_idx);
|
||||
|
||||
if (use_fast_path) {
|
||||
const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
|
||||
@@ -511,25 +631,15 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
|
||||
// configurations that do NOT match have been filtered-out.
|
||||
if (best_config == nullptr) {
|
||||
resolution_type = Resolution::Step::Type::INITIAL;
|
||||
} else if (this_config.isBetterThan(*best_config, desired_config)) {
|
||||
if (package_is_loader) {
|
||||
resolution_type = Resolution::Step::Type::BETTER_MATCH_LOADER;
|
||||
} else {
|
||||
resolution_type = Resolution::Step::Type::BETTER_MATCH;
|
||||
}
|
||||
} else if (should_overlay && this_config.compare(*best_config) == 0) {
|
||||
if (package_is_loader) {
|
||||
resolution_type = Resolution::Step::Type::OVERLAID_LOADER;
|
||||
} else if (package_is_overlay) {
|
||||
resolution_type = Resolution::Step::Type::OVERLAID;
|
||||
}
|
||||
} else if (this_config.isBetterThan(*best_config, &desired_config)) {
|
||||
resolution_type = (package_is_loader) ? Resolution::Step::Type::BETTER_MATCH_LOADER
|
||||
: Resolution::Step::Type::BETTER_MATCH;
|
||||
} else if (package_is_loader && this_config.compare(*best_config) == 0) {
|
||||
resolution_type = Resolution::Step::Type::OVERLAID_LOADER;
|
||||
} else {
|
||||
if (resource_resolution_logging_enabled_) {
|
||||
if (package_is_loader) {
|
||||
resolution_type = Resolution::Step::Type::SKIPPED_LOADER;
|
||||
} else {
|
||||
resolution_type = Resolution::Step::Type::SKIPPED;
|
||||
}
|
||||
resolution_type = (package_is_loader) ? Resolution::Step::Type::SKIPPED_LOADER
|
||||
: Resolution::Step::Type::SKIPPED;
|
||||
resolution_steps.push_back(Resolution::Step{resolution_type,
|
||||
this_config.toString(),
|
||||
&loaded_package->GetPackageName()});
|
||||
@@ -540,7 +650,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
|
||||
// The configuration matches and is better than the previous selection.
|
||||
// Find the entry value if it exists for this configuration.
|
||||
const ResTable_type* type = filtered_group.types[i];
|
||||
const uint32_t offset = LoadedPackage::GetEntryOffset(type, local_entry_idx);
|
||||
const uint32_t offset = LoadedPackage::GetEntryOffset(type, entry_idx);
|
||||
if (offset == ResTable_type::NO_ENTRY) {
|
||||
if (resource_resolution_logging_enabled_) {
|
||||
if (package_is_loader) {
|
||||
@@ -548,7 +658,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
|
||||
} else {
|
||||
resolution_type = Resolution::Step::Type::NO_ENTRY;
|
||||
}
|
||||
resolution_steps.push_back(Resolution::Step{Resolution::Step::Type::NO_ENTRY,
|
||||
resolution_steps.push_back(Resolution::Step{resolution_type,
|
||||
this_config.toString(),
|
||||
&loaded_package->GetPackageName()});
|
||||
}
|
||||
@@ -562,9 +672,9 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
|
||||
best_offset = offset;
|
||||
|
||||
if (resource_resolution_logging_enabled_) {
|
||||
resolution_steps.push_back(Resolution::Step{resolution_type,
|
||||
this_config.toString(),
|
||||
&loaded_package->GetPackageName()});
|
||||
last_resolution_.steps.push_back(Resolution::Step{resolution_type,
|
||||
this_config.toString(),
|
||||
&loaded_package->GetPackageName()});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -579,24 +689,17 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
|
||||
|
||||
if (!ignore_configuration) {
|
||||
this_config.copyFromDtoH((*iter)->config);
|
||||
if (!this_config.match(*desired_config)) {
|
||||
if (!this_config.match(desired_config)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (best_config == nullptr) {
|
||||
resolution_type = Resolution::Step::Type::INITIAL;
|
||||
} else if (this_config.isBetterThan(*best_config, desired_config)) {
|
||||
if (package_is_loader) {
|
||||
resolution_type = Resolution::Step::Type::BETTER_MATCH_LOADER;
|
||||
} else {
|
||||
resolution_type = Resolution::Step::Type::BETTER_MATCH;
|
||||
}
|
||||
} else if (should_overlay && this_config.compare(*best_config) == 0) {
|
||||
if (package_is_overlay) {
|
||||
resolution_type = Resolution::Step::Type::OVERLAID;
|
||||
} else if (package_is_loader) {
|
||||
resolution_type = Resolution::Step::Type::OVERLAID_LOADER;
|
||||
}
|
||||
} else if (this_config.isBetterThan(*best_config, &desired_config)) {
|
||||
resolution_type = (package_is_loader) ? Resolution::Step::Type::BETTER_MATCH_LOADER
|
||||
: Resolution::Step::Type::BETTER_MATCH;
|
||||
} else if (package_is_loader && this_config.compare(*best_config) == 0) {
|
||||
resolution_type = Resolution::Step::Type::OVERLAID_LOADER;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
@@ -604,7 +707,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
|
||||
|
||||
// The configuration matches and is better than the previous selection.
|
||||
// Find the entry value if it exists for this configuration.
|
||||
const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx);
|
||||
const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, entry_idx);
|
||||
if (offset == ResTable_type::NO_ENTRY) {
|
||||
continue;
|
||||
}
|
||||
@@ -622,9 +725,9 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
|
||||
}
|
||||
|
||||
if (resource_resolution_logging_enabled_) {
|
||||
resolution_steps.push_back(Resolution::Step{resolution_type,
|
||||
this_config.toString(),
|
||||
&loaded_package->GetPackageName()});
|
||||
last_resolution_.steps.push_back(Resolution::Step{resolution_type,
|
||||
this_config.toString(),
|
||||
&loaded_package->GetPackageName()});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -639,38 +742,30 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
|
||||
return kInvalidCookie;
|
||||
}
|
||||
|
||||
out_entry->entry = best_entry;
|
||||
out_entry->entry = ResTable_entry_handle::unmanaged(best_entry);
|
||||
out_entry->config = *best_config;
|
||||
out_entry->type_flags = type_flags;
|
||||
out_entry->package_name = &best_package->GetPackageName();
|
||||
out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
|
||||
out_entry->entry_string_ref =
|
||||
StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
|
||||
out_entry->dynamic_ref_table = &package_group.dynamic_ref_table;
|
||||
|
||||
if (resource_resolution_logging_enabled_) {
|
||||
last_resolution.resid = resid;
|
||||
last_resolution.cookie = best_cookie;
|
||||
last_resolution.steps = resolution_steps;
|
||||
|
||||
// Cache only the type/entry refs since that's all that's needed to build name
|
||||
last_resolution.type_string_ref =
|
||||
StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
|
||||
last_resolution.entry_string_ref =
|
||||
StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
|
||||
}
|
||||
StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
|
||||
out_entry->dynamic_ref_table = package_group.dynamic_ref_table.get();
|
||||
|
||||
return best_cookie;
|
||||
}
|
||||
|
||||
void AssetManager2::ResetResourceResolution() const {
|
||||
last_resolution_.cookie = kInvalidCookie;
|
||||
last_resolution_.resid = 0;
|
||||
last_resolution_.steps.clear();
|
||||
last_resolution_.type_string_ref = StringPoolRef();
|
||||
last_resolution_.entry_string_ref = StringPoolRef();
|
||||
}
|
||||
|
||||
void AssetManager2::SetResourceResolutionLoggingEnabled(bool enabled) {
|
||||
resource_resolution_logging_enabled_ = enabled;
|
||||
|
||||
if (!enabled) {
|
||||
last_resolution.cookie = kInvalidCookie;
|
||||
last_resolution.resid = 0;
|
||||
last_resolution.steps.clear();
|
||||
last_resolution.type_string_ref = StringPoolRef();
|
||||
last_resolution.entry_string_ref = StringPoolRef();
|
||||
ResetResourceResolution();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -680,24 +775,24 @@ std::string AssetManager2::GetLastResourceResolution() const {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
auto cookie = last_resolution.cookie;
|
||||
auto cookie = last_resolution_.cookie;
|
||||
if (cookie == kInvalidCookie) {
|
||||
LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path.";
|
||||
return std::string();
|
||||
}
|
||||
|
||||
uint32_t resid = last_resolution.resid;
|
||||
std::vector<Resolution::Step>& steps = last_resolution.steps;
|
||||
uint32_t resid = last_resolution_.resid;
|
||||
std::vector<Resolution::Step>& steps = last_resolution_.steps;
|
||||
|
||||
ResourceName resource_name;
|
||||
std::string resource_name_string;
|
||||
|
||||
const LoadedPackage* package =
|
||||
apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
|
||||
apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
|
||||
|
||||
if (package != nullptr) {
|
||||
ToResourceName(last_resolution.type_string_ref,
|
||||
last_resolution.entry_string_ref,
|
||||
ToResourceName(last_resolution_.type_string_ref,
|
||||
last_resolution_.entry_string_ref,
|
||||
package->GetPackageName(),
|
||||
&resource_name);
|
||||
resource_name_string = ToFormattedResourceString(&resource_name);
|
||||
@@ -762,25 +857,9 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t package_idx = package_ids_[get_package_id(resid)];
|
||||
if (package_idx == 0xff) {
|
||||
LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.",
|
||||
get_package_id(resid), resid);
|
||||
return false;
|
||||
}
|
||||
|
||||
const PackageGroup& package_group = package_groups_[package_idx];
|
||||
auto cookie_iter = std::find(package_group.cookies_.begin(),
|
||||
package_group.cookies_.end(), cookie);
|
||||
if (cookie_iter == package_group.cookies_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long package_pos = std::distance(package_group.cookies_.begin(), cookie_iter);
|
||||
const LoadedPackage* package = package_group.packages_[package_pos].loaded_package_;
|
||||
return ToResourceName(entry.type_string_ref,
|
||||
entry.entry_string_ref,
|
||||
package->GetPackageName(),
|
||||
*entry.package_name,
|
||||
out_name);
|
||||
}
|
||||
|
||||
@@ -807,7 +886,8 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
|
||||
return kInvalidCookie;
|
||||
}
|
||||
|
||||
if (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) {
|
||||
const ResTable_entry* table_entry = *entry.entry;
|
||||
if (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) {
|
||||
if (!may_be_bag) {
|
||||
LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
|
||||
return kInvalidCookie;
|
||||
@@ -822,7 +902,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
|
||||
}
|
||||
|
||||
const Res_value* device_value = reinterpret_cast<const Res_value*>(
|
||||
reinterpret_cast<const uint8_t*>(entry.entry) + dtohs(entry.entry->size));
|
||||
reinterpret_cast<const uint8_t*>(table_entry) + dtohs(table_entry->size));
|
||||
out_value->copyFrom_dtoh(*device_value);
|
||||
|
||||
// Convert the package ID to the runtime assigned package ID.
|
||||
@@ -903,13 +983,14 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>&
|
||||
// Check that the size of the entry header is at least as big as
|
||||
// the desired ResTable_map_entry. Also verify that the entry
|
||||
// was intended to be a map.
|
||||
if (dtohs(entry.entry->size) < sizeof(ResTable_map_entry) ||
|
||||
(dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) {
|
||||
const ResTable_entry* table_entry = *entry.entry;
|
||||
if (dtohs(table_entry->size) < sizeof(ResTable_map_entry) ||
|
||||
(dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) {
|
||||
// Not a bag, nothing to do.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry.entry);
|
||||
const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(table_entry);
|
||||
const ResTable_map* map_entry =
|
||||
reinterpret_cast<const ResTable_map*>(reinterpret_cast<const uint8_t*>(map) + map->size);
|
||||
const ResTable_map* const map_entry_end = map_entry + dtohl(map->count);
|
||||
@@ -1134,7 +1215,7 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name,
|
||||
}
|
||||
|
||||
if (resid != 0u) {
|
||||
return fix_package_id(resid, package_group.dynamic_ref_table.mAssignedPackageId);
|
||||
return fix_package_id(resid, package_group.dynamic_ref_table->mAssignedPackageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1191,7 +1272,7 @@ uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const
|
||||
for (auto& package_group : package_groups_) {
|
||||
for (auto& package2 : package_group.packages_) {
|
||||
if (package2.loaded_package_ == package) {
|
||||
return package_group.dynamic_ref_table.mAssignedPackageId;
|
||||
return package_group.dynamic_ref_table->mAssignedPackageId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include "android-base/logging.h"
|
||||
#include "android-base/stringprintf.h"
|
||||
#include "androidfw/ResourceTypes.h"
|
||||
#include "androidfw/Util.h"
|
||||
#include "utils/ByteOrder.h"
|
||||
#include "utils/Trace.h"
|
||||
|
||||
@@ -29,40 +31,124 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "androidfw/ResourceTypes.h"
|
||||
|
||||
using ::android::base::StringPrintf;
|
||||
|
||||
namespace android {
|
||||
|
||||
constexpr static inline bool is_valid_package_id(uint16_t id) {
|
||||
return id != 0 && id <= 255;
|
||||
static bool compare_target_entries(const Idmap_target_entry &e1, const uint32_t target_id) {
|
||||
return dtohl(e1.target_id) < target_id;
|
||||
}
|
||||
|
||||
constexpr static inline bool is_valid_type_id(uint16_t id) {
|
||||
// Type IDs and package IDs have the same constraints in the IDMAP.
|
||||
return is_valid_package_id(id);
|
||||
static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
|
||||
return dtohl(e1.overlay_id) < overlay_id;
|
||||
}
|
||||
|
||||
bool LoadedIdmap::Lookup(const IdmapEntry_header* header, uint16_t input_entry_id,
|
||||
uint16_t* output_entry_id) {
|
||||
if (input_entry_id < dtohs(header->entry_id_offset)) {
|
||||
// After applying the offset, the entry is not present.
|
||||
return false;
|
||||
OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
|
||||
: data_header_(loaded_idmap->data_header_),
|
||||
idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
|
||||
|
||||
OverlayStringPool::~OverlayStringPool() {
|
||||
uninit();
|
||||
}
|
||||
|
||||
const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const {
|
||||
const size_t offset = dtohl(data_header_->string_pool_index_offset);
|
||||
if (idmap_string_pool_ != nullptr && idx >= size() && idx >= offset) {
|
||||
return idmap_string_pool_->stringAt(idx - offset, outLen);
|
||||
}
|
||||
|
||||
input_entry_id -= dtohs(header->entry_id_offset);
|
||||
if (input_entry_id >= dtohs(header->entry_count)) {
|
||||
// The entry is not present.
|
||||
return false;
|
||||
return ResStringPool::stringAt(idx, outLen);
|
||||
}
|
||||
|
||||
const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const {
|
||||
const size_t offset = dtohl(data_header_->string_pool_index_offset);
|
||||
if (idmap_string_pool_ != nullptr && idx >= size() && idx >= offset) {
|
||||
return idmap_string_pool_->string8At(idx - offset, outLen);
|
||||
}
|
||||
|
||||
uint32_t result = dtohl(header->entries[input_entry_id]);
|
||||
if (result == 0xffffffffu) {
|
||||
return false;
|
||||
return ResStringPool::string8At(idx, outLen);
|
||||
}
|
||||
|
||||
OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header,
|
||||
const Idmap_overlay_entry* entries,
|
||||
uint8_t target_assigned_package_id)
|
||||
: data_header_(data_header),
|
||||
entries_(entries),
|
||||
target_assigned_package_id_(target_assigned_package_id) { };
|
||||
|
||||
status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
|
||||
const Idmap_overlay_entry* first_entry = entries_;
|
||||
const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count);
|
||||
auto entry = std::lower_bound(first_entry, end_entry, *resId, compare_overlay_entries);
|
||||
|
||||
if (entry == end_entry || dtohl(entry->overlay_id) != *resId) {
|
||||
// A mapping for the target resource id could not be found.
|
||||
return DynamicRefTable::lookupResourceId(resId);
|
||||
}
|
||||
*output_entry_id = static_cast<uint16_t>(result);
|
||||
return true;
|
||||
|
||||
*resId = (0x00FFFFFFU & dtohl(entry->target_id))
|
||||
| (((uint32_t) target_assigned_package_id_) << 24);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) const {
|
||||
return DynamicRefTable::lookupResourceId(resId);
|
||||
}
|
||||
|
||||
IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
|
||||
const Idmap_target_entry* entries,
|
||||
uint8_t target_assigned_package_id,
|
||||
const OverlayDynamicRefTable* overlay_ref_table)
|
||||
: data_header_(data_header),
|
||||
entries_(entries),
|
||||
target_assigned_package_id_(target_assigned_package_id),
|
||||
overlay_ref_table_(overlay_ref_table) { };
|
||||
|
||||
IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
|
||||
if ((target_res_id >> 24) != target_assigned_package_id_) {
|
||||
// The resource id must have the same package id as the target package.
|
||||
return {};
|
||||
}
|
||||
|
||||
// The resource ids encoded within the idmap are build-time resource ids.
|
||||
target_res_id = (0x00FFFFFFU & target_res_id)
|
||||
| (((uint32_t) data_header_->target_package_id) << 24);
|
||||
|
||||
const Idmap_target_entry* first_entry = entries_;
|
||||
const Idmap_target_entry* end_entry = entries_ + dtohl(data_header_->target_entry_count);
|
||||
auto entry = std::lower_bound(first_entry, end_entry, target_res_id, compare_target_entries);
|
||||
|
||||
if (entry == end_entry || dtohl(entry->target_id) != target_res_id) {
|
||||
// A mapping for the target resource id could not be found.
|
||||
return {};
|
||||
}
|
||||
|
||||
// A reference should be treated as an alias of the resource. Instead of returning the table
|
||||
// entry, return the alias resource id to look up. The alias resource might not reside within the
|
||||
// overlay package, so the resource id must be fixed with the dynamic reference table of the
|
||||
// overlay before returning.
|
||||
if (entry->type == Res_value::TYPE_REFERENCE
|
||||
|| entry->type == Res_value::TYPE_DYNAMIC_REFERENCE) {
|
||||
uint32_t overlay_resource_id = dtohl(entry->value);
|
||||
|
||||
// Lookup the resource without rewriting the overlay resource id back to the target resource id
|
||||
// being looked up.
|
||||
overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
|
||||
return Result(overlay_resource_id);
|
||||
}
|
||||
|
||||
// Copy the type and value into the ResTable_entry structure needed by asset manager.
|
||||
uint16_t malloc_size = sizeof(ResTable_entry) + sizeof(Res_value);
|
||||
auto table_entry = reinterpret_cast<ResTable_entry*>(malloc(malloc_size));
|
||||
memset(table_entry, 0, malloc_size);
|
||||
table_entry->size = htods(sizeof(ResTable_entry));
|
||||
|
||||
auto table_value = reinterpret_cast<Res_value*>(reinterpret_cast<uint8_t*>(table_entry)
|
||||
+ sizeof(ResTable_entry));
|
||||
table_value->dataType = entry->type;
|
||||
table_value->data = entry->value;
|
||||
|
||||
return Result(ResTable_entry_handle::managed(table_entry));
|
||||
}
|
||||
|
||||
static bool is_word_aligned(const void* data) {
|
||||
@@ -95,24 +181,26 @@ static bool IsValidIdmapHeader(const StringPiece& data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_valid_package_id(dtohs(header->target_package_id))) {
|
||||
LOG(ERROR) << StringPrintf("Target package ID in Idmap is invalid: 0x%02x",
|
||||
dtohs(header->target_package_id));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dtohs(header->type_count) > 255) {
|
||||
LOG(ERROR) << StringPrintf("Idmap has too many type mappings (was %d, max 255)",
|
||||
(int)dtohs(header->type_count));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
LoadedIdmap::LoadedIdmap(const Idmap_header* header) : header_(header) {
|
||||
LoadedIdmap::LoadedIdmap(const Idmap_header* header,
|
||||
const Idmap_data_header* data_header,
|
||||
const Idmap_target_entry* target_entries,
|
||||
const Idmap_overlay_entry* overlay_entries,
|
||||
ResStringPool* string_pool) : header_(header),
|
||||
data_header_(data_header),
|
||||
target_entries_(target_entries),
|
||||
overlay_entries_(overlay_entries),
|
||||
string_pool_(string_pool) {
|
||||
|
||||
size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path),
|
||||
arraysize(header_->overlay_path));
|
||||
overlay_apk_path_.assign(reinterpret_cast<const char*>(header_->overlay_path), length);
|
||||
|
||||
length = strnlen(reinterpret_cast<const char*>(header_->target_path),
|
||||
arraysize(header_->target_path));
|
||||
target_apk_path_.assign(reinterpret_cast<const char*>(header_->target_path), length);
|
||||
}
|
||||
|
||||
std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_data) {
|
||||
@@ -121,70 +209,67 @@ std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_da
|
||||
return {};
|
||||
}
|
||||
|
||||
const Idmap_header* header = reinterpret_cast<const Idmap_header*>(idmap_data.data());
|
||||
|
||||
// Can't use make_unique because LoadedImpl constructor is private.
|
||||
std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(new LoadedIdmap(header));
|
||||
|
||||
auto header = reinterpret_cast<const Idmap_header*>(idmap_data.data());
|
||||
const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()) + sizeof(*header);
|
||||
size_t data_size = idmap_data.size() - sizeof(*header);
|
||||
|
||||
size_t type_maps_encountered = 0u;
|
||||
while (data_size >= sizeof(IdmapEntry_header)) {
|
||||
if (!is_word_aligned(data_ptr)) {
|
||||
LOG(ERROR) << "Type mapping in Idmap is not word aligned";
|
||||
return {};
|
||||
}
|
||||
// Currently idmap2 can only generate one data block.
|
||||
auto data_header = reinterpret_cast<const Idmap_data_header*>(data_ptr);
|
||||
data_ptr += sizeof(*data_header);
|
||||
data_size -= sizeof(*data_header);
|
||||
|
||||
// Validate the type IDs.
|
||||
const IdmapEntry_header* entry_header = reinterpret_cast<const IdmapEntry_header*>(data_ptr);
|
||||
if (!is_valid_type_id(dtohs(entry_header->target_type_id)) || !is_valid_type_id(dtohs(entry_header->overlay_type_id))) {
|
||||
LOG(ERROR) << StringPrintf("Invalid type map (0x%02x -> 0x%02x)",
|
||||
dtohs(entry_header->target_type_id),
|
||||
dtohs(entry_header->overlay_type_id));
|
||||
return {};
|
||||
}
|
||||
|
||||
// Make sure there is enough space for the entries declared in the header.
|
||||
if ((data_size - sizeof(*entry_header)) / sizeof(uint32_t) <
|
||||
static_cast<size_t>(dtohs(entry_header->entry_count))) {
|
||||
LOG(ERROR) << StringPrintf("Idmap too small for the number of entries (%d)",
|
||||
(int)dtohs(entry_header->entry_count));
|
||||
return {};
|
||||
}
|
||||
|
||||
// Only add a non-empty overlay.
|
||||
if (dtohs(entry_header->entry_count != 0)) {
|
||||
loaded_idmap->type_map_[static_cast<uint8_t>(dtohs(entry_header->overlay_type_id))] =
|
||||
entry_header;
|
||||
}
|
||||
|
||||
const size_t entry_size_bytes =
|
||||
sizeof(*entry_header) + (dtohs(entry_header->entry_count) * sizeof(uint32_t));
|
||||
data_ptr += entry_size_bytes;
|
||||
data_size -= entry_size_bytes;
|
||||
type_maps_encountered++;
|
||||
}
|
||||
|
||||
// Verify that we parsed all the type maps.
|
||||
if (type_maps_encountered != static_cast<size_t>(dtohs(header->type_count))) {
|
||||
LOG(ERROR) << "Parsed " << type_maps_encountered << " type maps but expected "
|
||||
<< (int)dtohs(header->type_count);
|
||||
// Make sure there is enough space for the target entries declared in the header.
|
||||
const auto target_entries = reinterpret_cast<const Idmap_target_entry*>(data_ptr);
|
||||
if (data_size / sizeof(Idmap_target_entry) <
|
||||
static_cast<size_t>(dtohl(data_header->target_entry_count))) {
|
||||
LOG(ERROR) << StringPrintf("Idmap too small for the number of target entries (%d)",
|
||||
(int)dtohl(data_header->target_entry_count));
|
||||
return {};
|
||||
}
|
||||
|
||||
// Advance the data pointer past the target entries.
|
||||
const size_t target_entry_size_bytes =
|
||||
(dtohl(data_header->target_entry_count) * sizeof(Idmap_target_entry));
|
||||
data_ptr += target_entry_size_bytes;
|
||||
data_size -= target_entry_size_bytes;
|
||||
|
||||
// Make sure there is enough space for the overlay entries declared in the header.
|
||||
const auto overlay_entries = reinterpret_cast<const Idmap_overlay_entry*>(data_ptr);
|
||||
if (data_size / sizeof(Idmap_overlay_entry) <
|
||||
static_cast<size_t>(dtohl(data_header->overlay_entry_count))) {
|
||||
LOG(ERROR) << StringPrintf("Idmap too small for the number of overlay entries (%d)",
|
||||
(int)dtohl(data_header->overlay_entry_count));
|
||||
return {};
|
||||
}
|
||||
|
||||
// Advance the data pointer past the target entries.
|
||||
const size_t overlay_entry_size_bytes =
|
||||
(dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry));
|
||||
data_ptr += overlay_entry_size_bytes;
|
||||
data_size -= overlay_entry_size_bytes;
|
||||
|
||||
// Read the idmap string pool that holds the value of inline string entries.
|
||||
if (data_size < dtohl(data_header->string_pool_length)) {
|
||||
LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)",
|
||||
(int)dtohl(data_header->string_pool_length));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto idmap_string_pool = util::make_unique<ResStringPool>();
|
||||
if (dtohl(data_header->string_pool_length) > 0) {
|
||||
status_t err = idmap_string_pool->setTo(data_ptr, dtohl(data_header->string_pool_length));
|
||||
if (err != NO_ERROR) {
|
||||
LOG(ERROR) << "idmap string pool corrupt.";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// Can't use make_unique because LoadedImpl constructor is private.
|
||||
std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(
|
||||
new LoadedIdmap(header, data_header, target_entries, overlay_entries,
|
||||
idmap_string_pool.release()));
|
||||
|
||||
return std::move(loaded_idmap);
|
||||
}
|
||||
|
||||
uint8_t LoadedIdmap::TargetPackageId() const {
|
||||
return static_cast<uint8_t>(dtohs(header_->target_package_id));
|
||||
}
|
||||
|
||||
const IdmapEntry_header* LoadedIdmap::GetEntryMapForType(uint8_t type_id) const {
|
||||
auto iter = type_map_.find(type_id);
|
||||
if (iter != type_map_.end()) {
|
||||
return iter->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -51,9 +51,8 @@ namespace {
|
||||
// the Type structs.
|
||||
class TypeSpecPtrBuilder {
|
||||
public:
|
||||
explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header,
|
||||
const IdmapEntry_header* idmap_header)
|
||||
: header_(header), idmap_header_(idmap_header) {
|
||||
explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header)
|
||||
: header_(header) {
|
||||
}
|
||||
|
||||
void AddType(const ResTable_type* type) {
|
||||
@@ -70,7 +69,6 @@ class TypeSpecPtrBuilder {
|
||||
TypeSpec* type_spec =
|
||||
(TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType)));
|
||||
type_spec->type_spec = header_;
|
||||
type_spec->idmap_entries = idmap_header_;
|
||||
type_spec->type_count = types_.size();
|
||||
memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType));
|
||||
return TypeSpecPtr(type_spec);
|
||||
@@ -80,7 +78,6 @@ class TypeSpecPtrBuilder {
|
||||
DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder);
|
||||
|
||||
const ResTable_typeSpec* header_;
|
||||
const IdmapEntry_header* idmap_header_;
|
||||
std::vector<const ResTable_type*> types_;
|
||||
};
|
||||
|
||||
@@ -400,7 +397,6 @@ const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const {
|
||||
}
|
||||
|
||||
std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
|
||||
const LoadedIdmap* loaded_idmap,
|
||||
bool system,
|
||||
bool load_as_shared_library,
|
||||
bool for_loader) {
|
||||
@@ -426,12 +422,6 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
|
||||
loaded_package->dynamic_ = true;
|
||||
}
|
||||
|
||||
if (loaded_idmap != nullptr) {
|
||||
// This is an overlay and so it needs to pretend to be the target package.
|
||||
loaded_package->package_id_ = loaded_idmap->TargetPackageId();
|
||||
loaded_package->overlay_ = true;
|
||||
}
|
||||
|
||||
if (for_loader) {
|
||||
loaded_package->custom_loader_ = true;
|
||||
}
|
||||
@@ -517,16 +507,9 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
|
||||
return {};
|
||||
}
|
||||
|
||||
// If this is an overlay, associate the mapping of this type to the target type
|
||||
// from the IDMAP.
|
||||
const IdmapEntry_header* idmap_entry_header = nullptr;
|
||||
if (loaded_idmap != nullptr) {
|
||||
idmap_entry_header = loaded_idmap->GetEntryMapForType(type_spec->id);
|
||||
}
|
||||
|
||||
std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type_spec->id - 1];
|
||||
if (builder_ptr == nullptr) {
|
||||
builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec, idmap_entry_header);
|
||||
builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec);
|
||||
loaded_package->resource_ids_.set(type_spec->id, entry_count);
|
||||
} else {
|
||||
LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x",
|
||||
@@ -687,15 +670,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
|
||||
return {};
|
||||
}
|
||||
|
||||
// We only add the type to the package if there is no IDMAP, or if the type is
|
||||
// overlaying something.
|
||||
if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) {
|
||||
// If this is an overlay, insert it at the target type ID.
|
||||
if (type_spec_ptr->idmap_entries != nullptr) {
|
||||
type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1;
|
||||
}
|
||||
loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr);
|
||||
}
|
||||
loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr);
|
||||
}
|
||||
|
||||
return std::move(loaded_package);
|
||||
@@ -709,6 +684,10 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (loaded_idmap != nullptr) {
|
||||
global_string_pool_ = util::make_unique<OverlayStringPool>(loaded_idmap);
|
||||
}
|
||||
|
||||
const size_t package_count = dtohl(header->packageCount);
|
||||
size_t packages_seen = 0;
|
||||
|
||||
@@ -720,9 +699,9 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
|
||||
switch (child_chunk.type()) {
|
||||
case RES_STRING_POOL_TYPE:
|
||||
// Only use the first string pool. Ignore others.
|
||||
if (global_string_pool_.getError() == NO_INIT) {
|
||||
status_t err = global_string_pool_.setTo(child_chunk.header<ResStringPool_header>(),
|
||||
child_chunk.size());
|
||||
if (global_string_pool_->getError() == NO_INIT) {
|
||||
status_t err = global_string_pool_->setTo(child_chunk.header<ResStringPool_header>(),
|
||||
child_chunk.size());
|
||||
if (err != NO_ERROR) {
|
||||
LOG(ERROR) << "RES_STRING_POOL_TYPE corrupt.";
|
||||
return false;
|
||||
@@ -741,11 +720,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
|
||||
packages_seen++;
|
||||
|
||||
std::unique_ptr<const LoadedPackage> loaded_package =
|
||||
LoadedPackage::Load(child_chunk,
|
||||
loaded_idmap,
|
||||
system_,
|
||||
load_as_shared_library,
|
||||
for_loader);
|
||||
LoadedPackage::Load(child_chunk, system_, load_as_shared_library, for_loader);
|
||||
if (!loaded_package) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1363,11 +1363,10 @@ int32_t ResXMLParser::getAttributeData(size_t idx) const
|
||||
(((const uint8_t*)tag)
|
||||
+ dtohs(tag->attributeStart)
|
||||
+ (dtohs(tag->attributeSize)*idx));
|
||||
if (attr->typedValue.dataType != Res_value::TYPE_DYNAMIC_REFERENCE ||
|
||||
mTree.mDynamicRefTable == NULL) {
|
||||
if (mTree.mDynamicRefTable == NULL ||
|
||||
!mTree.mDynamicRefTable->requiresLookup(&attr->typedValue)) {
|
||||
return dtohl(attr->typedValue.data);
|
||||
}
|
||||
|
||||
uint32_t data = dtohl(attr->typedValue.data);
|
||||
if (mTree.mDynamicRefTable->lookupResourceId(&data) == NO_ERROR) {
|
||||
return data;
|
||||
@@ -1613,10 +1612,9 @@ uint32_t ResXMLParser::getSourceResourceId() const
|
||||
|
||||
static volatile int32_t gCount = 0;
|
||||
|
||||
ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable)
|
||||
ResXMLTree::ResXMLTree(std::shared_ptr<const DynamicRefTable> dynamicRefTable)
|
||||
: ResXMLParser(*this)
|
||||
, mDynamicRefTable((dynamicRefTable != nullptr) ? dynamicRefTable->clone()
|
||||
: std::unique_ptr<DynamicRefTable>(nullptr))
|
||||
, mDynamicRefTable(std::move(dynamicRefTable))
|
||||
, mError(NO_INIT), mOwnedData(NULL)
|
||||
{
|
||||
if (kDebugResXMLTree) {
|
||||
@@ -1627,7 +1625,7 @@ ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable)
|
||||
|
||||
ResXMLTree::ResXMLTree()
|
||||
: ResXMLParser(*this)
|
||||
, mDynamicRefTable(std::unique_ptr<DynamicRefTable>(nullptr))
|
||||
, mDynamicRefTable(nullptr)
|
||||
, mError(NO_INIT), mOwnedData(NULL)
|
||||
{
|
||||
if (kDebugResXMLTree) {
|
||||
@@ -4789,7 +4787,7 @@ void ResTable::setParameters(const ResTable_config* params)
|
||||
packageGroup->clearBagCache();
|
||||
|
||||
// Find which configurations match the set of parameters. This allows for a much
|
||||
// faster lookup in getEntry() if the set of values is narrowed down.
|
||||
// faster lookup in Lookup() if the set of values is narrowed down.
|
||||
for (size_t t = 0; t < packageGroup->types.size(); t++) {
|
||||
if (packageGroup->types[t].isEmpty()) {
|
||||
continue;
|
||||
@@ -6897,13 +6895,6 @@ DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib)
|
||||
mLookupTable[SYS_PACKAGE_ID] = SYS_PACKAGE_ID;
|
||||
}
|
||||
|
||||
std::unique_ptr<DynamicRefTable> DynamicRefTable::clone() const {
|
||||
std::unique_ptr<DynamicRefTable> clone = std::unique_ptr<DynamicRefTable>(
|
||||
new DynamicRefTable(mAssignedPackageId, mAppAsLib));
|
||||
clone->addMappings(*this);
|
||||
return clone;
|
||||
}
|
||||
|
||||
status_t DynamicRefTable::load(const ResTable_lib_header* const header)
|
||||
{
|
||||
const uint32_t entryCount = dtohl(header->count);
|
||||
@@ -7020,21 +7011,29 @@ status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
bool DynamicRefTable::requiresLookup(const Res_value* value) const {
|
||||
// Only resolve non-dynamic references and attributes if the package is loaded as a
|
||||
// library or if a shared library is attempting to retrieve its own resource
|
||||
if ((value->dataType == Res_value::TYPE_REFERENCE ||
|
||||
value->dataType == Res_value::TYPE_ATTRIBUTE) &&
|
||||
(mAppAsLib || (Res_GETPACKAGE(value->data) + 1) == 0)) {
|
||||
return true;
|
||||
}
|
||||
return value->dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE ||
|
||||
value->dataType == Res_value::TYPE_DYNAMIC_REFERENCE;
|
||||
}
|
||||
|
||||
status_t DynamicRefTable::lookupResourceValue(Res_value* value) const {
|
||||
if (!requiresLookup(value)) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
uint8_t resolvedType = Res_value::TYPE_REFERENCE;
|
||||
switch (value->dataType) {
|
||||
case Res_value::TYPE_ATTRIBUTE:
|
||||
resolvedType = Res_value::TYPE_ATTRIBUTE;
|
||||
FALLTHROUGH_INTENDED;
|
||||
case Res_value::TYPE_REFERENCE:
|
||||
// Only resolve non-dynamic references and attributes if the package is loaded as a
|
||||
// library or if a shared library is attempting to retrieve its own resource
|
||||
if (!(mAppAsLib || (Res_GETPACKAGE(value->data) + 1) == 0)) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
// If the package is loaded as shared library, the resource reference
|
||||
// also need to be fixed.
|
||||
break;
|
||||
case Res_value::TYPE_DYNAMIC_ATTRIBUTE:
|
||||
resolvedType = Res_value::TYPE_ATTRIBUTE;
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
{
|
||||
"name": "libandroidfw_tests",
|
||||
"host": true
|
||||
},
|
||||
{
|
||||
"name": "FrameworksResourceLoaderTests"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "android-base/unique_fd.h"
|
||||
|
||||
#include "androidfw/Asset.h"
|
||||
#include "androidfw/Idmap.h"
|
||||
#include "androidfw/LoadedArsc.h"
|
||||
#include "androidfw/misc.h"
|
||||
|
||||
@@ -95,10 +96,18 @@ class ApkAssets {
|
||||
return loaded_arsc_.get();
|
||||
}
|
||||
|
||||
inline const LoadedIdmap* GetLoadedIdmap() const {
|
||||
return loaded_idmap_.get();
|
||||
}
|
||||
|
||||
inline bool IsOverlay() const {
|
||||
return idmap_asset_.get() != nullptr;
|
||||
}
|
||||
|
||||
inline bool IsLoader() const {
|
||||
return for_loader_;
|
||||
}
|
||||
|
||||
bool IsUpToDate() const;
|
||||
|
||||
// Creates an Asset from any file on the file system.
|
||||
@@ -127,10 +136,11 @@ class ApkAssets {
|
||||
ZipArchivePtr zip_handle_;
|
||||
const std::string path_;
|
||||
time_t last_mod_time_;
|
||||
bool for_loader;
|
||||
bool for_loader_;
|
||||
std::unique_ptr<Asset> resources_asset_;
|
||||
std::unique_ptr<Asset> idmap_asset_;
|
||||
std::unique_ptr<const LoadedArsc> loaded_arsc_;
|
||||
std::unique_ptr<const LoadedIdmap> loaded_idmap_;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -122,7 +122,7 @@ class AssetManager2 {
|
||||
|
||||
// Returns the DynamicRefTable for the ApkAssets represented by the cookie.
|
||||
// This may be nullptr if the APK represented by `cookie` has no resource table.
|
||||
const DynamicRefTable* GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const;
|
||||
std::shared_ptr<const DynamicRefTable> GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const;
|
||||
|
||||
// Returns a string representation of the overlayable API of a package.
|
||||
bool GetOverlayablesToString(const android::StringPiece& package_name,
|
||||
@@ -236,12 +236,14 @@ class AssetManager2 {
|
||||
ResTable_config* in_out_selected_config, uint32_t* in_out_flags,
|
||||
uint32_t* out_last_reference) const;
|
||||
|
||||
// Enables or disables resource resolution logging. Clears stored steps when
|
||||
// disabled.
|
||||
// Resets the resource resolution structures in preparation for the next resource retrieval.
|
||||
void ResetResourceResolution() const;
|
||||
|
||||
// Enables or disables resource resolution logging. Clears stored steps when disabled.
|
||||
void SetResourceResolutionLoggingEnabled(bool enabled);
|
||||
|
||||
// Returns formatted log of last resource resolution path, or empty if no
|
||||
// resource has been resolved yet.
|
||||
// Returns formatted log of last resource resolution path, or empty if no resource has been
|
||||
// resolved yet.
|
||||
std::string GetLastResourceResolution() const;
|
||||
|
||||
const std::vector<uint32_t> GetBagResIdStack(uint32_t resid);
|
||||
@@ -264,7 +266,7 @@ class AssetManager2 {
|
||||
void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func) const {
|
||||
for (const PackageGroup& package_group : package_groups_) {
|
||||
if (!func(package_group.packages_.front().loaded_package_->GetPackageName(),
|
||||
package_group.dynamic_ref_table.mAssignedPackageId)) {
|
||||
package_group.dynamic_ref_table->mAssignedPackageId)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -275,6 +277,50 @@ class AssetManager2 {
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(AssetManager2);
|
||||
|
||||
// A collection of configurations and their associated ResTable_type that match the current
|
||||
// AssetManager configuration.
|
||||
struct FilteredConfigGroup {
|
||||
std::vector<ResTable_config> configurations;
|
||||
std::vector<const ResTable_type*> types;
|
||||
};
|
||||
|
||||
// Represents an single package.
|
||||
struct ConfiguredPackage {
|
||||
// A pointer to the immutable, loaded package info.
|
||||
const LoadedPackage* loaded_package_;
|
||||
|
||||
// A mutable AssetManager-specific list of configurations that match the AssetManager's
|
||||
// current configuration. This is used as an optimization to avoid checking every single
|
||||
// candidate configuration when looking up resources.
|
||||
ByteBucketArray<FilteredConfigGroup> filtered_configs_;
|
||||
};
|
||||
|
||||
// Represents a Runtime Resource Overlay that overlays resources in the logical package.
|
||||
struct ConfiguredOverlay {
|
||||
// The set of package groups that overlay this package group.
|
||||
IdmapResMap overlay_res_maps_;
|
||||
|
||||
// The cookie of the overlay assets.
|
||||
ApkAssetsCookie cookie;
|
||||
};
|
||||
|
||||
// Represents a logical package, which can be made up of many individual packages. Each package
|
||||
// in a PackageGroup shares the same package name and package ID.
|
||||
struct PackageGroup {
|
||||
// The set of packages that make-up this group.
|
||||
std::vector<ConfiguredPackage> packages_;
|
||||
|
||||
// The cookies associated with each package in the group. They share the same order as
|
||||
// packages_.
|
||||
std::vector<ApkAssetsCookie> cookies_;
|
||||
|
||||
// Runtime Resource Overlays that overlay resources in this package group.
|
||||
std::vector<ConfiguredOverlay> overlays_;
|
||||
|
||||
// A library reference table that contains build-package ID to runtime-package ID mappings.
|
||||
std::shared_ptr<DynamicRefTable> dynamic_ref_table = std::make_shared<DynamicRefTable>();
|
||||
};
|
||||
|
||||
// Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple
|
||||
// Res_value, or a complex map/bag type. If successful, it is available in `out_entry`.
|
||||
// Returns kInvalidCookie on failure. Otherwise, the return value is the cookie associated with
|
||||
@@ -295,6 +341,11 @@ class AssetManager2 {
|
||||
ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match,
|
||||
bool ignore_configuration, FindEntryResult* out_entry) const;
|
||||
|
||||
ApkAssetsCookie FindEntryInternal(const PackageGroup& package_group, uint8_t type_idx,
|
||||
uint16_t entry_idx, const ResTable_config& desired_config,
|
||||
bool /*stop_at_first_match*/,
|
||||
bool ignore_configuration, FindEntryResult* out_entry) const;
|
||||
|
||||
// Assigns package IDs to all shared library ApkAssets.
|
||||
// Should be called whenever the ApkAssets are changed.
|
||||
void BuildDynamicRefTable();
|
||||
@@ -307,6 +358,9 @@ class AssetManager2 {
|
||||
// This should always be called when mutating the AssetManager's configuration or ApkAssets set.
|
||||
void RebuildFilterList(bool filter_incompatible_configs = true);
|
||||
|
||||
// Retrieves the APK paths of overlays that overlay non-system packages.
|
||||
std::set<std::string> GetNonSystemOverlayPaths() const;
|
||||
|
||||
// AssetManager2::GetBag(resid) wraps this function to track which resource ids have already
|
||||
// been seen while traversing bag parents.
|
||||
const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids);
|
||||
@@ -318,38 +372,6 @@ class AssetManager2 {
|
||||
// have a longer lifetime.
|
||||
std::vector<const ApkAssets*> apk_assets_;
|
||||
|
||||
// A collection of configurations and their associated ResTable_type that match the current
|
||||
// AssetManager configuration.
|
||||
struct FilteredConfigGroup {
|
||||
std::vector<ResTable_config> configurations;
|
||||
std::vector<const ResTable_type*> types;
|
||||
};
|
||||
|
||||
// Represents an single package.
|
||||
struct ConfiguredPackage {
|
||||
// A pointer to the immutable, loaded package info.
|
||||
const LoadedPackage* loaded_package_;
|
||||
|
||||
// A mutable AssetManager-specific list of configurations that match the AssetManager's
|
||||
// current configuration. This is used as an optimization to avoid checking every single
|
||||
// candidate configuration when looking up resources.
|
||||
ByteBucketArray<FilteredConfigGroup> filtered_configs_;
|
||||
};
|
||||
|
||||
// Represents a logical package, which can be made up of many individual packages. Each package
|
||||
// in a PackageGroup shares the same package name and package ID.
|
||||
struct PackageGroup {
|
||||
// The set of packages that make-up this group.
|
||||
std::vector<ConfiguredPackage> packages_;
|
||||
|
||||
// The cookies associated with each package in the group. They share the same order as
|
||||
// packages_.
|
||||
std::vector<ApkAssetsCookie> cookies_;
|
||||
|
||||
// A library reference table that contains build-package ID to runtime-package ID mappings.
|
||||
DynamicRefTable dynamic_ref_table;
|
||||
};
|
||||
|
||||
// DynamicRefTables for shared library package resolution.
|
||||
// These are ordered according to apk_assets_. The mappings may change depending on what is
|
||||
// in apk_assets_, therefore they must be stored in the AssetManager and not in the
|
||||
@@ -418,7 +440,7 @@ class AssetManager2 {
|
||||
};
|
||||
|
||||
// Record of the last resolved resource's resolution path.
|
||||
mutable Resolution last_resolution;
|
||||
mutable Resolution last_resolution_;
|
||||
};
|
||||
|
||||
class Theme {
|
||||
|
||||
@@ -20,20 +20,122 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
|
||||
#include "android-base/macros.h"
|
||||
|
||||
#include "androidfw/StringPiece.h"
|
||||
#include "androidfw/ResourceTypes.h"
|
||||
#include "utils/ByteOrder.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
struct Idmap_header;
|
||||
struct IdmapEntry_header;
|
||||
class LoadedIdmap;
|
||||
class IdmapResMap;
|
||||
|
||||
// A string pool for overlay apk assets. The string pool holds the strings of the overlay resources
|
||||
// table and additionally allows for loading strings from the idmap string pool. The idmap string
|
||||
// pool strings are offset after the end of the overlay resource table string pool entries so
|
||||
// queries for strings defined inline in the idmap do not conflict with queries for overlay
|
||||
// resource table strings.
|
||||
class OverlayStringPool : public ResStringPool {
|
||||
public:
|
||||
virtual ~OverlayStringPool();
|
||||
virtual const char16_t* stringAt(size_t idx, size_t* outLen) const;
|
||||
virtual const char* string8At(size_t idx, size_t* outLen) const;
|
||||
|
||||
explicit OverlayStringPool(const LoadedIdmap* loaded_idmap);
|
||||
private:
|
||||
const Idmap_data_header* data_header_;
|
||||
const ResStringPool* idmap_string_pool_;
|
||||
};
|
||||
|
||||
// A dynamic reference table for loaded overlay packages that rewrites the resource id of overlay
|
||||
// resources to the resource id of corresponding target resources.
|
||||
class OverlayDynamicRefTable : public DynamicRefTable {
|
||||
public:
|
||||
virtual ~OverlayDynamicRefTable() = default;
|
||||
virtual status_t lookupResourceId(uint32_t* resId) const;
|
||||
|
||||
private:
|
||||
explicit OverlayDynamicRefTable(const Idmap_data_header* data_header,
|
||||
const Idmap_overlay_entry* entries,
|
||||
uint8_t target_assigned_package_id);
|
||||
|
||||
// Rewrites a compile-time overlay resource id to the runtime resource id of corresponding target
|
||||
// resource.
|
||||
virtual status_t lookupResourceIdNoRewrite(uint32_t* resId) const;
|
||||
|
||||
const Idmap_data_header* data_header_;
|
||||
const Idmap_overlay_entry* entries_;
|
||||
const int8_t target_assigned_package_id_;
|
||||
|
||||
friend LoadedIdmap;
|
||||
friend IdmapResMap;
|
||||
};
|
||||
|
||||
// A mapping of target resource ids to a values or resource ids that should overlay the target.
|
||||
class IdmapResMap {
|
||||
public:
|
||||
// Represents the result of a idmap lookup. The result can be one of three possibillities:
|
||||
// 1) The result is a resource id which represents the overlay resource that should act as an
|
||||
// alias of the target resource.
|
||||
// 2) The result is a table entry which overlays the type and value of the target resource.
|
||||
// 3) The result is neither and the target resource is not overlaid.
|
||||
class Result {
|
||||
public:
|
||||
Result() : data_(nullptr) {};
|
||||
explicit Result(uint32_t value) : data_(value) {};
|
||||
explicit Result(ResTable_entry_handle&& value) : data_(value) { };
|
||||
|
||||
// Returns `true` if the resource is overlaid.
|
||||
inline explicit operator bool() const {
|
||||
return !std::get_if<nullptr_t>(&data_);
|
||||
}
|
||||
|
||||
inline bool IsResourceId() const {
|
||||
return std::get_if<uint32_t>(&data_);
|
||||
}
|
||||
|
||||
inline uint32_t GetResourceId() const {
|
||||
return *std::get_if<uint32_t>(&data_);
|
||||
}
|
||||
|
||||
inline bool IsTableEntry() const {
|
||||
return std::get_if<ResTable_entry_handle>(&data_);
|
||||
}
|
||||
|
||||
inline const ResTable_entry_handle& GetTableEntry() const {
|
||||
return *std::get_if<ResTable_entry_handle>(&data_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::variant<uint32_t, nullptr_t, ResTable_entry_handle> data_;
|
||||
};
|
||||
|
||||
// Looks up the value that overlays the target resource id.
|
||||
Result Lookup(uint32_t target_res_id) const;
|
||||
|
||||
inline const OverlayDynamicRefTable* GetOverlayDynamicRefTable() const {
|
||||
return overlay_ref_table_;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit IdmapResMap(const Idmap_data_header* data_header,
|
||||
const Idmap_target_entry* entries,
|
||||
uint8_t target_assigned_package_id,
|
||||
const OverlayDynamicRefTable* overlay_ref_table);
|
||||
|
||||
const Idmap_data_header* data_header_;
|
||||
const Idmap_target_entry* entries_;
|
||||
const uint8_t target_assigned_package_id_;
|
||||
const OverlayDynamicRefTable* overlay_ref_table_;
|
||||
|
||||
friend LoadedIdmap;
|
||||
};
|
||||
|
||||
// Represents a loaded/parsed IDMAP for a Runtime Resource Overlay (RRO).
|
||||
// An RRO and its target APK have different resource IDs assigned to their resources. Overlaying
|
||||
// a resource is done by resource name. An IDMAP is a generated mapping between the resource IDs
|
||||
// of the RRO and the target APK for each resource with the same name.
|
||||
// An RRO and its target APK have different resource IDs assigned to their resources.
|
||||
// An IDMAP is a generated mapping between the resource IDs of the RRO and the target APK.
|
||||
// A LoadedIdmap can be set alongside the overlay's LoadedArsc to allow the overlay ApkAssets to
|
||||
// masquerade as the target ApkAssets resources.
|
||||
class LoadedIdmap {
|
||||
@@ -41,34 +143,52 @@ class LoadedIdmap {
|
||||
// Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed.
|
||||
static std::unique_ptr<const LoadedIdmap> Load(const StringPiece& idmap_data);
|
||||
|
||||
// Performs a lookup of the expected entry ID for the given IDMAP entry header.
|
||||
// Returns true if the mapping exists and fills `output_entry_id` with the result.
|
||||
static bool Lookup(const IdmapEntry_header* header, uint16_t input_entry_id,
|
||||
uint16_t* output_entry_id);
|
||||
|
||||
// Returns the package ID for which this overlay should apply.
|
||||
uint8_t TargetPackageId() const;
|
||||
|
||||
// Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated.
|
||||
inline const std::string& OverlayApkPath() const {
|
||||
return overlay_apk_path_;
|
||||
}
|
||||
|
||||
// Returns the mapping of target entry ID to overlay entry ID for the given target type.
|
||||
const IdmapEntry_header* GetEntryMapForType(uint8_t type_id) const;
|
||||
// Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated.
|
||||
inline const std::string& TargetApkPath() const {
|
||||
return target_apk_path_;
|
||||
}
|
||||
|
||||
// Returns a mapping from target resource ids to overlay values.
|
||||
inline const IdmapResMap GetTargetResourcesMap(
|
||||
uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const {
|
||||
return IdmapResMap(data_header_, target_entries_, target_assigned_package_id,
|
||||
overlay_ref_table);
|
||||
}
|
||||
|
||||
// Returns a dynamic reference table for a loaded overlay package.
|
||||
inline const OverlayDynamicRefTable GetOverlayDynamicRefTable(
|
||||
uint8_t target_assigned_package_id) const {
|
||||
return OverlayDynamicRefTable(data_header_, overlay_entries_, target_assigned_package_id);
|
||||
}
|
||||
|
||||
protected:
|
||||
// Exposed as protected so that tests can subclass and mock this class out.
|
||||
LoadedIdmap() = default;
|
||||
|
||||
const Idmap_header* header_ = nullptr;
|
||||
const Idmap_header* header_;
|
||||
const Idmap_data_header* data_header_;
|
||||
const Idmap_target_entry* target_entries_;
|
||||
const Idmap_overlay_entry* overlay_entries_;
|
||||
const std::unique_ptr<ResStringPool> string_pool_;
|
||||
|
||||
std::string overlay_apk_path_;
|
||||
std::unordered_map<uint8_t, const IdmapEntry_header*> type_map_;
|
||||
std::string target_apk_path_;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(LoadedIdmap);
|
||||
|
||||
explicit LoadedIdmap(const Idmap_header* header);
|
||||
explicit LoadedIdmap(const Idmap_header* header,
|
||||
const Idmap_data_header* data_header,
|
||||
const Idmap_target_entry* target_entries,
|
||||
const Idmap_overlay_entry* overlay_entries,
|
||||
ResStringPool* string_pool);
|
||||
|
||||
friend OverlayStringPool;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -51,10 +51,6 @@ struct TypeSpec {
|
||||
// and under which configurations it varies.
|
||||
const ResTable_typeSpec* type_spec;
|
||||
|
||||
// Pointer to the mmapped data where the IDMAP mappings for this type
|
||||
// exist. May be nullptr if no IDMAP exists.
|
||||
const IdmapEntry_header* idmap_entries;
|
||||
|
||||
// The number of types that follow this struct.
|
||||
// There is a type for each configuration that entries are defined for.
|
||||
size_t type_count;
|
||||
@@ -135,8 +131,7 @@ class LoadedPackage {
|
||||
return iterator(this, resource_ids_.size() + 1, 0);
|
||||
}
|
||||
|
||||
static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk,
|
||||
const LoadedIdmap* loaded_idmap, bool system,
|
||||
static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk, bool system,
|
||||
bool load_as_shared_library,
|
||||
bool load_as_custom_loader);
|
||||
|
||||
@@ -183,11 +178,6 @@ class LoadedPackage {
|
||||
return system_;
|
||||
}
|
||||
|
||||
// Returns true if this package is from an overlay ApkAssets.
|
||||
inline bool IsOverlay() const {
|
||||
return overlay_;
|
||||
}
|
||||
|
||||
// Returns true if this package is a custom loader and should behave like an overlay
|
||||
inline bool IsCustomLoader() const {
|
||||
return custom_loader_;
|
||||
@@ -222,9 +212,6 @@ class LoadedPackage {
|
||||
const TypeSpecPtr& ptr = type_specs_[i];
|
||||
if (ptr != nullptr) {
|
||||
uint8_t type_id = ptr->type_spec->id;
|
||||
if (ptr->idmap_entries != nullptr) {
|
||||
type_id = ptr->idmap_entries->target_type_id;
|
||||
}
|
||||
f(ptr.get(), type_id - 1);
|
||||
}
|
||||
}
|
||||
@@ -265,7 +252,6 @@ class LoadedPackage {
|
||||
int type_id_offset_ = 0;
|
||||
bool dynamic_ = false;
|
||||
bool system_ = false;
|
||||
bool overlay_ = false;
|
||||
bool custom_loader_ = false;
|
||||
bool defines_overlayable_ = false;
|
||||
|
||||
@@ -298,7 +284,7 @@ class LoadedArsc {
|
||||
// Returns the string pool where all string resource values
|
||||
// (Res_value::dataType == Res_value::TYPE_STRING) are indexed.
|
||||
inline const ResStringPool* GetStringPool() const {
|
||||
return &global_string_pool_;
|
||||
return global_string_pool_.get();
|
||||
}
|
||||
|
||||
// Gets a pointer to the package with the specified package ID, or nullptr if no such package
|
||||
@@ -319,12 +305,8 @@ class LoadedArsc {
|
||||
DISALLOW_COPY_AND_ASSIGN(LoadedArsc);
|
||||
|
||||
LoadedArsc() = default;
|
||||
bool LoadTable(
|
||||
const Chunk& chunk,
|
||||
const LoadedIdmap* loaded_idmap,
|
||||
bool load_as_shared_library,
|
||||
bool for_loader
|
||||
);
|
||||
bool LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library,
|
||||
bool for_loader);
|
||||
|
||||
static std::unique_ptr<const LoadedArsc> LoadData(std::unique_ptr<LoadedArsc>& loaded_arsc,
|
||||
const char* data,
|
||||
@@ -333,7 +315,7 @@ class LoadedArsc {
|
||||
bool load_as_shared_library = false,
|
||||
bool for_loader = false);
|
||||
|
||||
ResStringPool global_string_pool_;
|
||||
std::unique_ptr<ResStringPool> global_string_pool_ = util::make_unique<ResStringPool>();
|
||||
std::vector<std::unique_ptr<const LoadedPackage>> packages_;
|
||||
bool system_ = false;
|
||||
};
|
||||
|
||||
@@ -492,7 +492,7 @@ class ResStringPool
|
||||
public:
|
||||
ResStringPool();
|
||||
ResStringPool(const void* data, size_t size, bool copyData=false);
|
||||
~ResStringPool();
|
||||
virtual ~ResStringPool();
|
||||
|
||||
void setToEmpty();
|
||||
status_t setTo(const void* data, size_t size, bool copyData=false);
|
||||
@@ -506,10 +506,10 @@ public:
|
||||
inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const {
|
||||
return stringAt(ref.index, outLen);
|
||||
}
|
||||
const char16_t* stringAt(size_t idx, size_t* outLen) const;
|
||||
virtual const char16_t* stringAt(size_t idx, size_t* outLen) const;
|
||||
|
||||
// Note: returns null if the string pool is not UTF8.
|
||||
const char* string8At(size_t idx, size_t* outLen) const;
|
||||
virtual const char* string8At(size_t idx, size_t* outLen) const;
|
||||
|
||||
// Return string whether the pool is UTF8 or UTF16. Does not allow you
|
||||
// to distinguish null.
|
||||
@@ -812,7 +812,7 @@ public:
|
||||
* The tree stores a clone of the specified DynamicRefTable, so any changes to the original
|
||||
* DynamicRefTable will not affect this tree after instantiation.
|
||||
**/
|
||||
explicit ResXMLTree(const DynamicRefTable* dynamicRefTable);
|
||||
explicit ResXMLTree(std::shared_ptr<const DynamicRefTable> dynamicRefTable);
|
||||
ResXMLTree();
|
||||
~ResXMLTree();
|
||||
|
||||
@@ -827,7 +827,7 @@ private:
|
||||
|
||||
status_t validateNode(const ResXMLTree_node* node) const;
|
||||
|
||||
std::unique_ptr<const DynamicRefTable> mDynamicRefTable;
|
||||
std::shared_ptr<const DynamicRefTable> mDynamicRefTable;
|
||||
|
||||
status_t mError;
|
||||
void* mOwnedData;
|
||||
@@ -1584,6 +1584,50 @@ struct ResTable_map
|
||||
Res_value value;
|
||||
};
|
||||
|
||||
|
||||
// A ResTable_entry variant that either holds an unmanaged pointer to a constant ResTable_entry or
|
||||
// holds a ResTable_entry which is tied to the lifetime of the handle.
|
||||
class ResTable_entry_handle {
|
||||
public:
|
||||
ResTable_entry_handle() = default;
|
||||
|
||||
ResTable_entry_handle(const ResTable_entry_handle& handle) {
|
||||
entry_ = handle.entry_;
|
||||
}
|
||||
|
||||
ResTable_entry_handle(ResTable_entry_handle&& handle) noexcept {
|
||||
entry_ = handle.entry_;
|
||||
}
|
||||
|
||||
inline static ResTable_entry_handle managed(ResTable_entry* entry) {
|
||||
return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry));
|
||||
}
|
||||
|
||||
inline static ResTable_entry_handle unmanaged(const ResTable_entry* entry) {
|
||||
return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry, [](auto /*p */){}));
|
||||
}
|
||||
|
||||
inline ResTable_entry_handle& operator=(const ResTable_entry_handle& handle) noexcept {
|
||||
entry_ = handle.entry_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ResTable_entry_handle& operator=(ResTable_entry_handle&& handle) noexcept {
|
||||
entry_ = handle.entry_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline const ResTable_entry* operator*() & {
|
||||
return entry_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ResTable_entry_handle(std::shared_ptr<const ResTable_entry> entry)
|
||||
: entry_(std::move(entry)) { }
|
||||
|
||||
std::shared_ptr<const ResTable_entry> entry_;
|
||||
};
|
||||
|
||||
/**
|
||||
* A package-id to package name mapping for any shared libraries used
|
||||
* in this resource table. The package-id's encoded in this resource
|
||||
@@ -1668,7 +1712,8 @@ struct ResTable_overlayable_policy_header
|
||||
uint32_t entry_count;
|
||||
};
|
||||
|
||||
struct alignas(uint32_t) Idmap_header {
|
||||
#pragma pack(push, 1)
|
||||
struct Idmap_header {
|
||||
// Always 0x504D4449 ('IDMP')
|
||||
uint32_t magic;
|
||||
|
||||
@@ -1679,18 +1724,28 @@ struct alignas(uint32_t) Idmap_header {
|
||||
|
||||
uint8_t target_path[256];
|
||||
uint8_t overlay_path[256];
|
||||
};
|
||||
|
||||
uint16_t target_package_id;
|
||||
uint16_t type_count;
|
||||
} __attribute__((packed));
|
||||
struct Idmap_data_header {
|
||||
uint8_t target_package_id;
|
||||
uint8_t overlay_package_id;
|
||||
uint32_t target_entry_count;
|
||||
uint32_t overlay_entry_count;
|
||||
uint32_t string_pool_index_offset;
|
||||
uint32_t string_pool_length;
|
||||
};
|
||||
|
||||
struct alignas(uint32_t) IdmapEntry_header {
|
||||
uint16_t target_type_id;
|
||||
uint16_t overlay_type_id;
|
||||
uint16_t entry_count;
|
||||
uint16_t entry_id_offset;
|
||||
uint32_t entries[0];
|
||||
} __attribute__((packed));
|
||||
struct Idmap_target_entry {
|
||||
uint32_t target_id;
|
||||
uint8_t type;
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
struct Idmap_overlay_entry {
|
||||
uint32_t overlay_id;
|
||||
uint32_t target_id;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
class AssetManager2;
|
||||
|
||||
@@ -1708,6 +1763,7 @@ class DynamicRefTable
|
||||
public:
|
||||
DynamicRefTable();
|
||||
DynamicRefTable(uint8_t packageId, bool appAsLib);
|
||||
virtual ~DynamicRefTable() = default;
|
||||
|
||||
// Loads an unmapped reference table from the package.
|
||||
status_t load(const ResTable_lib_header* const header);
|
||||
@@ -1721,12 +1777,12 @@ public:
|
||||
|
||||
void addMapping(uint8_t buildPackageId, uint8_t runtimePackageId);
|
||||
|
||||
// Creates a new clone of the reference table
|
||||
std::unique_ptr<DynamicRefTable> clone() const;
|
||||
// Returns whether or not the value must be looked up.
|
||||
bool requiresLookup(const Res_value* value) const;
|
||||
|
||||
// Performs the actual conversion of build-time resource ID to run-time
|
||||
// resource ID.
|
||||
status_t lookupResourceId(uint32_t* resId) const;
|
||||
virtual status_t lookupResourceId(uint32_t* resId) const;
|
||||
status_t lookupResourceValue(Res_value* value) const;
|
||||
|
||||
inline const KeyedVector<String16, uint8_t>& entries() const {
|
||||
|
||||
@@ -79,39 +79,6 @@ TEST(ApkAssetsTest, LoadApkAsSharedLibrary) {
|
||||
EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic());
|
||||
}
|
||||
|
||||
TEST(ApkAssetsTest, LoadApkWithIdmap) {
|
||||
std::string contents;
|
||||
ResTable target_table;
|
||||
const std::string target_path = GetTestDataPath() + "/basic/basic.apk";
|
||||
ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents));
|
||||
ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/),
|
||||
Eq(NO_ERROR));
|
||||
|
||||
ResTable overlay_table;
|
||||
const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk";
|
||||
ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents));
|
||||
ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/),
|
||||
Eq(NO_ERROR));
|
||||
|
||||
util::unique_cptr<void> idmap_data;
|
||||
void* temp_data;
|
||||
size_t idmap_len;
|
||||
|
||||
ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(),
|
||||
overlay_path.c_str(), &temp_data, &idmap_len),
|
||||
Eq(NO_ERROR));
|
||||
idmap_data.reset(temp_data);
|
||||
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(base::WriteFully(tf.fd, idmap_data.get(), idmap_len));
|
||||
close(tf.fd);
|
||||
|
||||
// Open something so that the destructor of TemporaryFile closes a valid fd.
|
||||
tf.fd = open("/dev/null", O_WRONLY);
|
||||
|
||||
ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull());
|
||||
}
|
||||
|
||||
TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) {
|
||||
std::unique_ptr<const ApkAssets> loaded_apk =
|
||||
ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
|
||||
|
||||
@@ -718,15 +718,17 @@ TEST_F(AssetManager2Test, GetOverlayablesToString) {
|
||||
|
||||
const auto map = assetmanager.GetOverlayableMapForPackage(0x7f);
|
||||
ASSERT_NE(nullptr, map);
|
||||
ASSERT_EQ(2, map->size());
|
||||
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
|
||||
|
||||
@@ -14,114 +14,231 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/file.h"
|
||||
#include "androidfw/ApkAssets.h"
|
||||
#include "androidfw/AssetManager2.h"
|
||||
#include "androidfw/ResourceTypes.h"
|
||||
|
||||
#include "utils/String16.h"
|
||||
#include "utils/String8.h"
|
||||
|
||||
#include "TestHelpers.h"
|
||||
#include "data/basic/R.h"
|
||||
#include "data/overlay/R.h"
|
||||
#include "data/overlayable/R.h"
|
||||
#include "data/system/R.h"
|
||||
|
||||
using ::com::android::basic::R;
|
||||
namespace overlay = com::android::overlay;
|
||||
namespace overlayable = com::android::overlayable;
|
||||
|
||||
namespace android {
|
||||
|
||||
namespace {
|
||||
|
||||
class IdmapTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
std::string contents;
|
||||
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc",
|
||||
&contents));
|
||||
ASSERT_EQ(NO_ERROR, target_table_.add(contents.data(), contents.size(), 0, true));
|
||||
// Move to the test data directory so the idmap can locate the overlay APK.
|
||||
std::string original_path = base::GetExecutableDirectory();
|
||||
chdir(GetTestDataPath().c_str());
|
||||
|
||||
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk",
|
||||
"resources.arsc", &overlay_data_));
|
||||
ResTable overlay_table;
|
||||
ASSERT_EQ(NO_ERROR, overlay_table.add(overlay_data_.data(), overlay_data_.size()));
|
||||
system_assets_ = ApkAssets::Load("system/system.apk");
|
||||
ASSERT_NE(nullptr, system_assets_);
|
||||
|
||||
char target_name[256] = "com.android.basic";
|
||||
ASSERT_EQ(NO_ERROR, overlay_table.createIdmap(target_table_, 0, 0, target_name, target_name,
|
||||
&data_, &data_size_));
|
||||
overlay_assets_ = ApkAssets::LoadOverlay("overlay/overlay.idmap");
|
||||
ASSERT_NE(nullptr, overlay_assets_);
|
||||
|
||||
overlayable_assets_ = ApkAssets::Load("overlayable/overlayable.apk");
|
||||
ASSERT_NE(nullptr, overlayable_assets_);
|
||||
chdir(original_path.c_str());
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
::free(data_);
|
||||
}
|
||||
|
||||
ResTable target_table_;
|
||||
std::string overlay_data_;
|
||||
void* data_ = nullptr;
|
||||
size_t data_size_ = 0;
|
||||
protected:
|
||||
std::unique_ptr<const ApkAssets> system_assets_;
|
||||
std::unique_ptr<const ApkAssets> overlay_assets_;
|
||||
std::unique_ptr<const ApkAssets> overlayable_assets_;
|
||||
};
|
||||
|
||||
TEST_F(IdmapTest, CanLoadIdmap) {
|
||||
ASSERT_EQ(NO_ERROR,
|
||||
target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_));
|
||||
std::string GetStringFromApkAssets(const AssetManager2& asset_manager, const Res_value& value,
|
||||
ApkAssetsCookie cookie) {
|
||||
auto assets = asset_manager.GetApkAssets();
|
||||
const ResStringPool* string_pool = assets[cookie]->GetLoadedArsc()->GetStringPool();
|
||||
return GetStringFromPool(string_pool, value.data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST_F(IdmapTest, OverlayOverridesResourceValue) {
|
||||
AssetManager2 asset_manager;
|
||||
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
|
||||
overlay_assets_.get()});
|
||||
Res_value val;
|
||||
ssize_t block = target_table_.getResource(R::string::test2, &val, false);
|
||||
ASSERT_GE(block, 0);
|
||||
ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
|
||||
const ResStringPool* pool = target_table_.getTableStringBlock(block);
|
||||
ASSERT_TRUE(pool != NULL);
|
||||
ASSERT_LT(val.data, pool->size());
|
||||
ResTable_config config;
|
||||
uint32_t flags;
|
||||
ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable5,
|
||||
false /* may_be_bag */,
|
||||
0 /* density_override */, &val, &config,
|
||||
&flags);
|
||||
ASSERT_EQ(cookie, 2U);
|
||||
ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
|
||||
ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Overlay One");
|
||||
}
|
||||
|
||||
size_t str_len;
|
||||
const char16_t* target_str16 = pool->stringAt(val.data, &str_len);
|
||||
ASSERT_TRUE(target_str16 != NULL);
|
||||
ASSERT_EQ(String16("test2"), String16(target_str16, str_len));
|
||||
TEST_F(IdmapTest, OverlayOverridesResourceValueUsingDifferentPackage) {
|
||||
AssetManager2 asset_manager;
|
||||
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
|
||||
overlay_assets_.get()});
|
||||
Res_value val;
|
||||
ResTable_config config;
|
||||
uint32_t flags;
|
||||
ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable10,
|
||||
false /* may_be_bag */,
|
||||
0 /* density_override */, &val, &config,
|
||||
&flags);
|
||||
ASSERT_EQ(cookie, 0U);
|
||||
ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
|
||||
ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "yes");
|
||||
}
|
||||
|
||||
ASSERT_EQ(NO_ERROR,
|
||||
target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_));
|
||||
TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInternalResource) {
|
||||
AssetManager2 asset_manager;
|
||||
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
|
||||
overlay_assets_.get()});
|
||||
Res_value val;
|
||||
ResTable_config config;
|
||||
uint32_t flags;
|
||||
ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable8,
|
||||
false /* may_be_bag */,
|
||||
0 /* density_override */, &val, &config,
|
||||
&flags);
|
||||
ASSERT_EQ(cookie, 2U);
|
||||
ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE);
|
||||
ASSERT_EQ(val.data, (overlay::R::string::internal & 0x00ffffff) | (0x02 << 24));
|
||||
}
|
||||
|
||||
ssize_t new_block = target_table_.getResource(R::string::test2, &val, false);
|
||||
ASSERT_GE(new_block, 0);
|
||||
ASSERT_NE(block, new_block);
|
||||
ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
|
||||
pool = target_table_.getTableStringBlock(new_block);
|
||||
ASSERT_TRUE(pool != NULL);
|
||||
ASSERT_LT(val.data, pool->size());
|
||||
TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineInteger) {
|
||||
AssetManager2 asset_manager;
|
||||
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
|
||||
overlay_assets_.get()});
|
||||
Res_value val;
|
||||
ResTable_config config;
|
||||
uint32_t flags;
|
||||
ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::integer::config_integer,
|
||||
false /* may_be_bag */,
|
||||
0 /* density_override */, &val, &config,
|
||||
&flags);
|
||||
ASSERT_EQ(cookie, 2U);
|
||||
ASSERT_EQ(val.dataType, Res_value::TYPE_INT_DEC);
|
||||
ASSERT_EQ(val.data, 42);
|
||||
}
|
||||
|
||||
target_str16 = pool->stringAt(val.data, &str_len);
|
||||
ASSERT_TRUE(target_str16 != NULL);
|
||||
ASSERT_EQ(String16("test2-overlay"), String16(target_str16, str_len));
|
||||
TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineString) {
|
||||
AssetManager2 asset_manager;
|
||||
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
|
||||
overlay_assets_.get()});
|
||||
Res_value val;
|
||||
ResTable_config config;
|
||||
uint32_t flags;
|
||||
|
||||
ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11,
|
||||
false /* may_be_bag */,
|
||||
0 /* density_override */, &val, &config,
|
||||
&flags);
|
||||
ASSERT_EQ(cookie, 2U);
|
||||
ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
|
||||
ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Hardcoded string");
|
||||
}
|
||||
|
||||
TEST_F(IdmapTest, OverlayOverridesResourceValueUsingOverlayingResource) {
|
||||
AssetManager2 asset_manager;
|
||||
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
|
||||
overlay_assets_.get()});
|
||||
Res_value val;
|
||||
ResTable_config config;
|
||||
uint32_t flags;
|
||||
ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable9,
|
||||
false /* may_be_bag */,
|
||||
0 /* density_override */, &val, &config,
|
||||
&flags);
|
||||
ASSERT_EQ(cookie, 2U);
|
||||
ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE);
|
||||
ASSERT_EQ(val.data, overlayable::R::string::overlayable7);
|
||||
}
|
||||
|
||||
TEST_F(IdmapTest, OverlayOverridesXmlParser) {
|
||||
AssetManager2 asset_manager;
|
||||
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
|
||||
overlay_assets_.get()});
|
||||
Res_value val;
|
||||
ResTable_config config;
|
||||
uint32_t flags;
|
||||
ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::layout::hello_view,
|
||||
false /* may_be_bag */,
|
||||
0 /* density_override */, &val, &config,
|
||||
&flags);
|
||||
ASSERT_EQ(cookie, 2U);
|
||||
ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
|
||||
ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "res/layout/hello_view.xml");
|
||||
|
||||
auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", cookie,
|
||||
Asset::ACCESS_RANDOM);
|
||||
auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(cookie);
|
||||
auto xml_tree = util::make_unique<ResXMLTree>(std::move(dynamic_ref_table));
|
||||
status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), false);
|
||||
ASSERT_EQ(err, NO_ERROR);
|
||||
|
||||
while (xml_tree->next() != ResXMLParser::START_TAG) { }
|
||||
|
||||
// The resource id of @id/hello_view should be rewritten to the resource id/hello_view within the
|
||||
// target.
|
||||
ASSERT_EQ(xml_tree->getAttributeNameResID(0), 0x010100d0 /* android:attr/id */);
|
||||
ASSERT_EQ(xml_tree->getAttributeDataType(0), Res_value::TYPE_REFERENCE);
|
||||
ASSERT_EQ(xml_tree->getAttributeData(0), overlayable::R::id::hello_view);
|
||||
|
||||
// The resource id of @android:string/yes should not be rewritten even though it overlays
|
||||
// string/overlayable10 in the target.
|
||||
ASSERT_EQ(xml_tree->getAttributeNameResID(1), 0x0101014f /* android:attr/text */);
|
||||
ASSERT_EQ(xml_tree->getAttributeDataType(1), Res_value::TYPE_REFERENCE);
|
||||
ASSERT_EQ(xml_tree->getAttributeData(1), 0x01040013 /* android:string/yes */);
|
||||
|
||||
// The resource id of the attribute within the overlay should be rewritten to the resource id of
|
||||
// the attribute in the target.
|
||||
ASSERT_EQ(xml_tree->getAttributeNameResID(2), overlayable::R::attr::max_lines);
|
||||
ASSERT_EQ(xml_tree->getAttributeDataType(2), Res_value::TYPE_INT_DEC);
|
||||
ASSERT_EQ(xml_tree->getAttributeData(2), 4);
|
||||
}
|
||||
|
||||
TEST_F(IdmapTest, OverlaidResourceHasSameName) {
|
||||
ASSERT_EQ(NO_ERROR,
|
||||
target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_));
|
||||
AssetManager2 asset_manager;
|
||||
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
|
||||
overlay_assets_.get()});
|
||||
|
||||
ResTable::resource_name res_name;
|
||||
ASSERT_TRUE(target_table_.getResourceName(R::array::integerArray1, true, &res_name));
|
||||
|
||||
ASSERT_TRUE(res_name.package != NULL);
|
||||
ASSERT_TRUE(res_name.type != NULL);
|
||||
ASSERT_TRUE(res_name.name8 != NULL);
|
||||
|
||||
EXPECT_EQ(String16("com.android.basic"), String16(res_name.package, res_name.packageLen));
|
||||
EXPECT_EQ(String16("array"), String16(res_name.type, res_name.typeLen));
|
||||
EXPECT_EQ(String8("integerArray1"), String8(res_name.name8, res_name.nameLen));
|
||||
AssetManager2::ResourceName name;
|
||||
ASSERT_TRUE(asset_manager.GetResourceName(overlayable::R::string::overlayable9, &name));
|
||||
ASSERT_EQ(std::string(name.package), "com.android.overlayable");
|
||||
ASSERT_EQ(String16(name.type16), u"string");
|
||||
ASSERT_EQ(std::string(name.entry), "overlayable9");
|
||||
}
|
||||
|
||||
constexpr const uint32_t kNonOverlaidResourceId = 0x7fff0000u;
|
||||
TEST_F(IdmapTest, OverlayLoaderInterop) {
|
||||
std::string contents;
|
||||
auto loader_assets = ApkAssets::LoadArsc(GetTestDataPath() + "/loader/resources.arsc",
|
||||
/* for_loader */ true);
|
||||
|
||||
TEST_F(IdmapTest, OverlayDoesNotIncludeNonOverlaidResources) {
|
||||
// First check that the resource we're trying to not include when overlaid is present when
|
||||
// the overlay is loaded as a standalone APK.
|
||||
ResTable table;
|
||||
ASSERT_EQ(NO_ERROR, table.add(overlay_data_.data(), overlay_data_.size(), 0, true));
|
||||
AssetManager2 asset_manager;
|
||||
asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(),
|
||||
overlay_assets_.get()});
|
||||
|
||||
Res_value val;
|
||||
ssize_t block = table.getResource(kNonOverlaidResourceId, &val, false /*mayBeBag*/);
|
||||
ASSERT_GE(block, 0);
|
||||
|
||||
// Now add the overlay and verify that the unoverlaid resource is gone.
|
||||
ASSERT_EQ(NO_ERROR,
|
||||
target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_));
|
||||
block = target_table_.getResource(kNonOverlaidResourceId, &val, false /*mayBeBag*/);
|
||||
ASSERT_LT(block, 0);
|
||||
ResTable_config config;
|
||||
uint32_t flags;
|
||||
ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11,
|
||||
false /* may_be_bag */,
|
||||
0 /* density_override */, &val, &config,
|
||||
&flags);
|
||||
std::cout << asset_manager.GetLastResourceResolution();
|
||||
ASSERT_EQ(cookie, 1U);
|
||||
ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
|
||||
ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "loader");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -144,7 +144,7 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) {
|
||||
"resources.arsc", &contents));
|
||||
|
||||
std::unique_ptr<const LoadedArsc> loaded_arsc =
|
||||
LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/,
|
||||
LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, false /*system*/,
|
||||
true /*load_as_shared_library*/);
|
||||
ASSERT_THAT(loaded_arsc, NotNull());
|
||||
|
||||
@@ -222,67 +222,13 @@ TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) {
|
||||
ASSERT_THAT(type_spec->types[0], NotNull());
|
||||
}
|
||||
|
||||
class MockLoadedIdmap : public LoadedIdmap {
|
||||
public:
|
||||
MockLoadedIdmap() : LoadedIdmap() {
|
||||
local_header_.magic = kIdmapMagic;
|
||||
local_header_.version = kIdmapCurrentVersion;
|
||||
local_header_.target_package_id = 0x08;
|
||||
local_header_.type_count = 1;
|
||||
header_ = &local_header_;
|
||||
|
||||
entry_header = util::unique_cptr<IdmapEntry_header>(
|
||||
(IdmapEntry_header*)::malloc(sizeof(IdmapEntry_header) + sizeof(uint32_t)));
|
||||
entry_header->target_type_id = 0x03;
|
||||
entry_header->overlay_type_id = 0x02;
|
||||
entry_header->entry_id_offset = 1;
|
||||
entry_header->entry_count = 1;
|
||||
entry_header->entries[0] = 0x00000000u;
|
||||
type_map_[entry_header->overlay_type_id] = entry_header.get();
|
||||
}
|
||||
|
||||
private:
|
||||
Idmap_header local_header_;
|
||||
util::unique_cptr<IdmapEntry_header> entry_header;
|
||||
};
|
||||
|
||||
TEST(LoadedArscTest, LoadOverlay) {
|
||||
std::string contents;
|
||||
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc",
|
||||
&contents));
|
||||
|
||||
MockLoadedIdmap loaded_idmap;
|
||||
|
||||
std::unique_ptr<const LoadedArsc> loaded_arsc =
|
||||
LoadedArsc::Load(StringPiece(contents), &loaded_idmap);
|
||||
ASSERT_THAT(loaded_arsc, NotNull());
|
||||
|
||||
const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u);
|
||||
ASSERT_THAT(package, NotNull());
|
||||
|
||||
const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1);
|
||||
ASSERT_THAT(type_spec, NotNull());
|
||||
ASSERT_THAT(type_spec->type_count, Ge(1u));
|
||||
ASSERT_THAT(type_spec->types[0], NotNull());
|
||||
|
||||
// The entry being overlaid doesn't exist at the original entry index.
|
||||
ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull());
|
||||
|
||||
// Since this is an overlay, the actual entry ID must be mapped.
|
||||
ASSERT_THAT(type_spec->idmap_entries, NotNull());
|
||||
uint16_t target_entry_id = 0u;
|
||||
ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id));
|
||||
ASSERT_THAT(target_entry_id, Eq(0x0u));
|
||||
ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull());
|
||||
}
|
||||
|
||||
TEST(LoadedArscTest, LoadOverlayable) {
|
||||
std::string contents;
|
||||
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk",
|
||||
"resources.arsc", &contents));
|
||||
|
||||
std::unique_ptr<const LoadedArsc> loaded_arsc =
|
||||
LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/,
|
||||
LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, false /*system*/,
|
||||
false /*load_as_shared_library*/);
|
||||
|
||||
ASSERT_THAT(loaded_arsc, NotNull());
|
||||
@@ -383,9 +329,10 @@ TEST(LoadedArscTest, GetOverlayableMap) {
|
||||
ASSERT_EQ(std::string("com.android.overlayable"), packages[0]->GetPackageName());
|
||||
|
||||
const auto map = packages[0]->GetOverlayableMap();
|
||||
ASSERT_EQ(2, map.size());
|
||||
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"), "");
|
||||
}
|
||||
|
||||
TEST(LoadedArscTest, LoadCustomLoader) {
|
||||
@@ -394,7 +341,6 @@ TEST(LoadedArscTest, LoadCustomLoader) {
|
||||
std::unique_ptr<Asset>
|
||||
asset = ApkAssets::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
|
||||
|
||||
MockLoadedIdmap loaded_idmap;
|
||||
const StringPiece data(
|
||||
reinterpret_cast<const char*>(asset->getBuffer(true /*wordAligned*/)),
|
||||
asset->getLength());
|
||||
@@ -404,13 +350,13 @@ TEST(LoadedArscTest, LoadCustomLoader) {
|
||||
ASSERT_THAT(loaded_arsc, NotNull());
|
||||
|
||||
const LoadedPackage* package =
|
||||
loaded_arsc->GetPackageById(get_package_id(android::R::string::cancel));
|
||||
loaded_arsc->GetPackageById(get_package_id(overlayable::R::string::overlayable11));
|
||||
ASSERT_THAT(package, NotNull());
|
||||
EXPECT_THAT(package->GetPackageName(), StrEq("android"));
|
||||
EXPECT_THAT(package->GetPackageId(), Eq(0x01));
|
||||
EXPECT_THAT(package->GetPackageName(), StrEq("com.android.loader"));
|
||||
EXPECT_THAT(package->GetPackageId(), Eq(0x7f));
|
||||
|
||||
const uint8_t type_index = get_type_id(android::R::string::cancel) - 1;
|
||||
const uint16_t entry_index = get_entry_id(android::R::string::cancel);
|
||||
const uint8_t type_index = get_type_id(overlayable::R::string::overlayable11) - 1;
|
||||
const uint16_t entry_index = get_entry_id(overlayable::R::string::overlayable11);
|
||||
|
||||
const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
|
||||
ASSERT_THAT(type_spec, NotNull());
|
||||
|
||||
20
libs/androidfw/tests/data/loader/AndroidManifest.xml
Normal file
20
libs/androidfw/tests/data/loader/AndroidManifest.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2019 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.
|
||||
-->
|
||||
|
||||
<manifest package="com.android.loader">
|
||||
<application>
|
||||
</application>
|
||||
</manifest>
|
||||
27
libs/androidfw/tests/data/loader/build
Executable file
27
libs/androidfw/tests/data/loader/build
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (C) 2018 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
|
||||
|
||||
FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk
|
||||
|
||||
rm resources.arsc
|
||||
aapt2 compile --dir res -o compiled.flata
|
||||
aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o loader.apk compiled.flata
|
||||
unzip loader.apk resources.arsc
|
||||
rm loader.apk
|
||||
rm compiled.flata
|
||||
19
libs/androidfw/tests/data/loader/res/values/public.xml
Normal file
19
libs/androidfw/tests/data/loader/res/values/public.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2019 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<public type="string" name="overlayable11" id="0x7f01000b" />
|
||||
</resources>
|
||||
19
libs/androidfw/tests/data/loader/res/values/values.xml
Normal file
19
libs/androidfw/tests/data/loader/res/values/values.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2019 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="overlayable11">loader</string>
|
||||
</resources>
|
||||
Binary file not shown.
@@ -15,7 +15,9 @@
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.test.basic">
|
||||
<application>
|
||||
</application>
|
||||
package="com.android.test.overlay">
|
||||
<overlay
|
||||
android:targetPackage="com.android.test.basic"
|
||||
android:targetName="OverlayableResources3"
|
||||
android:resourcesMap="@xml/overlays"/>
|
||||
</manifest>
|
||||
|
||||
38
libs/androidfw/tests/data/overlay/R.h
Normal file
38
libs/androidfw/tests/data/overlay/R.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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 TESTS_DATA_OVERLAY_R_H_
|
||||
#define TESTS_DATA_OVERLAY_R_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace com {
|
||||
namespace android {
|
||||
namespace overlay {
|
||||
|
||||
struct R {
|
||||
struct string {
|
||||
enum : uint32_t {
|
||||
internal = 0x7f040000,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace overlay
|
||||
} // namespace android
|
||||
} // namespace com
|
||||
|
||||
#endif /* TESTS_DATA_OVERLAY_R_H_ */
|
||||
@@ -17,6 +17,15 @@
|
||||
|
||||
set -e
|
||||
|
||||
FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk
|
||||
|
||||
aapt2 compile --dir res -o compiled.flata
|
||||
aapt2 link --manifest AndroidManifest.xml -o overlay.apk compiled.flata
|
||||
aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o overlay.apk compiled.flata \
|
||||
--no-auto-version
|
||||
rm compiled.flata
|
||||
|
||||
# Navigate back a directory so the idmap can find the overlays in the test data directory when being
|
||||
# loaded during testing.
|
||||
cd ../
|
||||
idmap2 create --target-apk-path overlayable/overlayable.apk \
|
||||
--overlay-apk-path overlay/overlay.apk --idmap-path overlay/overlay.idmap
|
||||
|
||||
Binary file not shown.
BIN
libs/androidfw/tests/data/overlay/overlay.idmap
Normal file
BIN
libs/androidfw/tests/data/overlay/overlay.idmap
Normal file
Binary file not shown.
20
libs/androidfw/tests/data/overlay/res/layout/hello_view.xml
Normal file
20
libs/androidfw/tests/data/overlay/res/layout/hello_view.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2019 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.
|
||||
-->
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/hello_view"
|
||||
android:text="@android:string/yes"
|
||||
app:max_lines="4"/>
|
||||
@@ -13,13 +13,11 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="test2">test2-overlay</string>
|
||||
<integer-array name="integerArray1">
|
||||
<item>10</item>
|
||||
<item>11</item>
|
||||
</integer-array>
|
||||
<public type="animator" name="unoverlaid" id="0x7fff0000" />
|
||||
<item type="animator" name="unoverlaid">@null</item>
|
||||
<string name="overlay1">Overlay One</string>
|
||||
<string name="overlay2">Overlay Two</string>
|
||||
<string name="overlay3">@string/internal</string>
|
||||
<string name="overlay4">@string/overlay2</string>
|
||||
<string name="internal">Internal</string>
|
||||
<attr name="max_lines" format="integer" />
|
||||
</resources>
|
||||
|
||||
47
libs/androidfw/tests/data/overlay/res/xml/overlays.xml
Normal file
47
libs/androidfw/tests/data/overlay/res/xml/overlays.xml
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2019 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 language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<overlay>
|
||||
<!-- Overlays string/overlayable5 with the string "Overlay One". -->
|
||||
<item target="string/overlayable5" value="@string/overlay1"/>
|
||||
|
||||
<!-- Overlays string/overlayable6 and string/overlayable7 with the string "Overlay Two". -->
|
||||
<item target="string/overlayable7" value="@string/overlay2" />
|
||||
<item target="string/overlayable6" value="@string/overlay2" />
|
||||
|
||||
<!-- Overlays string/overlayable8 with a reference to @string/internal. -->
|
||||
<item target="string/overlayable8" value="@string/overlay3" />
|
||||
|
||||
<!-- Overlays string/overlayable9 with a reference to @string/overlay2. The reference to
|
||||
@string/overlay2 should be rewritten to @string/overlayable7 in the target. -->
|
||||
<item target="string/overlayable9" value="@string/overlay4" />
|
||||
|
||||
<!-- Overlays string/overlayable10 with the string "yes". -->
|
||||
<item target="string/overlayable10" value="@android:string/yes" />
|
||||
|
||||
<!-- Overlays string/overlayable11 with the string "Hardcoded string". -->
|
||||
<item target="string/overlayable11" value="Hardcoded string" />
|
||||
|
||||
<!-- Overlays string/overlayable10 with the string "yes". -->
|
||||
<item target="integer/config_int" value="42" />
|
||||
|
||||
<!-- @attr/max_lines and @id/hello_view should be rewritten to @attr/max_lines and
|
||||
@id/hello_view in the target. -->
|
||||
<item target="layout/hello_view" value="@layout/hello_view" />
|
||||
<item target="attr/max_lines" value="@attr/max_lines" />
|
||||
<item target="id/hello_view" value="@id/hello_view" />
|
||||
</overlay>
|
||||
|
||||
|
||||
@@ -31,6 +31,43 @@ struct R {
|
||||
overlayable2 = 0x7f010002,
|
||||
overlayable3 = 0x7f010003,
|
||||
overlayable4 = 0x7f010004,
|
||||
overlayable5 = 0x7f010005,
|
||||
overlayable6 = 0x7f010006,
|
||||
overlayable7 = 0x7f010007,
|
||||
overlayable8 = 0x7f010008,
|
||||
overlayable9 = 0x7f010009,
|
||||
overlayable10 = 0x7f01000a,
|
||||
overlayable11 = 0x7f01000b,
|
||||
};
|
||||
};
|
||||
|
||||
struct attr {
|
||||
enum : uint32_t {
|
||||
max_lines = 0x7f020000,
|
||||
};
|
||||
};
|
||||
|
||||
struct boolean {
|
||||
enum : uint32_t {
|
||||
config_bool = 0x7f030000,
|
||||
};
|
||||
};
|
||||
|
||||
struct id {
|
||||
enum : uint32_t {
|
||||
hello_view = 0x7f040000,
|
||||
};
|
||||
};
|
||||
|
||||
struct integer {
|
||||
enum : uint32_t {
|
||||
config_integer = 0x7f050000,
|
||||
};
|
||||
};
|
||||
|
||||
struct layout {
|
||||
enum : uint32_t {
|
||||
hello_view = 0x7f060000,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
|
||||
set -e
|
||||
|
||||
FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk
|
||||
|
||||
aapt2 compile --dir res -o compiled.flata
|
||||
aapt2 link --manifest AndroidManifest.xml -o overlayable.apk compiled.flata
|
||||
aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o overlayable.apk compiled.flata \
|
||||
--no-auto-version
|
||||
rm compiled.flata
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2019 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.
|
||||
-->
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/hello_view"
|
||||
android:text="None"
|
||||
app:max_lines="0"/>
|
||||
@@ -15,27 +15,41 @@
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<overlayable name="OverlayableResources1" actor="overlay://theme">
|
||||
<!-- Any overlay can overlay the value of @string/overlayable1 -->
|
||||
<item type="string" name="overlayable1" />
|
||||
<overlayable name="OverlayableResources1" actor="overlay://theme">
|
||||
<!-- Any overlay on the product or system partition can overlay the value of
|
||||
@string/overlayable2 -->
|
||||
<policy type="product|system">
|
||||
<item type="string" name="overlayable2" />
|
||||
</policy>
|
||||
|
||||
<!-- Any overlay on the product or system partition can overlay the value of
|
||||
@string/overlayable2 -->
|
||||
<policy type="product|system">
|
||||
<item type="string" name="overlayable2" />
|
||||
</policy>
|
||||
<!-- Any overlay can overlay the value of @string/overlayable4 -->
|
||||
<policy type="public">
|
||||
<item type="string" name="overlayable1" />
|
||||
<item type="string" name="overlayable4" />
|
||||
</policy>
|
||||
</overlayable>
|
||||
|
||||
<!-- Any overlay can overlay the value of @string/overlayable4 -->
|
||||
<policy type="public">
|
||||
<item type="string" name="overlayable4" />
|
||||
</policy>
|
||||
</overlayable>
|
||||
<overlayable name="OverlayableResources2" actor="overlay://com.android.overlayable">
|
||||
<!-- Any overlay on the vendor or product partition can overlay the value of
|
||||
@string/overlayable3 -->
|
||||
<policy type="vendor|product">
|
||||
<item type="string" name="overlayable3" />
|
||||
</policy>
|
||||
</overlayable>
|
||||
|
||||
<overlayable name="OverlayableResources2" actor="overlay://com.android.overlayable">
|
||||
<!-- Any overlay on the vendor or product partition can overlay the value of
|
||||
@string/overlayable3 -->
|
||||
<policy type="vendor|product">
|
||||
<item type="string" name="overlayable3" />
|
||||
</policy>
|
||||
</overlayable>
|
||||
<overlayable name="OverlayableResources3">
|
||||
<policy type="public">
|
||||
<item type="string" name="overlayable5" />
|
||||
<item type="string" name="overlayable6" />
|
||||
<item type="string" name="overlayable7" />
|
||||
<item type="string" name="overlayable8" />
|
||||
<item type="string" name="overlayable9" />
|
||||
<item type="string" name="overlayable10" />
|
||||
<item type="string" name="overlayable11" />
|
||||
<item type="integer" name="config_int" />
|
||||
<item type="id" name="hello_view" />
|
||||
<item type="attr" name="max_lines" />
|
||||
<item type="layout" name="hello_view" />
|
||||
</policy>
|
||||
</overlayable>
|
||||
</resources>
|
||||
@@ -20,4 +20,21 @@
|
||||
<public type="string" name="overlayable2" id="0x7f010002" />
|
||||
<public type="string" name="overlayable3" id="0x7f010003" />
|
||||
<public type="string" name="overlayable4" id="0x7f010004" />
|
||||
<public type="string" name="overlayable5" id="0x7f010005" />
|
||||
<public type="string" name="overlayable6" id="0x7f010006" />
|
||||
<public type="string" name="overlayable7" id="0x7f010007" />
|
||||
<public type="string" name="overlayable8" id="0x7f010008" />
|
||||
<public type="string" name="overlayable9" id="0x7f010009" />
|
||||
<public type="string" name="overlayable10" id="0x7f01000a" />
|
||||
<public type="string" name="overlayable11" id="0x7f01000b" />
|
||||
|
||||
<public type="attr" name="max_lines" id="0x7f020000" />
|
||||
|
||||
<public type="bool" name="config_bool" id="0x7f030000" />
|
||||
|
||||
<public type="id" name="hello_view" id="0x7f040000" />
|
||||
|
||||
<public type="integer" name="config_int" id="0x7f050000" />
|
||||
|
||||
<public type="layout" name="hello_view" id="0x7f060000" />
|
||||
</resources>
|
||||
@@ -20,4 +20,15 @@
|
||||
<string name="overlayable2">Overlayable Two</string>
|
||||
<string name="overlayable3">Overlayable Three</string>
|
||||
<string name="overlayable4">Overlayable Four</string>
|
||||
<string name="overlayable5">Overlayable Five</string>
|
||||
<string name="overlayable6">Overlayable Six</string>
|
||||
<string name="overlayable7">Overlayable Seven</string>
|
||||
<string name="overlayable8">Overlayable Eight</string>
|
||||
<string name="overlayable9">Overlayable Nine</string>
|
||||
<string name="overlayable10">Overlayable Ten</string>
|
||||
<string name="overlayable11">Overlayable Eleven</string>
|
||||
|
||||
<integer name="config_int">0</integer>
|
||||
<bool name="config_bool">false</bool>
|
||||
<attr name="max_lines" format="integer" />
|
||||
</resources>
|
||||
|
||||
@@ -43,7 +43,8 @@ struct R {
|
||||
|
||||
struct string {
|
||||
enum : uint32_t {
|
||||
cancel = 0x01040000,
|
||||
no = 0x01040009,
|
||||
yes = 0x01040013,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
20
libs/androidfw/tests/data/system/res/values/public.xml
Normal file
20
libs/androidfw/tests/data/system/res/values/public.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2019 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<public type="string" name="no" id="0x01040009" />
|
||||
<public type="string" name="yes" id="0x01040013" />
|
||||
</resources>
|
||||
20
libs/androidfw/tests/data/system/res/values/values.xml
Normal file
20
libs/androidfw/tests/data/system/res/values/values.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2018 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="yes">yes</string>
|
||||
<string name="no">no</string>
|
||||
</resources>
|
||||
Binary file not shown.
@@ -787,7 +787,9 @@ int doDump(Bundle* bundle)
|
||||
|
||||
// The dynamicRefTable can be null if there are no resources for this asset cookie.
|
||||
// This fine.
|
||||
const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
|
||||
auto noop_destructor = [](const DynamicRefTable* /*ref_table */) { };
|
||||
auto dynamicRefTable = std::shared_ptr<const DynamicRefTable>(
|
||||
res.getDynamicRefTableForCookie(assetsCookie), noop_destructor);
|
||||
|
||||
Asset* asset = NULL;
|
||||
|
||||
|
||||
@@ -226,10 +226,10 @@ TEST_F(XmlFlattenerTest, FlattenNonStandardPackageId) {
|
||||
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
|
||||
|
||||
// The tree needs a custom DynamicRefTable since it is not using a standard app ID (0x7f).
|
||||
android::DynamicRefTable dynamic_ref_table;
|
||||
dynamic_ref_table.addMapping(0x80, 0x80);
|
||||
auto dynamic_ref_table = std::make_shared<android::DynamicRefTable>();
|
||||
dynamic_ref_table->addMapping(0x80, 0x80);
|
||||
|
||||
android::ResXMLTree tree(&dynamic_ref_table);
|
||||
auto tree = android::ResXMLTree(std::move(dynamic_ref_table));
|
||||
ASSERT_TRUE(Flatten(doc.get(), &tree));
|
||||
|
||||
while (tree.next() != android::ResXMLTree::START_TAG) {
|
||||
|
||||
Reference in New Issue
Block a user