Loading core/jni/android_content_res_ApkAssets.cpp +5 −6 Original line number Diff line number Diff line Loading @@ -236,8 +236,8 @@ static void GetFlagValues(JNIEnv* env, FlagMap& flag_map) { "array"); } size_t i = 0; for (const auto& [_, flag_info] : flag_map) { jstring jstr = env->NewStringUTF(flag_info.name.c_str()); for (const auto& [flag_name, _] : flag_map) { jstring jstr = env->NewStringUTF(flag_name.c_str()); env->SetObjectArrayElement(flag_names, i++, jstr); env->DeleteLocalRef(jstr); } Loading @@ -249,10 +249,9 @@ static void GetFlagValues(JNIEnv* env, FlagMap& flag_map) { ALOGE("ApkAssets: Getting flag values failed due to jni error"); } else { i = 0; for (auto& [_, flag_info] : flag_map) { flag_info.status = flag_values[i++] != JNI_FALSE ? LoadedArscFeatureFlagStatus::Enabled : LoadedArscFeatureFlagStatus::Disabled; for (auto& [_, flag_value] : flag_map) { flag_value = flag_values[i++] != JNI_FALSE ? LoadedArscFlagStatus::Enabled : LoadedArscFlagStatus::Disabled; } } Loading libs/androidfw/LoadedArsc.cpp +37 −106 Original line number Diff line number Diff line Loading @@ -54,35 +54,24 @@ namespace { struct TypeSpecBuilder { explicit TypeSpecBuilder(incfs::verified_map_ptr<ResTable_typeSpec> header) : header_(header) { type_entries.reserve(dtohs(header_->typesCount)); flagged_type_entries.reserve(dtohs(header_->typesCount)); } void AddType(incfs::verified_map_ptr<ResTable_type> type, bool flagged) { TypeSpec::TypeEntry& entry = flagged ? flagged_type_entries.emplace_back() : type_entries.emplace_back(); void AddType(incfs::verified_map_ptr<ResTable_type> type) { TypeSpec::TypeEntry& entry = type_entries.emplace_back(); entry.config.copyFromDtoH(type->config); entry.type = type; } TypeSpec Build() { // put flagged values at the beginning so they are chosen over non-flagged ones if (!flagged_type_entries.empty()) { flagged_type_entries.insert(flagged_type_entries.end(), type_entries.begin(), type_entries.end()); flagged_type_entries.shrink_to_fit(); return {header_, std::move(flagged_type_entries)}; } else { type_entries.shrink_to_fit(); return {header_, std::move(type_entries)}; } } private: DISALLOW_COPY_AND_ASSIGN(TypeSpecBuilder); incfs::verified_map_ptr<ResTable_typeSpec> header_; std::vector<TypeSpec::TypeEntry> type_entries; std::vector<TypeSpec::TypeEntry> flagged_type_entries; }; } // namespace Loading Loading @@ -467,36 +456,8 @@ const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { return nullptr; } bool LoadType( const Chunk& chunk, std::unordered_map<int, std::optional<TypeSpecBuilder>>& type_builder_map, bool flagged) { const auto type = chunk.header<ResTable_type, kResTableTypeMinSize>(); if (!type) { LOG(ERROR) << "RES_TABLE_TYPE_TYPE too small."; return false; } if (!VerifyResTableType(type)) { return false; } // Type chunks must be preceded by their TypeSpec chunks. auto& maybe_type_builder = type_builder_map[type->id]; if (maybe_type_builder) { maybe_type_builder->AddType(type.verified(), flagged); } else { LOG(ERROR) << StringPrintf( "RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.", type->id); return false; } return true; } std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, package_property_t property_flags, const FlagMap& flag_map) { package_property_t property_flags) { ATRACE_NAME("LoadedPackage::Load"); const bool optimize_name_lookups = (property_flags & PROPERTY_OPTIMIZE_NAME_LOOKUPS) != 0; std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage(optimize_name_lookups)); Loading Loading @@ -632,49 +593,28 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_TYPE_TYPE: { if (!LoadType(child_chunk, type_builder_map, false)) { const auto type = child_chunk.header<ResTable_type, kResTableTypeMinSize>(); if (!type) { LOG(ERROR) << "RES_TABLE_TYPE_TYPE too small."; return {}; } } break; case RES_TABLE_FLAGGED: { if (android_content_res_resource_readwrite_flags()) { const auto flagged = child_chunk.header<ResTable_flagged>(); if (!flagged) { LOG(ERROR) << "RES_TABLE_FLAGGED too small."; if (!VerifyResTableType(type)) { return {}; } auto it = flag_map.find(flagged->flag_name_index.index); if (it == flag_map.end()) { // Type chunks must be preceded by their TypeSpec chunks. auto& maybe_type_builder = type_builder_map[type->id]; if (maybe_type_builder) { maybe_type_builder->AddType(type.verified()); } else { LOG(ERROR) << StringPrintf( "RES_TABLE_FLAGGED sections contains flag with index %d not in " "RES_TABLE_FLAG_LIST.", flagged->flag_name_index.index); return {}; } if (ShouldIncludeFlaggedResource(it->second.status, flagged->flag_negated)) { ChunkIterator flagged_type_iter(child_chunk.data_ptr(), child_chunk.data_size()); while (flagged_type_iter.HasNext()) { const Chunk flagged_child_chunk = flagged_type_iter.Next(); switch (flagged_child_chunk.type()) { case RES_TABLE_TYPE_TYPE: { if (!LoadType(flagged_child_chunk, type_builder_map, true)) { "RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.", type->id); return {}; } } break; default: LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type()); break; } } } } } break; case RES_TABLE_LIBRARY_TYPE: { const auto lib = child_chunk.header<ResTable_lib_header>(); if (!lib) { Loading Loading @@ -912,47 +852,29 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, case RES_TABLE_FLAG_LIST: { if (android_content_res_resource_readwrite_flags()) { if (!flag_map_.empty()) { LOG(WARNING) << "Multiple RES_TABLE_FLAG_LIST chunks found in RES_TABLE_TYPE, " "skipping all but the first one."; break; } const auto& flag_header = child_chunk.header<ResTable_flag_list>(); const auto names_begin = child_chunk.data_ptr().convert<uint32_t>(); size_t count = child_chunk.data_size() / sizeof(names_begin.value()); const auto names_end = names_begin + count; const auto start_index = child_chunk.data_ptr().convert<uint32_t>(); size_t count = child_chunk.data_size() / sizeof(start_index.value()); const auto end_index = start_index + count; if (count > 0 && global_string_pool_->size() == 0) { LOG(ERROR) << "RES_TABLE_FLAG_LIST with empty string pool"; return false; } for (auto name_iter = names_begin; name_iter != names_end; ++name_iter) { if (!name_iter) { for (auto index = start_index; index != end_index; ++index) { if (!index) { LOG(ERROR) << "Couldn't read RES_TABLE_FLAG_LIST."; return false; } auto sp_index = dtohl(name_iter.value()); auto sp_index = dtohl(index.value()); auto flag_name = global_string_pool_->string8At(sp_index); if (flag_name) { flag_map_.insert({sp_index, FeatureFlagInfo { .name = std::string(flag_name.value()), .status = LoadedArscFeatureFlagStatus::Unknown }}); flag_map_.insert({std::string(flag_name.value()), LoadedArscFlagStatus::Unknown}); } else { LOG(ERROR) << "flag list: couldn't find flag name with index " << sp_index; return false; } } if (!flag_map_.empty()) { if (get_flag_values_func_) { get_flag_values_func_(flag_map_); } else { for (auto& [_, flag_info] : flag_map_) { flag_info.status = LoadedArscFeatureFlagStatus::AlwaysShown; } } } } } break; Loading @@ -966,7 +888,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, packages_seen++; std::unique_ptr<const LoadedPackage> loaded_package = LoadedPackage::Load(child_chunk, property_flags, flag_map_); LoadedPackage::Load(child_chunk, property_flags); if (!loaded_package) { return false; } Loading @@ -983,6 +905,15 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, } } if (!flag_map_.empty()) { if (get_flag_values_func_) { get_flag_values_func_(flag_map_); } else { for (auto& [_, flag_value] : flag_map_) { flag_value = LoadedArscFlagStatus::AlwaysShown; } } } if (iter.HadError()) { LOG(ERROR) << iter.GetLastError(); Loading libs/androidfw/include/androidfw/LoadedArsc.h +14 −32 Original line number Diff line number Diff line Loading @@ -112,36 +112,6 @@ struct OverlayableInfo { uint32_t policy_flags; }; // Possible statuses for android feature flags enum class LoadedArscFeatureFlagStatus { // The flag is enabled and should be included only for non-negated conditions Enabled, // The flag is disabled and should be included only for negated conditions Disabled, // The flag should always be included regardless of whether the condition is negated or not AlwaysShown, // The flag should never be included regardless of whether the condition is negated or not AlwaysHidden, // The status of the flag is unknown Unknown }; struct FeatureFlagInfo { std::string name; LoadedArscFeatureFlagStatus status; }; inline bool ShouldIncludeFlaggedResource(LoadedArscFeatureFlagStatus status, bool flag_negated) { return (status == LoadedArscFeatureFlagStatus::AlwaysShown) || ((status == LoadedArscFeatureFlagStatus::Enabled) && !flag_negated) || ((status == LoadedArscFeatureFlagStatus::Disabled) && flag_negated); }; // A map from flag name (as an index in the StringPool it's stored in) to enabled status, during // resource loading we can check flag values to determine if values should be included using FlagMap = std::unordered_map<size_t, FeatureFlagInfo>; class LoadedPackage { public: class iterator { Loading Loading @@ -194,8 +164,7 @@ class LoadedPackage { } static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk, package_property_t property_flags, const FlagMap& flag_map); package_property_t property_flags); // Finds the entry with the specified type name and entry name. The names are in UTF-16 because // the underlying ResStringPool API expects this. For now this is acceptable, but since Loading Loading @@ -344,6 +313,19 @@ class LoadedPackage { std::unordered_map<std::string, std::string> overlayable_map_; }; // Possible statuses for android feature flags enum class LoadedArscFlagStatus { Enabled, Disabled, AlwaysShown, AlwaysHidden, Unknown }; // A map from flag name to enabled status, during resource loading we can check flag values to // determine if values should be included using FlagMap = std::unordered_map<std::string, LoadedArscFlagStatus>; // A callback that take a flag map and populates the enabled status for each of the flags using GetFlagValuesFunc = std::function<void(FlagMap&)>; Loading Loading
core/jni/android_content_res_ApkAssets.cpp +5 −6 Original line number Diff line number Diff line Loading @@ -236,8 +236,8 @@ static void GetFlagValues(JNIEnv* env, FlagMap& flag_map) { "array"); } size_t i = 0; for (const auto& [_, flag_info] : flag_map) { jstring jstr = env->NewStringUTF(flag_info.name.c_str()); for (const auto& [flag_name, _] : flag_map) { jstring jstr = env->NewStringUTF(flag_name.c_str()); env->SetObjectArrayElement(flag_names, i++, jstr); env->DeleteLocalRef(jstr); } Loading @@ -249,10 +249,9 @@ static void GetFlagValues(JNIEnv* env, FlagMap& flag_map) { ALOGE("ApkAssets: Getting flag values failed due to jni error"); } else { i = 0; for (auto& [_, flag_info] : flag_map) { flag_info.status = flag_values[i++] != JNI_FALSE ? LoadedArscFeatureFlagStatus::Enabled : LoadedArscFeatureFlagStatus::Disabled; for (auto& [_, flag_value] : flag_map) { flag_value = flag_values[i++] != JNI_FALSE ? LoadedArscFlagStatus::Enabled : LoadedArscFlagStatus::Disabled; } } Loading
libs/androidfw/LoadedArsc.cpp +37 −106 Original line number Diff line number Diff line Loading @@ -54,35 +54,24 @@ namespace { struct TypeSpecBuilder { explicit TypeSpecBuilder(incfs::verified_map_ptr<ResTable_typeSpec> header) : header_(header) { type_entries.reserve(dtohs(header_->typesCount)); flagged_type_entries.reserve(dtohs(header_->typesCount)); } void AddType(incfs::verified_map_ptr<ResTable_type> type, bool flagged) { TypeSpec::TypeEntry& entry = flagged ? flagged_type_entries.emplace_back() : type_entries.emplace_back(); void AddType(incfs::verified_map_ptr<ResTable_type> type) { TypeSpec::TypeEntry& entry = type_entries.emplace_back(); entry.config.copyFromDtoH(type->config); entry.type = type; } TypeSpec Build() { // put flagged values at the beginning so they are chosen over non-flagged ones if (!flagged_type_entries.empty()) { flagged_type_entries.insert(flagged_type_entries.end(), type_entries.begin(), type_entries.end()); flagged_type_entries.shrink_to_fit(); return {header_, std::move(flagged_type_entries)}; } else { type_entries.shrink_to_fit(); return {header_, std::move(type_entries)}; } } private: DISALLOW_COPY_AND_ASSIGN(TypeSpecBuilder); incfs::verified_map_ptr<ResTable_typeSpec> header_; std::vector<TypeSpec::TypeEntry> type_entries; std::vector<TypeSpec::TypeEntry> flagged_type_entries; }; } // namespace Loading Loading @@ -467,36 +456,8 @@ const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { return nullptr; } bool LoadType( const Chunk& chunk, std::unordered_map<int, std::optional<TypeSpecBuilder>>& type_builder_map, bool flagged) { const auto type = chunk.header<ResTable_type, kResTableTypeMinSize>(); if (!type) { LOG(ERROR) << "RES_TABLE_TYPE_TYPE too small."; return false; } if (!VerifyResTableType(type)) { return false; } // Type chunks must be preceded by their TypeSpec chunks. auto& maybe_type_builder = type_builder_map[type->id]; if (maybe_type_builder) { maybe_type_builder->AddType(type.verified(), flagged); } else { LOG(ERROR) << StringPrintf( "RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.", type->id); return false; } return true; } std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, package_property_t property_flags, const FlagMap& flag_map) { package_property_t property_flags) { ATRACE_NAME("LoadedPackage::Load"); const bool optimize_name_lookups = (property_flags & PROPERTY_OPTIMIZE_NAME_LOOKUPS) != 0; std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage(optimize_name_lookups)); Loading Loading @@ -632,49 +593,28 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_TYPE_TYPE: { if (!LoadType(child_chunk, type_builder_map, false)) { const auto type = child_chunk.header<ResTable_type, kResTableTypeMinSize>(); if (!type) { LOG(ERROR) << "RES_TABLE_TYPE_TYPE too small."; return {}; } } break; case RES_TABLE_FLAGGED: { if (android_content_res_resource_readwrite_flags()) { const auto flagged = child_chunk.header<ResTable_flagged>(); if (!flagged) { LOG(ERROR) << "RES_TABLE_FLAGGED too small."; if (!VerifyResTableType(type)) { return {}; } auto it = flag_map.find(flagged->flag_name_index.index); if (it == flag_map.end()) { // Type chunks must be preceded by their TypeSpec chunks. auto& maybe_type_builder = type_builder_map[type->id]; if (maybe_type_builder) { maybe_type_builder->AddType(type.verified()); } else { LOG(ERROR) << StringPrintf( "RES_TABLE_FLAGGED sections contains flag with index %d not in " "RES_TABLE_FLAG_LIST.", flagged->flag_name_index.index); return {}; } if (ShouldIncludeFlaggedResource(it->second.status, flagged->flag_negated)) { ChunkIterator flagged_type_iter(child_chunk.data_ptr(), child_chunk.data_size()); while (flagged_type_iter.HasNext()) { const Chunk flagged_child_chunk = flagged_type_iter.Next(); switch (flagged_child_chunk.type()) { case RES_TABLE_TYPE_TYPE: { if (!LoadType(flagged_child_chunk, type_builder_map, true)) { "RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.", type->id); return {}; } } break; default: LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type()); break; } } } } } break; case RES_TABLE_LIBRARY_TYPE: { const auto lib = child_chunk.header<ResTable_lib_header>(); if (!lib) { Loading Loading @@ -912,47 +852,29 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, case RES_TABLE_FLAG_LIST: { if (android_content_res_resource_readwrite_flags()) { if (!flag_map_.empty()) { LOG(WARNING) << "Multiple RES_TABLE_FLAG_LIST chunks found in RES_TABLE_TYPE, " "skipping all but the first one."; break; } const auto& flag_header = child_chunk.header<ResTable_flag_list>(); const auto names_begin = child_chunk.data_ptr().convert<uint32_t>(); size_t count = child_chunk.data_size() / sizeof(names_begin.value()); const auto names_end = names_begin + count; const auto start_index = child_chunk.data_ptr().convert<uint32_t>(); size_t count = child_chunk.data_size() / sizeof(start_index.value()); const auto end_index = start_index + count; if (count > 0 && global_string_pool_->size() == 0) { LOG(ERROR) << "RES_TABLE_FLAG_LIST with empty string pool"; return false; } for (auto name_iter = names_begin; name_iter != names_end; ++name_iter) { if (!name_iter) { for (auto index = start_index; index != end_index; ++index) { if (!index) { LOG(ERROR) << "Couldn't read RES_TABLE_FLAG_LIST."; return false; } auto sp_index = dtohl(name_iter.value()); auto sp_index = dtohl(index.value()); auto flag_name = global_string_pool_->string8At(sp_index); if (flag_name) { flag_map_.insert({sp_index, FeatureFlagInfo { .name = std::string(flag_name.value()), .status = LoadedArscFeatureFlagStatus::Unknown }}); flag_map_.insert({std::string(flag_name.value()), LoadedArscFlagStatus::Unknown}); } else { LOG(ERROR) << "flag list: couldn't find flag name with index " << sp_index; return false; } } if (!flag_map_.empty()) { if (get_flag_values_func_) { get_flag_values_func_(flag_map_); } else { for (auto& [_, flag_info] : flag_map_) { flag_info.status = LoadedArscFeatureFlagStatus::AlwaysShown; } } } } } break; Loading @@ -966,7 +888,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, packages_seen++; std::unique_ptr<const LoadedPackage> loaded_package = LoadedPackage::Load(child_chunk, property_flags, flag_map_); LoadedPackage::Load(child_chunk, property_flags); if (!loaded_package) { return false; } Loading @@ -983,6 +905,15 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, } } if (!flag_map_.empty()) { if (get_flag_values_func_) { get_flag_values_func_(flag_map_); } else { for (auto& [_, flag_value] : flag_map_) { flag_value = LoadedArscFlagStatus::AlwaysShown; } } } if (iter.HadError()) { LOG(ERROR) << iter.GetLastError(); Loading
libs/androidfw/include/androidfw/LoadedArsc.h +14 −32 Original line number Diff line number Diff line Loading @@ -112,36 +112,6 @@ struct OverlayableInfo { uint32_t policy_flags; }; // Possible statuses for android feature flags enum class LoadedArscFeatureFlagStatus { // The flag is enabled and should be included only for non-negated conditions Enabled, // The flag is disabled and should be included only for negated conditions Disabled, // The flag should always be included regardless of whether the condition is negated or not AlwaysShown, // The flag should never be included regardless of whether the condition is negated or not AlwaysHidden, // The status of the flag is unknown Unknown }; struct FeatureFlagInfo { std::string name; LoadedArscFeatureFlagStatus status; }; inline bool ShouldIncludeFlaggedResource(LoadedArscFeatureFlagStatus status, bool flag_negated) { return (status == LoadedArscFeatureFlagStatus::AlwaysShown) || ((status == LoadedArscFeatureFlagStatus::Enabled) && !flag_negated) || ((status == LoadedArscFeatureFlagStatus::Disabled) && flag_negated); }; // A map from flag name (as an index in the StringPool it's stored in) to enabled status, during // resource loading we can check flag values to determine if values should be included using FlagMap = std::unordered_map<size_t, FeatureFlagInfo>; class LoadedPackage { public: class iterator { Loading Loading @@ -194,8 +164,7 @@ class LoadedPackage { } static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk, package_property_t property_flags, const FlagMap& flag_map); package_property_t property_flags); // Finds the entry with the specified type name and entry name. The names are in UTF-16 because // the underlying ResStringPool API expects this. For now this is acceptable, but since Loading Loading @@ -344,6 +313,19 @@ class LoadedPackage { std::unordered_map<std::string, std::string> overlayable_map_; }; // Possible statuses for android feature flags enum class LoadedArscFlagStatus { Enabled, Disabled, AlwaysShown, AlwaysHidden, Unknown }; // A map from flag name to enabled status, during resource loading we can check flag values to // determine if values should be included using FlagMap = std::unordered_map<std::string, LoadedArscFlagStatus>; // A callback that take a flag map and populates the enabled status for each of the flags using GetFlagValuesFunc = std::function<void(FlagMap&)>; Loading