Merge "Optimize FilterApkAssets by caching config"
This commit is contained in:
committed by
Android (Google) Code Review
commit
8d845b02c9
@@ -285,14 +285,12 @@ Result<ResourceMapping> ResourceMapping::FromApkAssets(const ApkAssets& target_a
|
||||
bool enforce_overlayable,
|
||||
LogInfo& log_info) {
|
||||
AssetManager2 target_asset_manager;
|
||||
if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true /* invalidate_caches */,
|
||||
false /* filter_incompatible_configs*/)) {
|
||||
if (!target_asset_manager.SetApkAssets({&target_apk_assets})) {
|
||||
return Error("failed to create target asset manager");
|
||||
}
|
||||
|
||||
AssetManager2 overlay_asset_manager;
|
||||
if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets}, true /* invalidate_caches */,
|
||||
false /* filter_incompatible_configs */)) {
|
||||
if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets})) {
|
||||
return Error("failed to create overlay asset manager");
|
||||
}
|
||||
|
||||
|
||||
@@ -103,10 +103,10 @@ AssetManager2::AssetManager2() {
|
||||
}
|
||||
|
||||
bool AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets,
|
||||
bool invalidate_caches, bool filter_incompatible_configs) {
|
||||
bool invalidate_caches) {
|
||||
apk_assets_ = apk_assets;
|
||||
BuildDynamicRefTable();
|
||||
RebuildFilterList(filter_incompatible_configs);
|
||||
RebuildFilterList();
|
||||
if (invalidate_caches) {
|
||||
InvalidateCaches(static_cast<uint32_t>(-1));
|
||||
}
|
||||
@@ -623,7 +623,8 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
|
||||
if (UNLIKELY(logging_enabled)) {
|
||||
last_resolution_.steps.push_back(
|
||||
Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->config.toString(),
|
||||
overlay_result->package_name});
|
||||
overlay_result->package_name,
|
||||
overlay_result->cookie});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -646,22 +647,21 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
|
||||
const LoadedPackage* best_package = nullptr;
|
||||
incfs::verified_map_ptr<ResTable_type> best_type;
|
||||
const ResTable_config* best_config = nullptr;
|
||||
ResTable_config best_config_copy;
|
||||
uint32_t best_offset = 0U;
|
||||
uint32_t type_flags = 0U;
|
||||
|
||||
auto 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_;
|
||||
// If `desired_config` is not the same as the set configuration or the caller will accept a value
|
||||
// from any configuration, then we cannot use our filtered list of types since it only it contains
|
||||
// types matched to the set configuration.
|
||||
const bool use_filtered = !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_;
|
||||
ApkAssetsCookie cookie = package_group.cookies_[pi];
|
||||
const ApkAssetsCookie cookie = package_group.cookies_[pi];
|
||||
|
||||
// If the type IDs are offset in this package, we need to take that into account when searching
|
||||
// for a type.
|
||||
@@ -670,130 +670,82 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
|
||||
continue;
|
||||
}
|
||||
|
||||
// Allow custom loader packages to overlay resource values with configurations equivalent to the
|
||||
// current best configuration.
|
||||
const bool package_is_loader = loaded_package->IsCustomLoader();
|
||||
|
||||
auto entry_flags = type_spec->GetFlagsForEntryIndex(entry_idx);
|
||||
if (UNLIKELY(!entry_flags.has_value())) {
|
||||
return base::unexpected(entry_flags.error());
|
||||
}
|
||||
type_flags |= entry_flags.value();
|
||||
|
||||
// If the package is an overlay or custom loader,
|
||||
// then even configurations that are the same MUST be chosen.
|
||||
const bool package_is_loader = loaded_package->IsCustomLoader();
|
||||
const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
|
||||
const size_t type_entry_count = (use_filtered) ? filtered_group.type_entries.size()
|
||||
: type_spec->type_entries.size();
|
||||
for (size_t i = 0; i < type_entry_count; i++) {
|
||||
const TypeSpec::TypeEntry* type_entry = (use_filtered) ? filtered_group.type_entries[i]
|
||||
: &type_spec->type_entries[i];
|
||||
|
||||
if (use_fast_path) {
|
||||
const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
|
||||
for (const auto& type_config : filtered_group.type_configs) {
|
||||
const ResTable_config& this_config = type_config.config;
|
||||
|
||||
// We can skip calling ResTable_config::match() because we know that all candidate
|
||||
// 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)) {
|
||||
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 (UNLIKELY(logging_enabled)) {
|
||||
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()});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// The configuration matches and is better than the previous selection.
|
||||
// Find the entry value if it exists for this configuration.
|
||||
const auto& type = type_config.type;
|
||||
const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx);
|
||||
if (UNLIKELY(IsIOError(offset))) {
|
||||
return base::unexpected(offset.error());
|
||||
}
|
||||
if (!offset.has_value()) {
|
||||
if (UNLIKELY(logging_enabled)) {
|
||||
if (package_is_loader) {
|
||||
resolution_type = Resolution::Step::Type::NO_ENTRY_LOADER;
|
||||
} else {
|
||||
resolution_type = Resolution::Step::Type::NO_ENTRY;
|
||||
}
|
||||
resolution_steps.push_back(Resolution::Step{resolution_type,
|
||||
this_config.toString(),
|
||||
&loaded_package->GetPackageName()});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
best_cookie = cookie;
|
||||
best_package = loaded_package;
|
||||
best_type = type;
|
||||
best_config = &this_config;
|
||||
best_offset = offset.value();
|
||||
|
||||
if (UNLIKELY(logging_enabled)) {
|
||||
last_resolution_.steps.push_back(Resolution::Step{resolution_type,
|
||||
this_config.toString(),
|
||||
&loaded_package->GetPackageName()});
|
||||
}
|
||||
// We can skip calling ResTable_config::match() if the caller does not care for the
|
||||
// configuration to match or if we're using the list of types that have already had their
|
||||
// configuration matched.
|
||||
const ResTable_config& this_config = type_entry->config;
|
||||
if (!(use_filtered || ignore_configuration || this_config.match(desired_config))) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// This is the slower path, which doesn't use the filtered list of configurations.
|
||||
// Here we must read the ResTable_config from the mmapped APK, convert it to host endianness
|
||||
// and fill in any new fields that did not exist when the APK was compiled.
|
||||
// Furthermore when selecting configurations we can't just record the pointer to the
|
||||
// ResTable_config, we must copy it.
|
||||
const auto iter_end = type_spec->types + type_spec->type_count;
|
||||
for (auto iter = type_spec->types; iter != iter_end; ++iter) {
|
||||
const incfs::verified_map_ptr<ResTable_type>& type = *iter;
|
||||
|
||||
ResTable_config this_config{};
|
||||
if (!ignore_configuration) {
|
||||
this_config.copyFromDtoH(type->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)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// The configuration matches and is better than the previous selection.
|
||||
// Find the entry value if it exists for this configuration.
|
||||
const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx);
|
||||
if (UNLIKELY(IsIOError(offset))) {
|
||||
return base::unexpected(offset.error());
|
||||
}
|
||||
if (!offset.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
best_cookie = cookie;
|
||||
best_package = loaded_package;
|
||||
best_type = type;
|
||||
best_config_copy = this_config;
|
||||
best_config = &best_config_copy;
|
||||
best_offset = offset.value();
|
||||
|
||||
if (stop_at_first_match) {
|
||||
// Any configuration will suffice, so break.
|
||||
break;
|
||||
}
|
||||
|
||||
Resolution::Step::Type resolution_type;
|
||||
if (best_config == nullptr) {
|
||||
resolution_type = Resolution::Step::Type::INITIAL;
|
||||
} else if (this_config.isBetterThan(*best_config, &desired_config)) {
|
||||
resolution_type = Resolution::Step::Type::BETTER_MATCH;
|
||||
} else if (package_is_loader && this_config.compare(*best_config) == 0) {
|
||||
resolution_type = Resolution::Step::Type::OVERLAID;
|
||||
} else {
|
||||
if (UNLIKELY(logging_enabled)) {
|
||||
last_resolution_.steps.push_back(Resolution::Step{resolution_type,
|
||||
this_config.toString(),
|
||||
&loaded_package->GetPackageName()});
|
||||
resolution_steps.push_back(Resolution::Step{Resolution::Step::Type::SKIPPED,
|
||||
this_config.toString(),
|
||||
&loaded_package->GetPackageName(),
|
||||
cookie});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// The configuration matches and is better than the previous selection.
|
||||
// Find the entry value if it exists for this configuration.
|
||||
const auto& type = type_entry->type;
|
||||
const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx);
|
||||
if (UNLIKELY(IsIOError(offset))) {
|
||||
return base::unexpected(offset.error());
|
||||
}
|
||||
|
||||
if (!offset.has_value()) {
|
||||
if (UNLIKELY(logging_enabled)) {
|
||||
resolution_steps.push_back(Resolution::Step{Resolution::Step::Type::NO_ENTRY,
|
||||
this_config.toString(),
|
||||
&loaded_package->GetPackageName(),
|
||||
cookie});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
best_cookie = cookie;
|
||||
best_package = loaded_package;
|
||||
best_type = type;
|
||||
best_config = &this_config;
|
||||
best_offset = offset.value();
|
||||
|
||||
if (UNLIKELY(logging_enabled)) {
|
||||
last_resolution_.steps.push_back(Resolution::Step{resolution_type,
|
||||
this_config.toString(),
|
||||
&loaded_package->GetPackageName(),
|
||||
cookie});
|
||||
}
|
||||
|
||||
// Any configuration will suffice, so break.
|
||||
if (stop_at_first_match) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -851,19 +803,16 @@ std::string AssetManager2::GetLastResourceResolution() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto cookie = last_resolution_.cookie;
|
||||
const ApkAssetsCookie cookie = last_resolution_.cookie;
|
||||
if (cookie == kInvalidCookie) {
|
||||
LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path.";
|
||||
return {};
|
||||
}
|
||||
|
||||
uint32_t resid = last_resolution_.resid;
|
||||
std::vector<Resolution::Step>& steps = last_resolution_.steps;
|
||||
const uint32_t resid = last_resolution_.resid;
|
||||
const auto package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
|
||||
|
||||
std::string resource_name_string;
|
||||
|
||||
const LoadedPackage* package =
|
||||
apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
|
||||
|
||||
if (package != nullptr) {
|
||||
auto resource_name = ToResourceName(last_resolution_.type_string_ref,
|
||||
last_resolution_.entry_string_ref,
|
||||
@@ -878,44 +827,24 @@ std::string AssetManager2::GetLastResourceResolution() const {
|
||||
<< "\n\tFor config -"
|
||||
<< configuration_.toString();
|
||||
|
||||
std::string prefix;
|
||||
for (Resolution::Step step : steps) {
|
||||
switch (step.type) {
|
||||
case Resolution::Step::Type::INITIAL:
|
||||
prefix = "Found initial";
|
||||
break;
|
||||
case Resolution::Step::Type::BETTER_MATCH:
|
||||
prefix = "Found better";
|
||||
break;
|
||||
case Resolution::Step::Type::BETTER_MATCH_LOADER:
|
||||
prefix = "Found better in loader";
|
||||
break;
|
||||
case Resolution::Step::Type::OVERLAID:
|
||||
prefix = "Overlaid";
|
||||
break;
|
||||
case Resolution::Step::Type::OVERLAID_LOADER:
|
||||
prefix = "Overlaid by loader";
|
||||
break;
|
||||
case Resolution::Step::Type::SKIPPED:
|
||||
prefix = "Skipped";
|
||||
break;
|
||||
case Resolution::Step::Type::SKIPPED_LOADER:
|
||||
prefix = "Skipped loader";
|
||||
break;
|
||||
case Resolution::Step::Type::NO_ENTRY:
|
||||
prefix = "No entry";
|
||||
break;
|
||||
case Resolution::Step::Type::NO_ENTRY_LOADER:
|
||||
prefix = "No entry for loader";
|
||||
break;
|
||||
for (const Resolution::Step& step : last_resolution_.steps) {
|
||||
const static std::unordered_map<Resolution::Step::Type, const char*> kStepStrings = {
|
||||
{Resolution::Step::Type::INITIAL, "Found initial"},
|
||||
{Resolution::Step::Type::BETTER_MATCH, "Found better"},
|
||||
{Resolution::Step::Type::OVERLAID, "Overlaid"},
|
||||
{Resolution::Step::Type::SKIPPED, "Skipped"},
|
||||
{Resolution::Step::Type::NO_ENTRY, "No entry"}
|
||||
};
|
||||
|
||||
const auto prefix = kStepStrings.find(step.type);
|
||||
if (prefix == kStepStrings.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!prefix.empty()) {
|
||||
log_stream << "\n\t" << prefix << ": " << *step.package_name;
|
||||
|
||||
if (!step.config_name.isEmpty()) {
|
||||
log_stream << " -" << step.config_name;
|
||||
}
|
||||
log_stream << "\n\t" << prefix->second << ": " << *step.package_name << " ("
|
||||
<< apk_assets_[step.cookie]->GetPath() << ")";
|
||||
if (!step.config_name.isEmpty()) {
|
||||
log_stream << " -" << step.config_name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -935,6 +864,16 @@ base::expected<AssetManager2::ResourceName, NullOrIOError> AssetManager2::GetRes
|
||||
*result->package_name);
|
||||
}
|
||||
|
||||
base::expected<uint32_t, NullOrIOError> AssetManager2::GetResourceTypeSpecFlags(
|
||||
uint32_t resid) const {
|
||||
auto result = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */,
|
||||
true /* ignore_configuration */);
|
||||
if (!result.has_value()) {
|
||||
return base::unexpected(result.error());
|
||||
}
|
||||
return result->type_flags;
|
||||
}
|
||||
|
||||
base::expected<AssetManager2::SelectedValue, NullOrIOError> AssetManager2::GetResource(
|
||||
uint32_t resid, bool may_be_bag, uint16_t density_override) const {
|
||||
auto result = FindEntry(resid, density_override, false /* stop_at_first_match */,
|
||||
@@ -1333,7 +1272,7 @@ base::expected<uint32_t, NullOrIOError> AssetManager2::GetResourceId(
|
||||
return base::unexpected(std::nullopt);
|
||||
}
|
||||
|
||||
void AssetManager2::RebuildFilterList(bool filter_incompatible_configs) {
|
||||
void AssetManager2::RebuildFilterList() {
|
||||
for (PackageGroup& group : package_groups_) {
|
||||
for (ConfiguredPackage& impl : group.packages_) {
|
||||
// Destroy it.
|
||||
@@ -1343,14 +1282,11 @@ void AssetManager2::RebuildFilterList(bool filter_incompatible_configs) {
|
||||
new (&impl.filtered_configs_) ByteBucketArray<FilteredConfigGroup>();
|
||||
|
||||
// Create the filters here.
|
||||
impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec* spec, uint8_t type_index) {
|
||||
FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_index);
|
||||
const auto iter_end = spec->types + spec->type_count;
|
||||
for (auto iter = spec->types; iter != iter_end; ++iter) {
|
||||
ResTable_config this_config;
|
||||
this_config.copyFromDtoH((*iter)->config);
|
||||
if (!filter_incompatible_configs || this_config.match(configuration_)) {
|
||||
group.type_configs.push_back(TypeConfig{*iter, this_config});
|
||||
impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec& type_spec, uint8_t type_id) {
|
||||
FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_id - 1);
|
||||
for (const auto& type_entry : type_spec.type_entries) {
|
||||
if (type_entry.config.match(configuration_)) {
|
||||
group.type_entries.push_back(&type_entry);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "androidfw/ByteBucketArray.h"
|
||||
#include "androidfw/Chunk.h"
|
||||
#include "androidfw/ResourceUtils.h"
|
||||
#include "androidfw/Util.h"
|
||||
@@ -49,36 +48,24 @@ namespace {
|
||||
// Builder that helps accumulate Type structs and then create a single
|
||||
// contiguous block of memory to store both the TypeSpec struct and
|
||||
// the Type structs.
|
||||
class TypeSpecPtrBuilder {
|
||||
public:
|
||||
explicit TypeSpecPtrBuilder(incfs::verified_map_ptr<ResTable_typeSpec> header)
|
||||
: header_(header) {
|
||||
}
|
||||
struct TypeSpecBuilder {
|
||||
explicit TypeSpecBuilder(incfs::verified_map_ptr<ResTable_typeSpec> header) : header_(header) {}
|
||||
|
||||
void AddType(incfs::verified_map_ptr<ResTable_type> type) {
|
||||
types_.push_back(type);
|
||||
TypeSpec::TypeEntry& entry = type_entries.emplace_back();
|
||||
entry.config.copyFromDtoH(type->config);
|
||||
entry.type = type;
|
||||
}
|
||||
|
||||
TypeSpecPtr Build() {
|
||||
// Check for overflow.
|
||||
using ElementType = incfs::verified_map_ptr<ResTable_type>;
|
||||
if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(ElementType) <
|
||||
types_.size()) {
|
||||
return {};
|
||||
}
|
||||
TypeSpec* type_spec =
|
||||
(TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType)));
|
||||
type_spec->type_spec = header_;
|
||||
type_spec->type_count = types_.size();
|
||||
memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType));
|
||||
return TypeSpecPtr(type_spec);
|
||||
TypeSpec Build() {
|
||||
return {header_, std::move(type_entries)};
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder);
|
||||
DISALLOW_COPY_AND_ASSIGN(TypeSpecBuilder);
|
||||
|
||||
incfs::verified_map_ptr<ResTable_typeSpec> header_;
|
||||
std::vector<incfs::verified_map_ptr<ResTable_type>> types_;
|
||||
std::vector<TypeSpec::TypeEntry> type_entries;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@@ -322,15 +309,10 @@ base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::Get
|
||||
}
|
||||
|
||||
base::expected<std::monostate, IOError> LoadedPackage::CollectConfigurations(
|
||||
bool exclude_mipmap, std::set<ResTable_config>* out_configs) const {
|
||||
const size_t type_count = type_specs_.size();
|
||||
for (size_t i = 0; i < type_count; i++) {
|
||||
const TypeSpecPtr& type_spec = type_specs_[i];
|
||||
if (type_spec == nullptr) {
|
||||
continue;
|
||||
}
|
||||
bool exclude_mipmap, std::set<ResTable_config>* out_configs) const {\
|
||||
for (const auto& type_spec : type_specs_) {
|
||||
if (exclude_mipmap) {
|
||||
const int type_idx = type_spec->type_spec->id - 1;
|
||||
const int type_idx = type_spec.first - 1;
|
||||
const auto type_name16 = type_string_pool_.stringAt(type_idx);
|
||||
if (UNLIKELY(IsIOError(type_name16))) {
|
||||
return base::unexpected(GetIOError(type_name16.error()));
|
||||
@@ -354,11 +336,8 @@ base::expected<std::monostate, IOError> LoadedPackage::CollectConfigurations(
|
||||
}
|
||||
}
|
||||
|
||||
const auto iter_end = type_spec->types + type_spec->type_count;
|
||||
for (auto iter = type_spec->types; iter != iter_end; ++iter) {
|
||||
ResTable_config config;
|
||||
config.copyFromDtoH((*iter)->config);
|
||||
out_configs->insert(config);
|
||||
for (const auto& type_entry : type_spec.second.type_entries) {
|
||||
out_configs->insert(type_entry.config);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
@@ -366,19 +345,12 @@ base::expected<std::monostate, IOError> LoadedPackage::CollectConfigurations(
|
||||
|
||||
void LoadedPackage::CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const {
|
||||
char temp_locale[RESTABLE_MAX_LOCALE_LEN];
|
||||
const size_t type_count = type_specs_.size();
|
||||
for (size_t i = 0; i < type_count; i++) {
|
||||
const TypeSpecPtr& type_spec = type_specs_[i];
|
||||
if (type_spec != nullptr) {
|
||||
const auto iter_end = type_spec->types + type_spec->type_count;
|
||||
for (auto iter = type_spec->types; iter != iter_end; ++iter) {
|
||||
ResTable_config configuration;
|
||||
configuration.copyFromDtoH((*iter)->config);
|
||||
if (configuration.locale != 0) {
|
||||
configuration.getBcp47Locale(temp_locale, canonicalize);
|
||||
std::string locale(temp_locale);
|
||||
out_locales->insert(std::move(locale));
|
||||
}
|
||||
for (const auto& type_spec : type_specs_) {
|
||||
for (const auto& type_entry : type_spec.second.type_entries) {
|
||||
if (type_entry.config.locale != 0) {
|
||||
type_entry.config.getBcp47Locale(temp_locale, canonicalize);
|
||||
std::string locale(temp_locale);
|
||||
out_locales->insert(std::move(locale));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -398,14 +370,13 @@ base::expected<uint32_t, NullOrIOError> LoadedPackage::FindEntryByName(
|
||||
return base::unexpected(key_idx.error());
|
||||
}
|
||||
|
||||
const TypeSpec* type_spec = type_specs_[*type_idx].get();
|
||||
const TypeSpec* type_spec = GetTypeSpecByTypeIndex(*type_idx);
|
||||
if (type_spec == nullptr) {
|
||||
return base::unexpected(std::nullopt);
|
||||
}
|
||||
|
||||
const auto iter_end = type_spec->types + type_spec->type_count;
|
||||
for (auto iter = type_spec->types; iter != iter_end; ++iter) {
|
||||
const incfs::verified_map_ptr<ResTable_type>& type = *iter;
|
||||
for (const auto& type_entry : type_spec->type_entries) {
|
||||
const incfs::verified_map_ptr<ResTable_type>& type = type_entry.type;
|
||||
|
||||
size_t entry_count = dtohl(type->entryCount);
|
||||
for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) {
|
||||
@@ -492,7 +463,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
|
||||
// A map of TypeSpec builders, each associated with an type index.
|
||||
// We use these to accumulate the set of Types available for a TypeSpec, and later build a single,
|
||||
// contiguous block of memory that holds all the Types together with the TypeSpec.
|
||||
std::unordered_map<int, std::unique_ptr<TypeSpecPtrBuilder>> type_builder_map;
|
||||
std::unordered_map<int, std::unique_ptr<TypeSpecBuilder>> type_builder_map;
|
||||
|
||||
ChunkIterator iter(chunk.data_ptr(), chunk.data_size());
|
||||
while (iter.HasNext()) {
|
||||
@@ -562,9 +533,9 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type_spec->id - 1];
|
||||
std::unique_ptr<TypeSpecBuilder>& builder_ptr = type_builder_map[type_spec->id];
|
||||
if (builder_ptr == nullptr) {
|
||||
builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec.verified());
|
||||
builder_ptr = util::make_unique<TypeSpecBuilder>(type_spec.verified());
|
||||
loaded_package->resource_ids_.set(type_spec->id, entry_count);
|
||||
} else {
|
||||
LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x",
|
||||
@@ -584,7 +555,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
|
||||
}
|
||||
|
||||
// Type chunks must be preceded by their TypeSpec chunks.
|
||||
std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type->id - 1];
|
||||
std::unique_ptr<TypeSpecBuilder>& builder_ptr = type_builder_map[type->id];
|
||||
if (builder_ptr != nullptr) {
|
||||
builder_ptr->AddType(type.verified());
|
||||
} else {
|
||||
@@ -722,14 +693,9 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
|
||||
|
||||
// Flatten and construct the TypeSpecs.
|
||||
for (auto& entry : type_builder_map) {
|
||||
uint8_t type_idx = static_cast<uint8_t>(entry.first);
|
||||
TypeSpecPtr type_spec_ptr = entry.second->Build();
|
||||
if (type_spec_ptr == nullptr) {
|
||||
LOG(ERROR) << "Too many type configurations, overflow detected.";
|
||||
return {};
|
||||
}
|
||||
|
||||
loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr);
|
||||
TypeSpec type_spec = entry.second->Build();
|
||||
uint8_t type_id = static_cast<uint8_t>(entry.first);
|
||||
loaded_package->type_specs_[type_id] = std::move(type_spec);
|
||||
}
|
||||
|
||||
return std::move(loaded_package);
|
||||
|
||||
@@ -101,12 +101,7 @@ class AssetManager2 {
|
||||
// Only pass invalidate_caches=false when it is known that the structure
|
||||
// change in ApkAssets is due to a safe addition of resources with completely
|
||||
// new resource IDs.
|
||||
//
|
||||
// Only pass in filter_incompatible_configs=false when you want to load all
|
||||
// configurations (including incompatible ones) such as when constructing an
|
||||
// idmap.
|
||||
bool SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, bool invalidate_caches = true,
|
||||
bool filter_incompatible_configs = true);
|
||||
bool SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, bool invalidate_caches = true);
|
||||
|
||||
inline const std::vector<const ApkAssets*> GetApkAssets() const {
|
||||
return apk_assets_;
|
||||
@@ -298,6 +293,12 @@ class AssetManager2 {
|
||||
// data failed.
|
||||
base::expected<const ResolvedBag*, NullOrIOError> ResolveBag(SelectedValue& value) const;
|
||||
|
||||
// Returns the android::ResTable_typeSpec flags of the resource ID.
|
||||
//
|
||||
// Returns a null error if the resource could not be resolved, or an I/O error if reading
|
||||
// resource data failed.
|
||||
base::expected<uint32_t, NullOrIOError> GetResourceTypeSpecFlags(uint32_t resid) const;
|
||||
|
||||
const std::vector<uint32_t> GetBagResIdStack(uint32_t resid) const;
|
||||
|
||||
// Resets the resource resolution structures in preparation for the next resource retrieval.
|
||||
@@ -330,15 +331,10 @@ class AssetManager2 {
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(AssetManager2);
|
||||
|
||||
struct TypeConfig {
|
||||
incfs::verified_map_ptr<ResTable_type> type;
|
||||
ResTable_config config;
|
||||
};
|
||||
|
||||
// A collection of configurations and their associated ResTable_type that match the current
|
||||
// AssetManager configuration.
|
||||
struct FilteredConfigGroup {
|
||||
std::vector<TypeConfig> type_configs;
|
||||
std::vector<const TypeSpec::TypeEntry*> type_entries;
|
||||
};
|
||||
|
||||
// Represents an single package.
|
||||
@@ -413,7 +409,7 @@ class AssetManager2 {
|
||||
|
||||
// Triggers the re-construction of lists of types that match the set configuration.
|
||||
// This should always be called when mutating the AssetManager's configuration or ApkAssets set.
|
||||
void RebuildFilterList(bool filter_incompatible_configs = true);
|
||||
void RebuildFilterList();
|
||||
|
||||
// Retrieves the APK paths of overlays that overlay non-system packages.
|
||||
std::set<std::string> GetNonSystemOverlayPaths() const;
|
||||
@@ -460,13 +456,9 @@ class AssetManager2 {
|
||||
enum class Type {
|
||||
INITIAL,
|
||||
BETTER_MATCH,
|
||||
BETTER_MATCH_LOADER,
|
||||
OVERLAID,
|
||||
OVERLAID_LOADER,
|
||||
SKIPPED,
|
||||
SKIPPED_LOADER,
|
||||
NO_ENTRY,
|
||||
NO_ENTRY_LOADER,
|
||||
};
|
||||
|
||||
// Marks what kind of override this step was.
|
||||
@@ -477,6 +469,9 @@ class AssetManager2 {
|
||||
|
||||
// Marks the package name of the better resource found in this step.
|
||||
const std::string* package_name;
|
||||
|
||||
//
|
||||
ApkAssetsCookie cookie = kInvalidCookie;
|
||||
};
|
||||
|
||||
// Last resolved resource ID.
|
||||
|
||||
@@ -47,18 +47,19 @@ class DynamicPackageEntry {
|
||||
// TypeSpec is going to be immediately proceeded by
|
||||
// an array of Type structs, all in the same block of memory.
|
||||
struct TypeSpec {
|
||||
// Pointer to the mmapped data where flags are kept.
|
||||
// Flags denote whether the resource entry is public
|
||||
// and under which configurations it varies.
|
||||
struct TypeEntry {
|
||||
incfs::verified_map_ptr<ResTable_type> type;
|
||||
|
||||
// Type configurations are accessed frequently when setting up an AssetManager and querying
|
||||
// resources. Access this cached configuration to minimize page faults.
|
||||
ResTable_config config;
|
||||
};
|
||||
|
||||
// Pointer to the mmapped data where flags are kept. Flags denote whether the resource entry is
|
||||
// public and under which configurations it varies.
|
||||
incfs::verified_map_ptr<ResTable_typeSpec> type_spec;
|
||||
|
||||
// The number of types that follow this struct.
|
||||
// There is a type for each configuration that entries are defined for.
|
||||
size_t type_count;
|
||||
|
||||
// Trick to easily access a variable number of Type structs
|
||||
// proceeding this struct, and to ensure their alignment.
|
||||
incfs::verified_map_ptr<ResTable_type> types[0];
|
||||
std::vector<TypeEntry> type_entries;
|
||||
|
||||
base::expected<uint32_t, NullOrIOError> GetFlagsForEntryIndex(uint16_t entry_index) const {
|
||||
if (entry_index >= dtohl(type_spec->entryCount)) {
|
||||
@@ -92,11 +93,6 @@ enum : package_property_t {
|
||||
PROPERTY_OVERLAY = 1U << 3U,
|
||||
};
|
||||
|
||||
// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of
|
||||
// ResTable_type pointers.
|
||||
// TypeSpecPtr is a managed pointer that knows how to delete itself.
|
||||
using TypeSpecPtr = util::unique_cptr<TypeSpec>;
|
||||
|
||||
struct OverlayableInfo {
|
||||
std::string name;
|
||||
std::string actor;
|
||||
@@ -239,17 +235,17 @@ class LoadedPackage {
|
||||
inline const TypeSpec* GetTypeSpecByTypeIndex(uint8_t type_index) const {
|
||||
// If the type IDs are offset in this package, we need to take that into account when searching
|
||||
// for a type.
|
||||
return type_specs_[type_index - type_id_offset_].get();
|
||||
const auto& type_spec = type_specs_.find(type_index + 1 - type_id_offset_);
|
||||
if (type_spec == type_specs_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &type_spec->second;
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void ForEachTypeSpec(Func f) const {
|
||||
for (size_t i = 0; i < type_specs_.size(); i++) {
|
||||
const TypeSpecPtr& ptr = type_specs_[i];
|
||||
if (ptr != nullptr) {
|
||||
uint8_t type_id = ptr->type_spec->id;
|
||||
f(ptr.get(), type_id - 1);
|
||||
}
|
||||
for (const auto& type_spec : type_specs_) {
|
||||
f(type_spec.second, type_spec.first);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,7 +285,7 @@ class LoadedPackage {
|
||||
int type_id_offset_ = 0;
|
||||
package_property_t property_flags_ = 0U;
|
||||
|
||||
ByteBucketArray<TypeSpecPtr> type_specs_;
|
||||
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_;
|
||||
|
||||
@@ -55,6 +55,12 @@ class AssetManager2Test : public ::testing::Test {
|
||||
basic_de_fr_assets_ = ApkAssets::Load("basic/basic_de_fr.apk");
|
||||
ASSERT_NE(nullptr, basic_de_fr_assets_);
|
||||
|
||||
basic_xhdpi_assets_ = ApkAssets::Load("basic/basic_xhdpi-v4.apk");
|
||||
ASSERT_NE(nullptr, basic_de_fr_assets_);
|
||||
|
||||
basic_xxhdpi_assets_ = ApkAssets::Load("basic/basic_xxhdpi-v4.apk");
|
||||
ASSERT_NE(nullptr, basic_de_fr_assets_);
|
||||
|
||||
style_assets_ = ApkAssets::Load("styles/styles.apk");
|
||||
ASSERT_NE(nullptr, style_assets_);
|
||||
|
||||
@@ -87,6 +93,8 @@ class AssetManager2Test : public ::testing::Test {
|
||||
protected:
|
||||
std::unique_ptr<const ApkAssets> basic_assets_;
|
||||
std::unique_ptr<const ApkAssets> basic_de_fr_assets_;
|
||||
std::unique_ptr<const ApkAssets> basic_xhdpi_assets_;
|
||||
std::unique_ptr<const ApkAssets> basic_xxhdpi_assets_;
|
||||
std::unique_ptr<const ApkAssets> style_assets_;
|
||||
std::unique_ptr<const ApkAssets> lib_one_assets_;
|
||||
std::unique_ptr<const ApkAssets> lib_two_assets_;
|
||||
@@ -225,6 +233,24 @@ TEST_F(AssetManager2Test, GetSharedLibraryResourceName) {
|
||||
ASSERT_EQ("com.android.lib_one:string/foo", ToFormattedResourceString(*name));
|
||||
}
|
||||
|
||||
TEST_F(AssetManager2Test, GetResourceNameNonMatchingConfig) {
|
||||
AssetManager2 assetmanager;
|
||||
assetmanager.SetApkAssets({basic_de_fr_assets_.get()});
|
||||
|
||||
auto value = assetmanager.GetResourceName(basic::R::string::test1);
|
||||
ASSERT_TRUE(value.has_value());
|
||||
EXPECT_EQ("com.android.basic:string/test1", ToFormattedResourceString(*value));
|
||||
}
|
||||
|
||||
TEST_F(AssetManager2Test, GetResourceTypeSpecFlags) {
|
||||
AssetManager2 assetmanager;
|
||||
assetmanager.SetApkAssets({basic_de_fr_assets_.get()});
|
||||
|
||||
auto value = assetmanager.GetResourceTypeSpecFlags(basic::R::string::test1);
|
||||
ASSERT_TRUE(value.has_value());
|
||||
EXPECT_EQ(ResTable_typeSpec::SPEC_PUBLIC | ResTable_config::CONFIG_LOCALE, *value);
|
||||
}
|
||||
|
||||
TEST_F(AssetManager2Test, FindsBagResourceFromSingleApkAssets) {
|
||||
AssetManager2 assetmanager;
|
||||
assetmanager.SetApkAssets({basic_assets_.get()});
|
||||
@@ -442,6 +468,29 @@ TEST_F(AssetManager2Test, ResolveDeepIdReference) {
|
||||
EXPECT_EQ(*low_ref, value->resid);
|
||||
}
|
||||
|
||||
TEST_F(AssetManager2Test, DensityOverride) {
|
||||
AssetManager2 assetmanager;
|
||||
assetmanager.SetApkAssets({basic_assets_.get(), basic_xhdpi_assets_.get(),
|
||||
basic_xxhdpi_assets_.get()});
|
||||
assetmanager.SetConfiguration({
|
||||
.density = ResTable_config::DENSITY_XHIGH,
|
||||
.sdkVersion = 21,
|
||||
});
|
||||
|
||||
auto value = assetmanager.GetResource(basic::R::string::density, false /*may_be_bag*/);
|
||||
ASSERT_TRUE(value.has_value());
|
||||
EXPECT_EQ(Res_value::TYPE_STRING, value->type);
|
||||
EXPECT_EQ("xhdpi", GetStringFromPool(assetmanager.GetStringPoolForCookie(value->cookie),
|
||||
value->data));
|
||||
|
||||
value = assetmanager.GetResource(basic::R::string::density, false /*may_be_bag*/,
|
||||
ResTable_config::DENSITY_XXHIGH);
|
||||
ASSERT_TRUE(value.has_value());
|
||||
EXPECT_EQ(Res_value::TYPE_STRING, value->type);
|
||||
EXPECT_EQ("xxhdpi", GetStringFromPool(assetmanager.GetStringPoolForCookie(value->cookie),
|
||||
value->data));
|
||||
}
|
||||
|
||||
TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) {
|
||||
AssetManager2 assetmanager;
|
||||
assetmanager.SetApkAssets({basic_assets_.get()});
|
||||
@@ -716,7 +765,7 @@ TEST_F(AssetManager2Test, GetLastPathWithSingleApkAssets) {
|
||||
|
||||
auto result = assetmanager.GetLastResourceResolution();
|
||||
EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n"
|
||||
"\tFor config -de\n\tFound initial: com.android.basic", result);
|
||||
"\tFor config -de\n\tFound initial: com.android.basic (basic/basic.apk)", result);
|
||||
}
|
||||
|
||||
TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) {
|
||||
@@ -736,8 +785,8 @@ TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) {
|
||||
auto result = assetmanager.GetLastResourceResolution();
|
||||
EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n"
|
||||
"\tFor config -de\n"
|
||||
"\tFound initial: com.android.basic\n"
|
||||
"\tFound better: com.android.basic -de", result);
|
||||
"\tFound initial: com.android.basic (basic/basic.apk)\n"
|
||||
"\tFound better: com.android.basic (basic/basic_de_fr.apk) -de", result);
|
||||
}
|
||||
|
||||
TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) {
|
||||
|
||||
@@ -65,10 +65,10 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) {
|
||||
|
||||
const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
|
||||
ASSERT_THAT(type_spec, NotNull());
|
||||
ASSERT_THAT(type_spec->type_count, Ge(1u));
|
||||
ASSERT_THAT(type_spec->type_entries.size(), Ge(1u));
|
||||
|
||||
auto type = type_spec->types[0];
|
||||
ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value());
|
||||
auto type = type_spec->type_entries[0];
|
||||
ASSERT_TRUE(LoadedPackage::GetEntry(type.type, entry_index).has_value());
|
||||
}
|
||||
|
||||
TEST(LoadedArscTest, LoadSparseEntryApp) {
|
||||
@@ -89,10 +89,10 @@ TEST(LoadedArscTest, LoadSparseEntryApp) {
|
||||
|
||||
const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
|
||||
ASSERT_THAT(type_spec, NotNull());
|
||||
ASSERT_THAT(type_spec->type_count, Ge(1u));
|
||||
ASSERT_THAT(type_spec->type_entries.size(), Ge(1u));
|
||||
|
||||
auto type = type_spec->types[0];
|
||||
ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value());
|
||||
auto type = type_spec->type_entries[0];
|
||||
ASSERT_TRUE(LoadedPackage::GetEntry(type.type, entry_index).has_value());
|
||||
}
|
||||
|
||||
TEST(LoadedArscTest, LoadSharedLibrary) {
|
||||
@@ -176,13 +176,13 @@ TEST(LoadedArscTest, LoadFeatureSplit) {
|
||||
|
||||
const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
|
||||
ASSERT_THAT(type_spec, NotNull());
|
||||
ASSERT_THAT(type_spec->type_count, Ge(1u));
|
||||
ASSERT_THAT(type_spec->type_entries.size(), Ge(1u));
|
||||
|
||||
auto type_name16 = package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1);
|
||||
ASSERT_TRUE(type_name16.has_value());
|
||||
EXPECT_THAT(util::Utf16ToUtf8(*type_name16), StrEq("string"));
|
||||
|
||||
ASSERT_TRUE(LoadedPackage::GetEntry(type_spec->types[0], entry_index).has_value());
|
||||
ASSERT_TRUE(LoadedPackage::GetEntry(type_spec->type_entries[0].type, entry_index).has_value());
|
||||
}
|
||||
|
||||
// AAPT(2) generates resource tables with chunks in a certain order. The rule is that
|
||||
@@ -217,11 +217,11 @@ TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) {
|
||||
|
||||
const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0);
|
||||
ASSERT_THAT(type_spec, NotNull());
|
||||
ASSERT_THAT(type_spec->type_count, Ge(1u));
|
||||
ASSERT_THAT(type_spec->type_entries.size(), Ge(1u));
|
||||
|
||||
type_spec = package->GetTypeSpecByTypeIndex(1);
|
||||
ASSERT_THAT(type_spec, NotNull());
|
||||
ASSERT_THAT(type_spec->type_count, Ge(1u));
|
||||
ASSERT_THAT(type_spec->type_entries.size(), Ge(1u));
|
||||
}
|
||||
|
||||
TEST(LoadedArscTest, LoadOverlayable) {
|
||||
@@ -363,10 +363,10 @@ TEST(LoadedArscTest, LoadCustomLoader) {
|
||||
|
||||
const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
|
||||
ASSERT_THAT(type_spec, NotNull());
|
||||
ASSERT_THAT(type_spec->type_count, Ge(1u));
|
||||
ASSERT_THAT(type_spec->type_entries.size(), Ge(1u));
|
||||
|
||||
auto type = type_spec->types[0];
|
||||
ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value());
|
||||
auto type = type_spec->type_entries[0];
|
||||
ASSERT_TRUE(LoadedPackage::GetEntry(type.type, entry_index).has_value());
|
||||
}
|
||||
|
||||
// structs with size fields (like Res_value, ResTable_entry) should be
|
||||
|
||||
@@ -227,8 +227,7 @@ bool AssetManagerSymbolSource::AddAssetPath(const StringPiece& path) {
|
||||
apk_assets.push_back(apk_asset.get());
|
||||
}
|
||||
|
||||
asset_manager_.SetApkAssets(apk_assets, true /* invalidate_caches */,
|
||||
false /* filter_incompatible_configs */);
|
||||
asset_manager_.SetApkAssets(apk_assets);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -351,9 +350,9 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName(
|
||||
return true;
|
||||
}
|
||||
|
||||
auto value = asset_manager_.GetResource(res_id.id, true /* may_be_bag */);
|
||||
if (value.has_value()) {
|
||||
type_spec_flags = value->flags;
|
||||
auto flags = asset_manager_.GetResourceTypeSpecFlags(res_id.id);
|
||||
if (flags.has_value()) {
|
||||
type_spec_flags = *flags;
|
||||
found = true;
|
||||
return false;
|
||||
}
|
||||
@@ -406,8 +405,8 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById(
|
||||
return {};
|
||||
}
|
||||
|
||||
auto value = asset_manager_.GetResource(id.id, true /* may_be_bag */);
|
||||
if (!value.has_value()) {
|
||||
auto flags = asset_manager_.GetResourceTypeSpecFlags(id.id);
|
||||
if (!flags.has_value()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -422,7 +421,7 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById(
|
||||
}
|
||||
|
||||
if (s) {
|
||||
s->is_public = (value->flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
|
||||
s->is_public = (*flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
|
||||
return s;
|
||||
}
|
||||
return {};
|
||||
|
||||
Reference in New Issue
Block a user