/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "format/proto/ProtoDeserialize.h" #include "ResourceTable.h" #include "ResourceUtils.h" #include "ResourceValues.h" #include "ValueVisitor.h" #include "android-base/logging.h" #include "android-base/macros.h" #include "androidfw/Locale.h" #include "androidfw/ResourceTypes.h" #include "androidfw/Util.h" using ::android::ConfigDescription; using ::android::LocaleValue; using ::android::ResStringPool; using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; namespace aapt { namespace { class ReferenceIdToNameVisitor : public DescendingValueVisitor { public: using DescendingValueVisitor::Visit; explicit ReferenceIdToNameVisitor(const std::map* mapping) : mapping_(mapping) { CHECK(mapping_ != nullptr); } void Visit(Reference* reference) override { if (!reference->id || !reference->id.value().is_valid()) { return; } ResourceId id = reference->id.value(); auto cache_iter = mapping_->find(id); if (cache_iter != mapping_->end()) { reference->name = cache_iter->second.ToResourceName(); } } private: DISALLOW_COPY_AND_ASSIGN(ReferenceIdToNameVisitor); const std::map* mapping_; }; } // namespace bool DeserializeConfigFromPb(const pb::Configuration& pb_config, ConfigDescription* out_config, std::string* out_error) { out_config->mcc = static_cast(pb_config.mcc()); out_config->mnc = static_cast(pb_config.mnc()); if (!pb_config.locale().empty()) { LocaleValue lv; if (!lv.InitFromBcp47Tag(pb_config.locale())) { std::ostringstream error; error << "configuration has invalid locale '" << pb_config.locale() << "'"; *out_error = error.str(); return false; } lv.WriteTo(out_config); } switch (pb_config.layout_direction()) { case pb::Configuration_LayoutDirection_LAYOUT_DIRECTION_LTR: out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_LAYOUTDIR) | ConfigDescription::LAYOUTDIR_LTR; break; case pb::Configuration_LayoutDirection_LAYOUT_DIRECTION_RTL: out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_LAYOUTDIR) | ConfigDescription::LAYOUTDIR_RTL; break; default: break; } out_config->smallestScreenWidthDp = static_cast(pb_config.smallest_screen_width_dp()); out_config->screenWidthDp = static_cast(pb_config.screen_width_dp()); out_config->screenHeightDp = static_cast(pb_config.screen_height_dp()); switch (pb_config.screen_layout_size()) { case pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_SMALL: out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENSIZE) | ConfigDescription::SCREENSIZE_SMALL; break; case pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_NORMAL: out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENSIZE) | ConfigDescription::SCREENSIZE_NORMAL; break; case pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_LARGE: out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENSIZE) | ConfigDescription::SCREENSIZE_LARGE; break; case pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_XLARGE: out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENSIZE) | ConfigDescription::SCREENSIZE_XLARGE; break; default: break; } switch (pb_config.screen_layout_long()) { case pb::Configuration_ScreenLayoutLong_SCREEN_LAYOUT_LONG_LONG: out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENLONG) | ConfigDescription::SCREENLONG_YES; break; case pb::Configuration_ScreenLayoutLong_SCREEN_LAYOUT_LONG_NOTLONG: out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENLONG) | ConfigDescription::SCREENLONG_NO; break; default: break; } switch (pb_config.screen_round()) { case pb::Configuration_ScreenRound_SCREEN_ROUND_ROUND: out_config->screenLayout2 = (out_config->screenLayout2 & ~ConfigDescription::MASK_SCREENROUND) | ConfigDescription::SCREENROUND_YES; break; case pb::Configuration_ScreenRound_SCREEN_ROUND_NOTROUND: out_config->screenLayout2 = (out_config->screenLayout2 & ~ConfigDescription::MASK_SCREENROUND) | ConfigDescription::SCREENROUND_NO; break; default: break; } switch (pb_config.wide_color_gamut()) { case pb::Configuration_WideColorGamut_WIDE_COLOR_GAMUT_WIDECG: out_config->colorMode = (out_config->colorMode & ~ConfigDescription::MASK_WIDE_COLOR_GAMUT) | ConfigDescription::WIDE_COLOR_GAMUT_YES; break; case pb::Configuration_WideColorGamut_WIDE_COLOR_GAMUT_NOWIDECG: out_config->colorMode = (out_config->colorMode & ~ConfigDescription::MASK_WIDE_COLOR_GAMUT) | ConfigDescription::WIDE_COLOR_GAMUT_NO; break; default: break; } switch (pb_config.hdr()) { case pb::Configuration_Hdr_HDR_HIGHDR: out_config->colorMode = (out_config->colorMode & ~ConfigDescription::MASK_HDR) | ConfigDescription::HDR_YES; break; case pb::Configuration_Hdr_HDR_LOWDR: out_config->colorMode = (out_config->colorMode & ~ConfigDescription::MASK_HDR) | ConfigDescription::HDR_NO; break; default: break; } switch (pb_config.orientation()) { case pb::Configuration_Orientation_ORIENTATION_PORT: out_config->orientation = ConfigDescription::ORIENTATION_PORT; break; case pb::Configuration_Orientation_ORIENTATION_LAND: out_config->orientation = ConfigDescription::ORIENTATION_LAND; break; case pb::Configuration_Orientation_ORIENTATION_SQUARE: out_config->orientation = ConfigDescription::ORIENTATION_SQUARE; break; default: break; } switch (pb_config.ui_mode_type()) { case pb::Configuration_UiModeType_UI_MODE_TYPE_NORMAL: out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) | ConfigDescription::UI_MODE_TYPE_NORMAL; break; case pb::Configuration_UiModeType_UI_MODE_TYPE_DESK: out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) | ConfigDescription::UI_MODE_TYPE_DESK; break; case pb::Configuration_UiModeType_UI_MODE_TYPE_CAR: out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) | ConfigDescription::UI_MODE_TYPE_CAR; break; case pb::Configuration_UiModeType_UI_MODE_TYPE_TELEVISION: out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) | ConfigDescription::UI_MODE_TYPE_TELEVISION; break; case pb::Configuration_UiModeType_UI_MODE_TYPE_APPLIANCE: out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) | ConfigDescription::UI_MODE_TYPE_APPLIANCE; break; case pb::Configuration_UiModeType_UI_MODE_TYPE_WATCH: out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) | ConfigDescription::UI_MODE_TYPE_WATCH; break; case pb::Configuration_UiModeType_UI_MODE_TYPE_VRHEADSET: out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) | ConfigDescription::UI_MODE_TYPE_VR_HEADSET; break; default: break; } switch (pb_config.ui_mode_night()) { case pb::Configuration_UiModeNight_UI_MODE_NIGHT_NIGHT: out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_NIGHT) | ConfigDescription::UI_MODE_NIGHT_YES; break; case pb::Configuration_UiModeNight_UI_MODE_NIGHT_NOTNIGHT: out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_NIGHT) | ConfigDescription::UI_MODE_NIGHT_NO; break; default: break; } out_config->density = static_cast(pb_config.density()); switch (pb_config.touchscreen()) { case pb::Configuration_Touchscreen_TOUCHSCREEN_NOTOUCH: out_config->touchscreen = ConfigDescription::TOUCHSCREEN_NOTOUCH; break; case pb::Configuration_Touchscreen_TOUCHSCREEN_STYLUS: out_config->touchscreen = ConfigDescription::TOUCHSCREEN_STYLUS; break; case pb::Configuration_Touchscreen_TOUCHSCREEN_FINGER: out_config->touchscreen = ConfigDescription::TOUCHSCREEN_FINGER; break; default: break; } switch (pb_config.keys_hidden()) { case pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSEXPOSED: out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_KEYSHIDDEN) | ConfigDescription::KEYSHIDDEN_NO; break; case pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSHIDDEN: out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_KEYSHIDDEN) | ConfigDescription::KEYSHIDDEN_YES; break; case pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSSOFT: out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_KEYSHIDDEN) | ConfigDescription::KEYSHIDDEN_SOFT; break; default: break; } switch (pb_config.keyboard()) { case pb::Configuration_Keyboard_KEYBOARD_NOKEYS: out_config->keyboard = ConfigDescription::KEYBOARD_NOKEYS; break; case pb::Configuration_Keyboard_KEYBOARD_QWERTY: out_config->keyboard = ConfigDescription::KEYBOARD_QWERTY; break; case pb::Configuration_Keyboard_KEYBOARD_TWELVEKEY: out_config->keyboard = ConfigDescription::KEYBOARD_12KEY; break; default: break; } switch (pb_config.nav_hidden()) { case pb::Configuration_NavHidden_NAV_HIDDEN_NAVEXPOSED: out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_NAVHIDDEN) | ConfigDescription::NAVHIDDEN_NO; break; case pb::Configuration_NavHidden_NAV_HIDDEN_NAVHIDDEN: out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_NAVHIDDEN) | ConfigDescription::NAVHIDDEN_YES; break; default: break; } switch (pb_config.navigation()) { case pb::Configuration_Navigation_NAVIGATION_NONAV: out_config->navigation = ConfigDescription::NAVIGATION_NONAV; break; case pb::Configuration_Navigation_NAVIGATION_DPAD: out_config->navigation = ConfigDescription::NAVIGATION_DPAD; break; case pb::Configuration_Navigation_NAVIGATION_TRACKBALL: out_config->navigation = ConfigDescription::NAVIGATION_TRACKBALL; break; case pb::Configuration_Navigation_NAVIGATION_WHEEL: out_config->navigation = ConfigDescription::NAVIGATION_WHEEL; break; default: break; } out_config->screenWidth = static_cast(pb_config.screen_width()); out_config->screenHeight = static_cast(pb_config.screen_height()); out_config->sdkVersion = static_cast(pb_config.sdk_version()); out_config->grammaticalInflection = pb_config.grammatical_gender(); return true; } static void DeserializeSourceFromPb(const pb::Source& pb_source, const ResStringPool& src_pool, android::Source* out_source) { out_source->path = android::util::GetString(src_pool, pb_source.path_idx()); out_source->line = static_cast(pb_source.position().line_number()); } static Visibility::Level DeserializeVisibilityFromPb(const pb::Visibility::Level& pb_level) { switch (pb_level) { case pb::Visibility::PRIVATE: return Visibility::Level::kPrivate; case pb::Visibility::PUBLIC: return Visibility::Level::kPublic; default: break; } return Visibility::Level::kUndefined; } bool DeserializeOverlayableItemFromPb(const pb::OverlayableItem& pb_overlayable, const android::ResStringPool& src_pool, OverlayableItem* out_overlayable, std::string* out_error) { for (const int policy : pb_overlayable.policy()) { switch (policy) { case pb::OverlayableItem::PUBLIC: out_overlayable->policies |= PolicyFlags::PUBLIC; break; case pb::OverlayableItem::SYSTEM: out_overlayable->policies |= PolicyFlags::SYSTEM_PARTITION; break; case pb::OverlayableItem::VENDOR: out_overlayable->policies |= PolicyFlags::VENDOR_PARTITION; break; case pb::OverlayableItem::PRODUCT: out_overlayable->policies |= PolicyFlags::PRODUCT_PARTITION; break; case pb::OverlayableItem::SIGNATURE: out_overlayable->policies |= PolicyFlags::SIGNATURE; break; case pb::OverlayableItem::ODM: out_overlayable->policies |= PolicyFlags::ODM_PARTITION; break; case pb::OverlayableItem::OEM: out_overlayable->policies |= PolicyFlags::OEM_PARTITION; break; case pb::OverlayableItem::ACTOR: out_overlayable->policies |= PolicyFlags::ACTOR_SIGNATURE; break; case pb::OverlayableItem::CONFIG_SIGNATURE: out_overlayable->policies |= PolicyFlags::CONFIG_SIGNATURE; break; default: *out_error = "unknown overlayable policy"; return false; } } if (pb_overlayable.has_source()) { DeserializeSourceFromPb(pb_overlayable.source(), src_pool, &out_overlayable->source); } out_overlayable->comment = pb_overlayable.comment(); return true; } static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStringPool& src_pool, io::IFileCollection* files, const std::vector>& overlayables, ResourceTable* out_table, std::string* out_error) { std::map id_index; ResourceTablePackage* pkg = out_table->FindOrCreatePackage(pb_package.package_name()); for (const pb::Type& pb_type : pb_package.type()) { auto res_type = ParseResourceNamedType(pb_type.name()); if (!res_type) { std::ostringstream error; error << "unknown type '" << pb_type.name() << "'"; *out_error = error.str(); return false; } ResourceTableType* type = pkg->FindOrCreateType(*res_type); for (const pb::Entry& pb_entry : pb_type.entry()) { ResourceEntry* entry = type->CreateEntry(pb_entry.name()); const ResourceId resource_id( pb_package.has_package_id() ? static_cast(pb_package.package_id().id()) : 0u, pb_type.has_type_id() ? static_cast(pb_type.type_id().id()) : 0u, pb_entry.has_entry_id() ? static_cast(pb_entry.entry_id().id()) : 0u); if (resource_id.id != 0u) { entry->id = resource_id; } // Deserialize the symbol status (public/private with source and comments). if (pb_entry.has_visibility()) { const pb::Visibility& pb_visibility = pb_entry.visibility(); if (pb_visibility.has_source()) { DeserializeSourceFromPb(pb_visibility.source(), src_pool, &entry->visibility.source); } entry->visibility.comment = pb_visibility.comment(); entry->visibility.staged_api = pb_visibility.staged_api(); const Visibility::Level level = DeserializeVisibilityFromPb(pb_visibility.level()); entry->visibility.level = level; if (level == Visibility::Level::kPublic) { // Propagate the public visibility up to the Type. type->visibility_level = Visibility::Level::kPublic; } else if (level == Visibility::Level::kPrivate) { // Only propagate if no previous state was assigned. if (type->visibility_level == Visibility::Level::kUndefined) { type->visibility_level = Visibility::Level::kPrivate; } } } if (pb_entry.has_allow_new()) { const pb::AllowNew& pb_allow_new = pb_entry.allow_new(); AllowNew allow_new; if (pb_allow_new.has_source()) { DeserializeSourceFromPb(pb_allow_new.source(), src_pool, &allow_new.source); } allow_new.comment = pb_allow_new.comment(); entry->allow_new = std::move(allow_new); } if (pb_entry.has_overlayable_item()) { // Find the overlayable to which this item belongs pb::OverlayableItem pb_overlayable_item = pb_entry.overlayable_item(); if (pb_overlayable_item.overlayable_idx() >= overlayables.size()) { *out_error = android::base::StringPrintf("invalid overlayable_idx value %d for entry %s/%s", pb_overlayable_item.overlayable_idx(), pb_type.name().c_str(), pb_entry.name().c_str()); return false; } OverlayableItem overlayable_item(overlayables[pb_overlayable_item.overlayable_idx()]); if (!DeserializeOverlayableItemFromPb(pb_overlayable_item, src_pool, &overlayable_item, out_error)) { return false; } entry->overlayable_item = std::move(overlayable_item); } if (pb_entry.has_staged_id()) { const pb::StagedId& pb_staged_id = pb_entry.staged_id(); StagedId staged_id; if (pb_staged_id.has_source()) { DeserializeSourceFromPb(pb_staged_id.source(), src_pool, &staged_id.source); } staged_id.id = pb_staged_id.staged_id(); entry->staged_id = std::move(staged_id); } ResourceId resid(pb_package.package_id().id(), pb_type.type_id().id(), pb_entry.entry_id().id()); if (resid.is_valid()) { id_index[resid] = ResourceNameRef(pkg->name, type->named_type, entry->name); } for (const pb::ConfigValue& pb_config_value : pb_entry.config_value()) { const pb::Configuration& pb_config = pb_config_value.config(); ConfigDescription config; if (!DeserializeConfigFromPb(pb_config, &config, out_error)) { return false; } ResourceConfigValue* config_value = entry->FindOrCreateValue(config, pb_config.product()); if (config_value->value != nullptr) { *out_error = "duplicate configuration in resource table"; return false; } config_value->value = DeserializeValueFromPb(pb_config_value.value(), src_pool, config, &out_table->string_pool, files, out_error); if (config_value->value == nullptr) { return false; } } } } ReferenceIdToNameVisitor visitor(&id_index); VisitAllValuesInPackage(pkg, &visitor); return true; } bool DeserializeTableFromPb(const pb::ResourceTable& pb_table, io::IFileCollection* files, ResourceTable* out_table, std::string* out_error) { // We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which // causes errors when qualifying it with android:: using namespace android; ResStringPool source_pool; if (pb_table.has_source_pool()) { status_t result = source_pool.setTo(pb_table.source_pool().data().data(), pb_table.source_pool().data().size()); if (result != NO_ERROR) { *out_error = "invalid source pool"; return false; } } for (const pb::DynamicRefTable& dynamic_ref : pb_table.dynamic_ref_table()) { out_table->included_packages_.insert( {dynamic_ref.package_id().id(), dynamic_ref.package_name()}); } // Deserialize the overlayable groups of the table std::vector> overlayables; for (const pb::Overlayable& pb_overlayable : pb_table.overlayable()) { auto group = std::make_shared(pb_overlayable.name(), pb_overlayable.actor()); if (pb_overlayable.has_source()) { DeserializeSourceFromPb(pb_overlayable.source(), source_pool, &group->source); } overlayables.push_back(group); } for (const pb::Package& pb_package : pb_table.package()) { if (!DeserializePackageFromPb(pb_package, source_pool, files, overlayables, out_table, out_error)) { return false; } } return true; } static ResourceFile::Type DeserializeFileReferenceTypeFromPb(const pb::FileReference::Type& type) { switch (type) { case pb::FileReference::BINARY_XML: return ResourceFile::Type::kBinaryXml; case pb::FileReference::PROTO_XML: return ResourceFile::Type::kProtoXml; case pb::FileReference::PNG: return ResourceFile::Type::kPng; default: return ResourceFile::Type::kUnknown; } } bool DeserializeCompiledFileFromPb(const pb::internal::CompiledFile& pb_file, ResourceFile* out_file, std::string* out_error) { ResourceNameRef name_ref; if (!ResourceUtils::ParseResourceName(pb_file.resource_name(), &name_ref)) { std::ostringstream error; error << "invalid resource name in compiled file header: " << pb_file.resource_name(); *out_error = error.str(); return false; } out_file->name = name_ref.ToResourceName(); out_file->source.path = pb_file.source_path(); out_file->type = DeserializeFileReferenceTypeFromPb(pb_file.type()); std::string config_error; if (!DeserializeConfigFromPb(pb_file.config(), &out_file->config, &config_error)) { std::ostringstream error; error << "invalid resource configuration in compiled file header: " << config_error; *out_error = error.str(); return false; } for (const pb::internal::CompiledFile_Symbol& pb_symbol : pb_file.exported_symbol()) { if (!ResourceUtils::ParseResourceName(pb_symbol.resource_name(), &name_ref)) { std::ostringstream error; error << "invalid resource name for exported symbol in compiled file header: " << pb_file.resource_name(); *out_error = error.str(); return false; } size_t line = 0u; if (pb_symbol.has_source()) { line = pb_symbol.source().line_number(); } out_file->exported_symbols.push_back(SourcedResourceName{name_ref.ToResourceName(), line}); } return true; } static Reference::Type DeserializeReferenceTypeFromPb(const pb::Reference_Type& pb_type) { switch (pb_type) { case pb::Reference_Type_REFERENCE: return Reference::Type::kResource; case pb::Reference_Type_ATTRIBUTE: return Reference::Type::kAttribute; default: break; } return Reference::Type::kResource; } static bool DeserializeReferenceFromPb(const pb::Reference& pb_ref, Reference* out_ref, std::string* out_error) { out_ref->reference_type = DeserializeReferenceTypeFromPb(pb_ref.type()); out_ref->private_reference = pb_ref.private_(); out_ref->is_dynamic = pb_ref.is_dynamic().value(); if (pb_ref.id() != 0) { out_ref->id = ResourceId(pb_ref.id()); } if (!pb_ref.name().empty()) { ResourceNameRef name_ref; if (!ResourceUtils::ParseResourceName(pb_ref.name(), &name_ref, nullptr)) { std::ostringstream error; error << "reference has invalid resource name '" << pb_ref.name() << "'"; *out_error = error.str(); return false; } out_ref->name = name_ref.ToResourceName(); } if (pb_ref.type_flags() != 0) { out_ref->type_flags = pb_ref.type_flags(); } out_ref->allow_raw = pb_ref.allow_raw(); return true; } static bool DeserializeMacroFromPb(const pb::MacroBody& pb_ref, Macro* out_ref, std::string* out_error) { out_ref->raw_value = pb_ref.raw_string(); if (pb_ref.has_style_string()) { out_ref->style_string.str = pb_ref.style_string().str(); for (const auto& span : pb_ref.style_string().spans()) { out_ref->style_string.spans.emplace_back(android::Span{ .name = span.name(), .first_char = span.start_index(), .last_char = span.end_index()}); } } for (const auto& untranslatable_section : pb_ref.untranslatable_sections()) { out_ref->untranslatable_sections.emplace_back( UntranslatableSection{.start = static_cast(untranslatable_section.start_index()), .end = static_cast(untranslatable_section.end_index())}); } for (const auto& namespace_decls : pb_ref.namespace_stack()) { out_ref->alias_namespaces.emplace_back( Macro::Namespace{.alias = namespace_decls.prefix(), .package_name = namespace_decls.package_name(), .is_private = namespace_decls.is_private()}); } return true; } template static void DeserializeItemMetaDataFromPb(const T& pb_item, const android::ResStringPool& src_pool, Value* out_value) { if (pb_item.has_source()) { android::Source source; DeserializeSourceFromPb(pb_item.source(), src_pool, &source); out_value->SetSource(std::move(source)); } out_value->SetComment(pb_item.comment()); } static size_t DeserializePluralEnumFromPb(const pb::Plural_Arity& arity) { switch (arity) { case pb::Plural_Arity_ZERO: return Plural::Zero; case pb::Plural_Arity_ONE: return Plural::One; case pb::Plural_Arity_TWO: return Plural::Two; case pb::Plural_Arity_FEW: return Plural::Few; case pb::Plural_Arity_MANY: return Plural::Many; default: break; } return Plural::Other; } std::unique_ptr DeserializeValueFromPb(const pb::Value& pb_value, const android::ResStringPool& src_pool, const ConfigDescription& config, android::StringPool* value_pool, io::IFileCollection* files, std::string* out_error) { std::unique_ptr value; if (pb_value.has_item()) { value = DeserializeItemFromPb(pb_value.item(), src_pool, config, value_pool, files, out_error); if (value == nullptr) { return {}; } } else if (pb_value.has_compound_value()) { const pb::CompoundValue& pb_compound_value = pb_value.compound_value(); switch (pb_compound_value.value_case()) { case pb::CompoundValue::kAttr: { const pb::Attribute& pb_attr = pb_compound_value.attr(); std::unique_ptr attr = util::make_unique(pb_attr.format_flags()); attr->min_int = pb_attr.min_int(); attr->max_int = pb_attr.max_int(); for (const pb::Attribute_Symbol& pb_symbol : pb_attr.symbol()) { Attribute::Symbol symbol; DeserializeItemMetaDataFromPb(pb_symbol, src_pool, &symbol.symbol); if (!DeserializeReferenceFromPb(pb_symbol.name(), &symbol.symbol, out_error)) { return {}; } symbol.value = pb_symbol.value(); symbol.type = pb_symbol.type() != 0U ? pb_symbol.type() : android::Res_value::TYPE_INT_DEC; attr->symbols.push_back(std::move(symbol)); } value = std::move(attr); } break; case pb::CompoundValue::kStyle: { const pb::Style& pb_style = pb_compound_value.style(); std::unique_ptr