Use reference counted pointers for ApkAssets
The primary reason for memory corruption is freed ApkAssets
Java expected them to only be freed in the finalizers, but
there are explicit close() calls now, destroying objects that
are still in use in some AssetManager2 objects
This CL makes sure those AssetManagers don't assume ApkAssets
always exist, but instead tries to lock them in memory for any
access
It also adds logging in case of deleting an assets object with
any weak pointers still existing. Those will get into the
bugreports attached to related bugs to help with investigation.
Benchmarks don't regress, and the device appears to be working.
Given that the crashes used to be pretty rare, let's wait for
any new reports or lack of those.
+ add a missing .clang-format file to the jni directory
+ enabled C++23 in the project that uses AssetManager headers
Bug: 197260547
Bug: 276922628
Test: unit tests + boot + benchmarks
Change-Id: I495fd9e012fe370a1f725dbb0265b4ee1be8d805
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index bc46cf5..d604df3 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -91,13 +91,14 @@
StringPoolRef entry_string_ref;
};
-AssetManager2::AssetManager2() {
- memset(&configuration_, 0, sizeof(configuration_));
+AssetManager2::AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration)
+ : configuration_(configuration) {
+ // Don't invalidate caches here as there's nothing cached yet.
+ SetApkAssets(apk_assets, false);
}
-bool AssetManager2::SetApkAssets(std::vector<const ApkAssets*> apk_assets, bool invalidate_caches) {
- apk_assets_ = std::move(apk_assets);
- BuildDynamicRefTable();
+bool AssetManager2::SetApkAssets(ApkAssetsList apk_assets, bool invalidate_caches) {
+ BuildDynamicRefTable(apk_assets);
RebuildFilterList();
if (invalidate_caches) {
InvalidateCaches(static_cast<uint32_t>(-1));
@@ -105,7 +106,13 @@
return true;
}
-void AssetManager2::BuildDynamicRefTable() {
+bool AssetManager2::SetApkAssets(std::initializer_list<ApkAssetsPtr> apk_assets,
+ bool invalidate_caches) {
+ return SetApkAssets(ApkAssetsList(apk_assets.begin(), apk_assets.size()), invalidate_caches);
+}
+
+void AssetManager2::BuildDynamicRefTable(ApkAssetsList apk_assets) {
+ apk_assets_.assign(apk_assets.begin(), apk_assets.end());
package_groups_.clear();
package_ids_.fill(0xff);
@@ -116,16 +123,19 @@
// Overlay resources are not directly referenced by an application so their resource ids
// can change throughout the application's lifetime. Assign overlay package ids last.
- std::vector<const ApkAssets*> sorted_apk_assets(apk_assets_);
- std::stable_partition(sorted_apk_assets.begin(), sorted_apk_assets.end(), [](const ApkAssets* a) {
- return !a->IsOverlay();
- });
+ std::vector<const ApkAssets*> sorted_apk_assets;
+ sorted_apk_assets.reserve(apk_assets_.size());
+ for (auto& asset : apk_assets) {
+ sorted_apk_assets.push_back(asset.get());
+ }
+ std::stable_partition(sorted_apk_assets.begin(), sorted_apk_assets.end(),
+ [](auto a) { return !a->IsOverlay(); });
// The assets cookie must map to the position of the apk assets in the unsorted apk assets list.
std::unordered_map<const ApkAssets*, ApkAssetsCookie> apk_assets_cookies;
- apk_assets_cookies.reserve(apk_assets_.size());
- for (size_t i = 0, n = apk_assets_.size(); i < n; i++) {
- apk_assets_cookies[apk_assets_[i]] = static_cast<ApkAssetsCookie>(i);
+ apk_assets_cookies.reserve(apk_assets.size());
+ for (size_t i = 0, n = apk_assets.size(); i < n; i++) {
+ apk_assets_cookies[apk_assets[i].get()] = static_cast<ApkAssetsCookie>(i);
}
// 0x01 is reserved for the android package.
@@ -242,7 +252,8 @@
std::string list;
for (const auto& apk_assets : apk_assets_) {
- base::StringAppendF(&list, "%s,", apk_assets->GetDebugName().c_str());
+ auto assets = apk_assets.promote();
+ base::StringAppendF(&list, "%s,", assets ? assets->GetDebugName().c_str() : "nullptr");
}
LOG(INFO) << "ApkAssets: " << list;
@@ -279,7 +290,8 @@
if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
return nullptr;
}
- return apk_assets_[cookie]->GetLoadedArsc()->GetStringPool();
+ auto assets = apk_assets_[cookie].promote();
+ return assets ? assets->GetLoadedArsc()->GetStringPool() : nullptr;
}
const DynamicRefTable* AssetManager2::GetDynamicRefTableForPackage(uint32_t package_id) const {
@@ -331,7 +343,11 @@
std::string* out) const {
uint8_t package_id = 0U;
for (const auto& apk_assets : apk_assets_) {
- const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc();
+ auto assets = apk_assets.promote();
+ if (!assets) {
+ continue;
+ }
+ const LoadedArsc* loaded_arsc = assets->GetLoadedArsc();
if (loaded_arsc == nullptr) {
continue;
}
@@ -384,8 +400,10 @@
}
bool AssetManager2::ContainsAllocatedTable() const {
- return std::find_if(apk_assets_.begin(), apk_assets_.end(),
- std::mem_fn(&ApkAssets::IsTableAllocated)) != apk_assets_.end();
+ return std::find_if(apk_assets_.begin(), apk_assets_.end(), [](auto&& assets_weak) {
+ auto assets = assets_weak.promote();
+ return assets && assets->IsTableAllocated();
+ }) != apk_assets_.end();
}
void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
@@ -398,8 +416,8 @@
}
}
-std::set<const ApkAssets*> AssetManager2::GetNonSystemOverlays() const {
- std::set<const ApkAssets*> non_system_overlays;
+std::set<AssetManager2::ApkAssetsPtr> AssetManager2::GetNonSystemOverlays() const {
+ std::set<ApkAssetsPtr> non_system_overlays;
for (const PackageGroup& package_group : package_groups_) {
bool found_system_package = false;
for (const ConfiguredPackage& package : package_group.packages_) {
@@ -411,7 +429,9 @@
if (!found_system_package) {
for (const ConfiguredOverlay& overlay : package_group.overlays_) {
- non_system_overlays.insert(apk_assets_[overlay.cookie]);
+ if (auto asset = apk_assets_[overlay.cookie].promote()) {
+ non_system_overlays.insert(std::move(asset));
+ }
}
}
}
@@ -423,21 +443,24 @@
bool exclude_system, bool exclude_mipmap) const {
ATRACE_NAME("AssetManager::GetResourceConfigurations");
const auto non_system_overlays =
- (exclude_system) ? GetNonSystemOverlays() : std::set<const ApkAssets*>();
+ exclude_system ? GetNonSystemOverlays() : std::set<ApkAssetsPtr>();
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;
- }
-
- auto apk_assets = apk_assets_[package_group.cookies_[i]];
- if (exclude_system && apk_assets->IsOverlay() &&
- non_system_overlays.find(apk_assets) == non_system_overlays.end()) {
- // Exclude overlays that target system resources.
- continue;
+ if (exclude_system) {
+ if (package.loaded_package_->IsSystem()) {
+ continue;
+ }
+ if (!non_system_overlays.empty()) {
+ // Exclude overlays that target only system resources.
+ auto apk_assets = apk_assets_[package_group.cookies_[i]].promote();
+ if (apk_assets && apk_assets->IsOverlay() &&
+ non_system_overlays.find(apk_assets) == non_system_overlays.end()) {
+ continue;
+ }
+ }
}
auto result = package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations);
@@ -454,20 +477,23 @@
ATRACE_NAME("AssetManager::GetResourceLocales");
std::set<std::string> locales;
const auto non_system_overlays =
- (exclude_system) ? GetNonSystemOverlays() : std::set<const ApkAssets*>();
+ exclude_system ? GetNonSystemOverlays() : std::set<ApkAssetsPtr>();
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;
- }
-
- auto apk_assets = apk_assets_[package_group.cookies_[i]];
- if (exclude_system && apk_assets->IsOverlay() &&
- non_system_overlays.find(apk_assets) == non_system_overlays.end()) {
- // Exclude overlays that target system resources.
- continue;
+ if (exclude_system) {
+ if (package.loaded_package_->IsSystem()) {
+ continue;
+ }
+ if (!non_system_overlays.empty()) {
+ // Exclude overlays that target only system resources.
+ auto apk_assets = apk_assets_[package_group.cookies_[i]].promote();
+ if (apk_assets && apk_assets->IsOverlay() &&
+ non_system_overlays.find(apk_assets) == non_system_overlays.end()) {
+ continue;
+ }
+ }
}
package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales);
@@ -492,13 +518,12 @@
ATRACE_NAME("AssetManager::OpenDir");
std::string full_path = "assets/" + dirname;
- std::unique_ptr<SortedVector<AssetDir::FileInfo>> files =
- util::make_unique<SortedVector<AssetDir::FileInfo>>();
+ auto files = util::make_unique<SortedVector<AssetDir::FileInfo>>();
// Start from the back.
for (auto iter = apk_assets_.rbegin(); iter != apk_assets_.rend(); ++iter) {
- const ApkAssets* apk_assets = *iter;
- if (apk_assets->IsOverlay()) {
+ auto apk_assets = iter->promote();
+ if (!apk_assets || apk_assets->IsOverlay()) {
continue;
}
@@ -527,14 +552,15 @@
Asset::AccessMode mode,
ApkAssetsCookie* out_cookie) const {
for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) {
+ const auto assets = apk_assets_[i].promote();
// Prevent RRO from modifying assets and other entries accessed by file
// path. Explicitly asking for a path in a given package (denoted by a
// cookie) is still OK.
- if (apk_assets_[i]->IsOverlay()) {
+ if (!assets || assets->IsOverlay()) {
continue;
}
- std::unique_ptr<Asset> asset = apk_assets_[i]->GetAssetsProvider()->Open(filename, mode);
+ std::unique_ptr<Asset> asset = assets->GetAssetsProvider()->Open(filename, mode);
if (asset) {
if (out_cookie != nullptr) {
*out_cookie = i;
@@ -555,7 +581,8 @@
if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
return {};
}
- return apk_assets_[cookie]->GetAssetsProvider()->Open(filename, mode);
+ auto assets = apk_assets_[cookie].promote();
+ return assets ? assets->GetAssetsProvider()->Open(filename, mode) : nullptr;
}
base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
@@ -603,7 +630,12 @@
}
bool overlaid = false;
- if (!stop_at_first_match && !ignore_configuration && !apk_assets_[result->cookie]->IsLoader()) {
+ auto assets = apk_assets_[result->cookie].promote();
+ if (!assets) {
+ ALOGE("Found expired ApkAssets #%d for resource ID 0x%08x.", result->cookie, resid);
+ return base::unexpected(std::nullopt);
+ }
+ if (!stop_at_first_match && !ignore_configuration && !assets->IsLoader()) {
for (const auto& id_map : package_group.overlays_) {
auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid);
if (!overlay_entry) {
@@ -633,7 +665,7 @@
if (UNLIKELY(logging_enabled)) {
last_resolution_.steps.push_back(
Resolution::Step{Resolution::Step::Type::OVERLAID_INLINE, result->cookie, String8()});
- if (auto path = apk_assets_[result->cookie]->GetPath()) {
+ if (auto path = assets->GetPath()) {
const std::string overlay_path = path->data();
if (IsFabricatedOverlay(overlay_path)) {
// FRRO don't have package name so we use the creating package here.
@@ -859,7 +891,9 @@
}
const uint32_t resid = last_resolution_.resid;
- const auto package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
+ auto assets = apk_assets_[cookie].promote();
+ const auto package =
+ assets ? assets->GetLoadedArsc()->GetPackageById(get_package_id(resid)) : nullptr;
std::string resource_name_string;
if (package != nullptr) {
@@ -890,7 +924,9 @@
continue;
}
const auto prefix = kStepStrings[int(step.type) - int(Resolution::Step::Type::INITIAL)];
- log_stream << "\n\t" << prefix << ": " << apk_assets_[step.cookie]->GetDebugName();
+ auto assets = apk_assets_[step.cookie].promote();
+ log_stream << "\n\t" << prefix << ": " << (assets ? assets->GetDebugName() : "<null>")
+ << " #" << step.cookie;
if (!step.config_name.isEmpty()) {
log_stream << " - " << step.config_name;
}
@@ -1555,11 +1591,14 @@
// Determine which ApkAssets are loaded in both theme AssetManagers.
const auto& src_assets = source.asset_manager_->GetApkAssets();
for (size_t i = 0; i < src_assets.size(); i++) {
- const ApkAssets* src_asset = src_assets[i];
+ auto src_asset = src_assets[i].promote();
+ if (!src_asset) {
+ continue;
+ }
const auto& dest_assets = asset_manager_->GetApkAssets();
for (size_t j = 0; j < dest_assets.size(); j++) {
- const ApkAssets* dest_asset = dest_assets[j];
+ auto dest_asset = dest_assets[j].promote();
if (src_asset != dest_asset) {
// ResourcesManager caches and reuses ApkAssets when the same apk must be present in
// multiple AssetManagers. Two ApkAssets point to the same version of the same resources