Merge changes Ie5999dda,I2853cd3c,I7d9f1fe3,I19576654,I158af793, ...
* changes:
[res] Don't stat asset providers on RO filesystems
[res] Change OverlayableInfo to hold string views
[res] Change staged alias container to vector
[res] Change callback from function to function_ref
[res] Reuse memory in RebuildFilterList()
[res] Split keys and values in Theme::Entry vector
[res] Properly create ZipAssetsProvider with fd
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
index bb31c11..b2300ce 100644
--- a/cmds/idmap2/libidmap2/ResourceMapping.cpp
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -89,7 +89,7 @@
// If the overlay supplies a target overlayable name, the resource must belong to the
// overlayable defined with the specified name to be overlaid.
return Error(R"(<overlay> android:targetName "%s" does not match overlayable name "%s")",
- overlay_info.target_name.c_str(), (*overlayable_info)->name.c_str());
+ overlay_info.target_name.c_str(), (*overlayable_info)->name.data());
}
// Enforce policy restrictions if the resource is declared as overlayable.
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
old mode 100755
new mode 100644
index c0fa63a..15aaae2
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -18,6 +18,7 @@
#include "android-base/errors.h"
#include "android-base/logging.h"
+#include "android-base/utf8.h"
namespace android {
@@ -84,7 +85,7 @@
}
std::string overlay_path(loaded_idmap->OverlayApkPath());
- auto fd = unique_fd(::open(overlay_path.c_str(), O_RDONLY|O_CLOEXEC));
+ auto fd = unique_fd(base::utf8::open(overlay_path.c_str(), O_RDONLY | O_CLOEXEC));
std::unique_ptr<AssetsProvider> overlay_assets;
if (IsFabricatedOverlay(fd)) {
// Fabricated overlays do not contain resource definitions. All of the overlay resource values
@@ -92,7 +93,7 @@
overlay_assets = EmptyAssetsProvider::Create(std::move(overlay_path));
} else {
// The overlay should be an APK.
- overlay_assets = ZipAssetsProvider::Create(std::move(fd), std::move(overlay_path), flags);
+ overlay_assets = ZipAssetsProvider::Create(std::move(overlay_path), flags, std::move(fd));
}
if (overlay_assets == nullptr) {
return {};
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index c3d153d..cc7e871 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -374,7 +374,7 @@
const std::string name = ToFormattedResourceString(*res_name);
output.append(base::StringPrintf(
"resource='%s' overlayable='%s' actor='%s' policy='0x%08x'\n",
- name.c_str(), info->name.c_str(), info->actor.c_str(), info->policy_flags));
+ name.c_str(), info->name.data(), info->actor.data(), info->policy_flags));
}
}
}
@@ -1356,21 +1356,22 @@
void AssetManager2::RebuildFilterList() {
for (PackageGroup& group : package_groups_) {
- for (ConfiguredPackage& impl : group.packages_) {
- impl.filtered_configs_.clear();
-
+ for (ConfiguredPackage& package : group.packages_) {
+ package.filtered_configs_.forEachItem([](auto, auto& fcg) { fcg.type_entries.clear(); });
// Create the filters here.
- impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec& type_spec, uint8_t type_id) {
+ package.loaded_package_->ForEachTypeSpec([&](const TypeSpec& type_spec, uint8_t type_id) {
FilteredConfigGroup* group = nullptr;
for (const auto& type_entry : type_spec.type_entries) {
if (type_entry.config.match(configuration_)) {
if (!group) {
- group = &impl.filtered_configs_.editItemAt(type_id - 1);
+ group = &package.filtered_configs_.editItemAt(type_id - 1);
}
group->type_entries.push_back(&type_entry);
}
}
});
+ package.filtered_configs_.trimBuckets(
+ [](const auto& fcg) { return fcg.type_entries.empty(); });
}
}
}
@@ -1411,30 +1412,34 @@
std::unique_ptr<Theme> AssetManager2::NewTheme() {
constexpr size_t kInitialReserveSize = 32;
auto theme = std::unique_ptr<Theme>(new Theme(this));
+ theme->keys_.reserve(kInitialReserveSize);
theme->entries_.reserve(kInitialReserveSize);
return theme;
}
+void AssetManager2::ForEachPackage(base::function_ref<bool(const std::string&, uint8_t)> func,
+ package_property_t excluded_property_flags) const {
+ for (const PackageGroup& package_group : package_groups_) {
+ const auto loaded_package = package_group.packages_.front().loaded_package_;
+ if ((loaded_package->GetPropertyFlags() & excluded_property_flags) == 0U
+ && !func(loaded_package->GetPackageName(),
+ package_group.dynamic_ref_table->mAssignedPackageId)) {
+ return;
+ }
+ }
+}
+
Theme::Theme(AssetManager2* asset_manager) : asset_manager_(asset_manager) {
}
Theme::~Theme() = default;
struct Theme::Entry {
- uint32_t attr_res_id;
ApkAssetsCookie cookie;
uint32_t type_spec_flags;
Res_value value;
};
-namespace {
-struct ThemeEntryKeyComparer {
- bool operator() (const Theme::Entry& entry, uint32_t attr_res_id) const noexcept {
- return entry.attr_res_id < attr_res_id;
- }
-};
-} // namespace
-
base::expected<std::monostate, NullOrIOError> Theme::ApplyStyle(uint32_t resid, bool force) {
ATRACE_NAME("Theme::ApplyStyle");
@@ -1463,18 +1468,20 @@
continue;
}
- auto entry_it = std::lower_bound(entries_.begin(), entries_.end(), attr_res_id,
- ThemeEntryKeyComparer{});
- if (entry_it != entries_.end() && entry_it->attr_res_id == attr_res_id) {
+ const auto key_it = std::lower_bound(keys_.begin(), keys_.end(), attr_res_id);
+ const auto entry_it = entries_.begin() + (key_it - keys_.begin());
+ if (key_it != keys_.end() && *key_it == attr_res_id) {
if (is_undefined) {
// DATA_NULL_UNDEFINED clears the value of the attribute in the theme only when `force` is
- /// true.
+ // true.
+ keys_.erase(key_it);
entries_.erase(entry_it);
} else if (force) {
- *entry_it = Entry{attr_res_id, it->cookie, (*bag)->type_spec_flags, it->value};
+ *entry_it = Entry{it->cookie, (*bag)->type_spec_flags, it->value};
}
} else {
- entries_.insert(entry_it, Entry{attr_res_id, it->cookie, (*bag)->type_spec_flags, it->value});
+ keys_.insert(key_it, attr_res_id);
+ entries_.insert(entry_it, Entry{it->cookie, (*bag)->type_spec_flags, it->value});
}
}
return {};
@@ -1485,6 +1492,7 @@
ATRACE_NAME("Theme::Rebase");
// Reset the entries without changing the vector capacity to prevent reallocations during
// ApplyStyle.
+ keys_.clear();
entries_.clear();
asset_manager_ = am;
for (size_t i = 0; i < style_count; i++) {
@@ -1493,16 +1501,14 @@
}
std::optional<AssetManager2::SelectedValue> Theme::GetAttribute(uint32_t resid) const {
-
constexpr const uint32_t kMaxIterations = 20;
uint32_t type_spec_flags = 0u;
for (uint32_t i = 0; i <= kMaxIterations; i++) {
- auto entry_it = std::lower_bound(entries_.begin(), entries_.end(), resid,
- ThemeEntryKeyComparer{});
- if (entry_it == entries_.end() || entry_it->attr_res_id != resid) {
+ const auto key_it = std::lower_bound(keys_.begin(), keys_.end(), resid);
+ if (key_it == keys_.end() || *key_it != resid) {
return std::nullopt;
}
-
+ const auto entry_it = entries_.begin() + (key_it - keys_.begin());
type_spec_flags |= entry_it->type_spec_flags;
if (entry_it->value.dataType == Res_value::TYPE_ATTRIBUTE) {
resid = entry_it->value.data;
@@ -1536,6 +1542,7 @@
}
void Theme::Clear() {
+ keys_.clear();
entries_.clear();
}
@@ -1547,11 +1554,12 @@
type_spec_flags_ = source.type_spec_flags_;
if (asset_manager_ == source.asset_manager_) {
+ keys_ = source.keys_;
entries_ = source.entries_;
} else {
- std::map<ApkAssetsCookie, ApkAssetsCookie> src_to_dest_asset_cookies;
- typedef std::map<int, int> SourceToDestinationRuntimePackageMap;
- std::map<ApkAssetsCookie, SourceToDestinationRuntimePackageMap> src_asset_cookie_id_map;
+ std::unordered_map<ApkAssetsCookie, ApkAssetsCookie> src_to_dest_asset_cookies;
+ using SourceToDestinationRuntimePackageMap = std::unordered_map<int, int>;
+ std::unordered_map<ApkAssetsCookie, SourceToDestinationRuntimePackageMap> src_asset_cookie_id_map;
// Determine which ApkAssets are loaded in both theme AssetManagers.
const auto src_assets = source.asset_manager_->GetApkAssets();
@@ -1579,15 +1587,17 @@
}
src_to_dest_asset_cookies.insert(std::make_pair(i, j));
- src_asset_cookie_id_map.insert(std::make_pair(i, package_map));
+ src_asset_cookie_id_map.insert(std::make_pair(i, std::move(package_map)));
break;
}
}
// Reset the data in the destination theme.
+ keys_.clear();
entries_.clear();
- for (const auto& entry : source.entries_) {
+ for (size_t i = 0, size = source.entries_.size(); i != size; ++i) {
+ const auto& entry = source.entries_[i];
bool is_reference = (entry.value.dataType == Res_value::TYPE_ATTRIBUTE
|| entry.value.dataType == Res_value::TYPE_REFERENCE
|| entry.value.dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE
@@ -1627,13 +1637,15 @@
}
}
+ const auto source_res_id = source.keys_[i];
+
// The package id of the attribute needs to be rewritten to the package id of the
// attribute in the destination.
- int attribute_dest_package_id = get_package_id(entry.attr_res_id);
+ int attribute_dest_package_id = get_package_id(source_res_id);
if (attribute_dest_package_id != 0x01) {
// Find the cookie of the attribute resource id in the source AssetManager
base::expected<FindEntryResult, NullOrIOError> attribute_entry_result =
- source.asset_manager_->FindEntry(entry.attr_res_id, 0 /* density_override */ ,
+ source.asset_manager_->FindEntry(source_res_id, 0 /* density_override */ ,
true /* stop_at_first_match */,
true /* ignore_configuration */);
if (UNLIKELY(IsIOError(attribute_entry_result))) {
@@ -1657,16 +1669,15 @@
attribute_dest_package_id = attribute_dest_package->second;
}
- auto dest_attr_id = make_resid(attribute_dest_package_id, get_type_id(entry.attr_res_id),
- get_entry_id(entry.attr_res_id));
- Theme::Entry new_entry{dest_attr_id, data_dest_cookie, entry.type_spec_flags,
- Res_value{.dataType = entry.value.dataType,
- .data = attribute_data}};
-
+ auto dest_attr_id = make_resid(attribute_dest_package_id, get_type_id(source_res_id),
+ get_entry_id(source_res_id));
+ const auto key_it = std::lower_bound(keys_.begin(), keys_.end(), dest_attr_id);
+ const auto entry_it = entries_.begin() + (key_it - keys_.begin());
// Since the entries were cleared, the attribute resource id has yet been mapped to any value.
- auto entry_it = std::lower_bound(entries_.begin(), entries_.end(), dest_attr_id,
- ThemeEntryKeyComparer{});
- entries_.insert(entry_it, new_entry);
+ keys_.insert(key_it, dest_attr_id);
+ entries_.insert(entry_it, Entry{data_dest_cookie, entry.type_spec_flags,
+ Res_value{.dataType = entry.value.dataType,
+ .data = attribute_data}});
}
}
return {};
@@ -1674,9 +1685,11 @@
void Theme::Dump() const {
LOG(INFO) << base::StringPrintf("Theme(this=%p, AssetManager2=%p)", this, asset_manager_);
- for (auto& entry : entries_) {
+ for (size_t i = 0, size = keys_.size(); i != size; ++i) {
+ auto res_id = keys_[i];
+ const auto& entry = entries_[i];
LOG(INFO) << base::StringPrintf(" entry(0x%08x)=(0x%08x) type=(0x%02x), cookie(%d)",
- entry.attr_res_id, entry.value.data, entry.value.dataType,
+ res_id, entry.value.data, entry.value.dataType,
entry.cookie);
}
}
diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp
index 80e5607..b9264c5 100644
--- a/libs/androidfw/AssetsProvider.cpp
+++ b/libs/androidfw/AssetsProvider.cpp
@@ -92,21 +92,27 @@
last_mod_time_(last_mod_time) {}
std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path,
- package_property_t flags) {
+ package_property_t flags,
+ base::unique_fd fd) {
+ const auto released_fd = fd.ok() ? fd.release() : -1;
ZipArchiveHandle handle;
- if (int32_t result = OpenArchive(path.c_str(), &handle); result != 0) {
+ if (int32_t result = released_fd < 0 ? OpenArchive(path.c_str(), &handle)
+ : OpenArchiveFd(released_fd, path.c_str(), &handle)) {
LOG(ERROR) << "Failed to open APK '" << path << "': " << ::ErrorCodeString(result);
CloseArchive(handle);
return {};
}
struct stat sb{.st_mtime = -1};
- if (stat(path.c_str(), &sb) < 0) {
- // Stat requires execute permissions on all directories path to the file. If the process does
- // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
- // always have to return true.
- LOG(WARNING) << "Failed to stat file '" << path << "': "
- << base::SystemErrorCodeToString(errno);
+ // Skip all up-to-date checks if the file won't ever change.
+ if (!isReadonlyFilesystem(path.c_str())) {
+ if ((released_fd < 0 ? stat(path.c_str(), &sb) : fstat(released_fd, &sb)) < 0) {
+ // Stat requires execute permissions on all directories path to the file. If the process does
+ // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
+ // always have to return true.
+ LOG(WARNING) << "Failed to stat file '" << path << "': "
+ << base::SystemErrorCodeToString(errno);
+ }
}
return std::unique_ptr<ZipAssetsProvider>(
@@ -133,12 +139,15 @@
}
struct stat sb{.st_mtime = -1};
- if (fstat(released_fd, &sb) < 0) {
- // Stat requires execute permissions on all directories path to the file. If the process does
- // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
- // always have to return true.
- LOG(WARNING) << "Failed to fstat file '" << friendly_name << "': "
- << base::SystemErrorCodeToString(errno);
+ // Skip all up-to-date checks if the file won't ever change.
+ if (!isReadonlyFilesystem(released_fd)) {
+ if (fstat(released_fd, &sb) < 0) {
+ // Stat requires execute permissions on all directories path to the file. If the process does
+ // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
+ // always have to return true.
+ LOG(WARNING) << "Failed to fstat file '" << friendly_name
+ << "': " << base::SystemErrorCodeToString(errno);
+ }
}
return std::unique_ptr<ZipAssetsProvider>(
@@ -275,6 +284,9 @@
}
bool ZipAssetsProvider::IsUpToDate() const {
+ if (last_mod_time_ == -1) {
+ return true;
+ }
struct stat sb{};
if (fstat(GetFileDescriptor(zip_handle_.get()), &sb) < 0) {
// If fstat fails on the zip archive, return true so the zip archive the resource system does
@@ -288,7 +300,7 @@
: dir_(std::forward<std::string>(path)), last_mod_time_(last_mod_time) {}
std::unique_ptr<DirectoryAssetsProvider> DirectoryAssetsProvider::Create(std::string path) {
- struct stat sb{};
+ struct stat sb;
const int result = stat(path.c_str(), &sb);
if (result == -1) {
LOG(ERROR) << "Failed to find directory '" << path << "'.";
@@ -304,8 +316,9 @@
path += OS_PATH_SEPARATOR;
}
- return std::unique_ptr<DirectoryAssetsProvider>(new DirectoryAssetsProvider(std::move(path),
- sb.st_mtime));
+ const bool isReadonly = isReadonlyFilesystem(path.c_str());
+ return std::unique_ptr<DirectoryAssetsProvider>(
+ new DirectoryAssetsProvider(std::move(path), isReadonly ? -1 : sb.st_mtime));
}
std::unique_ptr<Asset> DirectoryAssetsProvider::OpenInternal(const std::string& path,
@@ -335,7 +348,10 @@
}
bool DirectoryAssetsProvider::IsUpToDate() const {
- struct stat sb{};
+ if (last_mod_time_ == -1) {
+ return true;
+ }
+ struct stat sb;
if (stat(dir_.c_str(), &sb) < 0) {
// If stat fails on the zip archive, return true so the zip archive the resource system does
// attempt to refresh the ApkAsset.
@@ -431,4 +447,4 @@
return true;
}
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 386f718..c0fdfe2 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -645,16 +645,16 @@
}
std::string name;
- util::ReadUtf16StringFromDevice(overlayable->name, arraysize(overlayable->name), &name);
+ util::ReadUtf16StringFromDevice(overlayable->name, std::size(overlayable->name), &name);
std::string actor;
- util::ReadUtf16StringFromDevice(overlayable->actor, arraysize(overlayable->actor), &actor);
-
- if (loaded_package->overlayable_map_.find(name) !=
- loaded_package->overlayable_map_.end()) {
- LOG(ERROR) << "Multiple <overlayable> blocks with the same name '" << name << "'.";
+ util::ReadUtf16StringFromDevice(overlayable->actor, std::size(overlayable->actor), &actor);
+ auto [name_to_actor_it, inserted] =
+ loaded_package->overlayable_map_.emplace(std::move(name), std::move(actor));
+ if (!inserted) {
+ LOG(ERROR) << "Multiple <overlayable> blocks with the same name '"
+ << name_to_actor_it->first << "'.";
return {};
}
- loaded_package->overlayable_map_.emplace(name, actor);
// Iterate over the overlayable policy chunks contained within the overlayable chunk data
ChunkIterator overlayable_iter(child_chunk.data_ptr(), child_chunk.data_size());
@@ -669,7 +669,6 @@
LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small.";
return {};
}
-
if ((overlayable_child_chunk.data_size() / sizeof(ResTable_ref))
< dtohl(policy_header->entry_count)) {
LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small to hold entries.";
@@ -691,8 +690,8 @@
// Add the pairing of overlayable properties and resource ids to the package
OverlayableInfo overlayable_info {
- .name = name,
- .actor = actor,
+ .name = name_to_actor_it->first,
+ .actor = name_to_actor_it->second,
.policy_flags = policy_header->policy_flags
};
loaded_package->overlayable_infos_.emplace_back(std::move(overlayable_info), std::move(ids));
@@ -736,6 +735,7 @@
const auto entry_end = entry_begin + dtohl(lib_alias->count);
std::unordered_set<uint32_t> finalized_ids;
finalized_ids.reserve(entry_end - entry_begin);
+ loaded_package->alias_id_map_.reserve(entry_end - entry_begin);
for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) {
if (!entry_iter) {
LOG(ERROR) << "NULL ResTable_staged_alias_entry record??";
@@ -749,13 +749,20 @@
}
auto staged_id = dtohl(entry_iter->stagedResId);
- auto [_, success] = loaded_package->alias_id_map_.emplace(staged_id, finalized_id);
- if (!success) {
+ loaded_package->alias_id_map_.emplace_back(staged_id, finalized_id);
+ }
+
+ std::sort(loaded_package->alias_id_map_.begin(), loaded_package->alias_id_map_.end(),
+ [](auto&& l, auto&& r) { return l.first < r.first; });
+ const auto duplicate_it =
+ std::adjacent_find(loaded_package->alias_id_map_.begin(),
+ loaded_package->alias_id_map_.end(),
+ [](auto&& l, auto&& r) { return l.first == r.first; });
+ if (duplicate_it != loaded_package->alias_id_map_.end()) {
LOG(ERROR) << StringPrintf("Repeated staged resource id '%08x' in staged aliases.",
- staged_id);
+ duplicate_it->first);
return {};
}
- }
} break;
default:
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index e9aaedc..e4d1218 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -17,6 +17,7 @@
#ifndef ANDROIDFW_ASSETMANAGER2_H_
#define ANDROIDFW_ASSETMANAGER2_H_
+#include "android-base/function_ref.h"
#include "android-base/macros.h"
#include <array>
@@ -320,17 +321,8 @@
// Creates a new Theme from this AssetManager.
std::unique_ptr<Theme> NewTheme();
- void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func,
- package_property_t excluded_property_flags = 0U) const {
- for (const PackageGroup& package_group : package_groups_) {
- const auto loaded_package = package_group.packages_.front().loaded_package_;
- if ((loaded_package->GetPropertyFlags() & excluded_property_flags) == 0U
- && !func(loaded_package->GetPackageName(),
- package_group.dynamic_ref_table->mAssignedPackageId)) {
- return;
- }
- }
- }
+ void ForEachPackage(base::function_ref<bool(const std::string&, uint8_t)> func,
+ package_property_t excluded_property_flags = 0U) const;
void DumpToLog() const;
@@ -571,6 +563,7 @@
AssetManager2* asset_manager_ = nullptr;
uint32_t type_spec_flags_ = 0u;
+ std::vector<uint32_t> keys_;
std::vector<Entry> entries_;
};
diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h
index af6e7f4..7891194 100644
--- a/libs/androidfw/include/androidfw/AssetsProvider.h
+++ b/libs/androidfw/include/androidfw/AssetsProvider.h
@@ -80,8 +80,8 @@
// Supplies assets from a zip archive.
struct ZipAssetsProvider : public AssetsProvider {
- static std::unique_ptr<ZipAssetsProvider> Create(std::string path,
- package_property_t flags);
+ static std::unique_ptr<ZipAssetsProvider> Create(std::string path, package_property_t flags,
+ base::unique_fd fd = {});
static std::unique_ptr<ZipAssetsProvider> Create(base::unique_fd fd,
std::string friendly_name,
diff --git a/libs/androidfw/include/androidfw/ByteBucketArray.h b/libs/androidfw/include/androidfw/ByteBucketArray.h
index 05a2c4d..ca0a9ed 100644
--- a/libs/androidfw/include/androidfw/ByteBucketArray.h
+++ b/libs/androidfw/include/androidfw/ByteBucketArray.h
@@ -17,6 +17,7 @@
#ifndef __BYTE_BUCKET_ARRAY_H
#define __BYTE_BUCKET_ARRAY_H
+#include <algorithm>
#include <cstdint>
#include <cstring>
@@ -36,15 +37,11 @@
}
~ByteBucketArray() {
- clear();
+ deleteBuckets();
}
void clear() {
- for (size_t i = 0; i < kNumBuckets; i++) {
- if (buckets_[i] != NULL) {
- delete[] buckets_[i];
- }
- }
+ deleteBuckets();
memset(buckets_, 0, sizeof(buckets_));
}
@@ -59,7 +56,7 @@
uint8_t bucket_index = static_cast<uint8_t>(index) >> 4;
T* bucket = buckets_[bucket_index];
- if (bucket == NULL) {
+ if (bucket == nullptr) {
return default_;
}
return bucket[0x0f & static_cast<uint8_t>(index)];
@@ -70,9 +67,9 @@
<< ") with size=" << size();
uint8_t bucket_index = static_cast<uint8_t>(index) >> 4;
- T* bucket = buckets_[bucket_index];
- if (bucket == NULL) {
- bucket = buckets_[bucket_index] = new T[kBucketSize]();
+ T*& bucket = buckets_[bucket_index];
+ if (bucket == nullptr) {
+ bucket = new T[kBucketSize]();
}
return bucket[0x0f & static_cast<uint8_t>(index)];
}
@@ -86,9 +83,42 @@
return true;
}
+ template <class Func>
+ void forEachItem(Func f) {
+ for (size_t i = 0; i < kNumBuckets; i++) {
+ const auto bucket = buckets_[i];
+ if (bucket != nullptr) {
+ for (size_t j = 0; j < kBucketSize; j++) {
+ f((i << 4) | j, bucket[j]);
+ }
+ }
+ }
+ }
+
+ template <class Func>
+ void trimBuckets(Func isEmptyFunc) {
+ for (size_t i = 0; i < kNumBuckets; i++) {
+ const auto bucket = buckets_[i];
+ if (bucket != nullptr) {
+ if (std::all_of(bucket, bucket + kBucketSize, isEmptyFunc)) {
+ delete[] bucket;
+ buckets_[i] = nullptr;
+ }
+ }
+ }
+ }
+
private:
enum { kNumBuckets = 16, kBucketSize = 16 };
+ void deleteBuckets() {
+ for (size_t i = 0; i < kNumBuckets; i++) {
+ if (buckets_[i] != nullptr) {
+ delete[] buckets_[i];
+ }
+ }
+ }
+
T* buckets_[kNumBuckets];
static inline const T default_ = {};
};
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 79d96282..4d12885 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -99,8 +99,8 @@
};
struct OverlayableInfo {
- std::string name;
- std::string actor;
+ std::string_view name;
+ std::string_view actor;
uint32_t policy_flags;
};
@@ -275,7 +275,7 @@
return overlayable_map_;
}
- const std::map<uint32_t, uint32_t>& GetAliasResourceIdMap() const {
+ const std::vector<std::pair<uint32_t, uint32_t>>& GetAliasResourceIdMap() const {
return alias_id_map_;
}
@@ -295,8 +295,8 @@
std::unordered_map<uint8_t, TypeSpec> type_specs_;
ByteBucketArray<uint32_t> resource_ids_;
std::vector<DynamicPackageEntry> dynamic_package_map_;
- std::vector<const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_;
- std::map<uint32_t, uint32_t> alias_id_map_;
+ std::vector<std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_;
+ std::vector<std::pair<uint32_t, uint32_t>> alias_id_map_;
// A map of overlayable name to actor
std::unordered_map<std::string, std::string> overlayable_map_;
diff --git a/libs/androidfw/include/androidfw/misc.h b/libs/androidfw/include/androidfw/misc.h
index 5a5a0e2..d40d24e 100644
--- a/libs/androidfw/include/androidfw/misc.h
+++ b/libs/androidfw/include/androidfw/misc.h
@@ -44,6 +44,10 @@
/* get the file's modification date; returns -1 w/errno set on failure */
time_t getFileModDate(const char* fileName);
+// Check if |path| or |fd| resides on a readonly filesystem.
+bool isReadonlyFilesystem(const char* path);
+bool isReadonlyFilesystem(int fd);
+
}; // namespace android
#endif // _LIBS_ANDROID_FW_MISC_H
diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp
index 5285420..7af5066 100644
--- a/libs/androidfw/misc.cpp
+++ b/libs/androidfw/misc.cpp
@@ -21,12 +21,17 @@
//
#include <androidfw/misc.h>
-#include <sys/stat.h>
-#include <cstring>
-#include <errno.h>
-#include <cstdio>
+#include "android-base/logging.h"
-using namespace android;
+#ifndef _WIN32
+#include <sys/statvfs.h>
+#include <sys/vfs.h>
+#endif // _WIN32
+
+#include <cstring>
+#include <cstdio>
+#include <errno.h>
+#include <sys/stat.h>
namespace android {
@@ -41,8 +46,7 @@
if (errno == ENOENT || errno == ENOTDIR)
return kFileTypeNonexistent;
else {
- fprintf(stderr, "getFileType got errno=%d on '%s'\n",
- errno, fileName);
+ PLOG(ERROR) << "getFileType(): stat(" << fileName << ") failed";
return kFileTypeUnknown;
}
} else {
@@ -82,4 +86,32 @@
return sb.st_mtime;
}
+#ifdef _WIN32
+// No need to implement these for Windows, the functions only matter on a device.
+bool isReadonlyFilesystem(const char*) {
+ return false;
+}
+bool isReadonlyFilesystem(int) {
+ return false;
+}
+#else // _WIN32
+bool isReadonlyFilesystem(const char* path) {
+ struct statfs sfs;
+ if (::statfs(path, &sfs)) {
+ PLOG(ERROR) << "isReadonlyFilesystem(): statfs(" << path << ") failed";
+ return false;
+ }
+ return (sfs.f_flags & ST_RDONLY) != 0;
+}
+
+bool isReadonlyFilesystem(int fd) {
+ struct statfs sfs;
+ if (::fstatfs(fd, &sfs)) {
+ PLOG(ERROR) << "isReadonlyFilesystem(): fstatfs(" << fd << ") failed";
+ return false;
+ }
+ return (sfs.f_flags & ST_RDONLY) != 0;
+}
+#endif // _WIN32
+
}; // namespace android
diff --git a/libs/androidfw/tests/ByteBucketArray_test.cpp b/libs/androidfw/tests/ByteBucketArray_test.cpp
index 5d464c7..9c36cfb 100644
--- a/libs/androidfw/tests/ByteBucketArray_test.cpp
+++ b/libs/androidfw/tests/ByteBucketArray_test.cpp
@@ -52,4 +52,57 @@
}
}
+TEST(ByteBucketArrayTest, TestForEach) {
+ ByteBucketArray<int> bba;
+ ASSERT_TRUE(bba.set(0, 1));
+ ASSERT_TRUE(bba.set(10, 2));
+ ASSERT_TRUE(bba.set(26, 3));
+ ASSERT_TRUE(bba.set(129, 4));
+ ASSERT_TRUE(bba.set(234, 5));
+
+ int count = 0;
+ bba.forEachItem([&count](auto i, auto val) {
+ ++count;
+ switch (i) {
+ case 0:
+ EXPECT_EQ(1, val);
+ break;
+ case 10:
+ EXPECT_EQ(2, val);
+ break;
+ case 26:
+ EXPECT_EQ(3, val);
+ break;
+ case 129:
+ EXPECT_EQ(4, val);
+ break;
+ case 234:
+ EXPECT_EQ(5, val);
+ break;
+ default:
+ EXPECT_EQ(0, val);
+ break;
+ }
+ });
+ ASSERT_EQ(4 * 16, count);
+}
+
+TEST(ByteBucketArrayTest, TestTrimBuckets) {
+ ByteBucketArray<int> bba;
+ ASSERT_TRUE(bba.set(0, 1));
+ ASSERT_TRUE(bba.set(255, 2));
+ {
+ bba.trimBuckets([](auto val) { return val < 2; });
+ int count = 0;
+ bba.forEachItem([&count](auto, auto) { ++count; });
+ ASSERT_EQ(1 * 16, count);
+ }
+ {
+ bba.trimBuckets([](auto val) { return val < 3; });
+ int count = 0;
+ bba.forEachItem([&count](auto, auto) { ++count; });
+ ASSERT_EQ(0, count);
+ }
+}
+
} // namespace android