| /* |
| * 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 "android-base/logging.h" |
| #include "android-base/macros.h" |
| #include "androidfw/ResourceTypes.h" |
| #include "androidfw/Locale.h" |
| |
| #include "ResourceTable.h" |
| #include "ResourceUtils.h" |
| #include "ResourceValues.h" |
| #include "ValueVisitor.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<ResourceId, ResourceNameRef>* 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<ResourceId, ResourceNameRef>* mapping_; |
| }; |
| |
| } // namespace |
| |
| bool DeserializeConfigFromPb(const pb::Configuration& pb_config, ConfigDescription* out_config, |
| std::string* out_error) { |
| out_config->mcc = static_cast<uint16_t>(pb_config.mcc()); |
| out_config->mnc = static_cast<uint16_t>(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<uint16_t>(pb_config.smallest_screen_width_dp()); |
| out_config->screenWidthDp = static_cast<uint16_t>(pb_config.screen_width_dp()); |
| out_config->screenHeightDp = static_cast<uint16_t>(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<uint16_t>(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<uint16_t>(pb_config.screen_width()); |
| out_config->screenHeight = static_cast<uint16_t>(pb_config.screen_height()); |
| out_config->sdkVersion = static_cast<uint16_t>(pb_config.sdk_version()); |
| return true; |
| } |
| |
| static void DeserializeSourceFromPb(const pb::Source& pb_source, const ResStringPool& src_pool, |
| Source* out_source) { |
| out_source->path = util::GetString(src_pool, pb_source.path_idx()); |
| out_source->line = static_cast<size_t>(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; |
| 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<std::shared_ptr<Overlayable>>& overlayables, |
| ResourceTable* out_table, std::string* out_error) { |
| Maybe<uint8_t> id; |
| if (pb_package.has_package_id()) { |
| id = static_cast<uint8_t>(pb_package.package_id().id()); |
| } |
| |
| std::map<ResourceId, ResourceNameRef> id_index; |
| |
| ResourceTablePackage* pkg = |
| out_table->CreatePackageAllowingDuplicateNames(pb_package.package_name(), id); |
| for (const pb::Type& pb_type : pb_package.type()) { |
| const ResourceType* res_type = ParseResourceType(pb_type.name()); |
| if (res_type == nullptr) { |
| std::ostringstream error; |
| error << "unknown type '" << pb_type.name() << "'"; |
| *out_error = error.str(); |
| return false; |
| } |
| |
| ResourceTableType* type = pkg->FindOrCreateType(*res_type); |
| if (pb_type.has_type_id()) { |
| type->id = static_cast<uint8_t>(pb_type.type_id().id()); |
| } |
| |
| for (const pb::Entry& pb_entry : pb_type.entry()) { |
| ResourceEntry* entry; |
| if (pb_entry.has_entry_id()) { |
| auto entry_id = static_cast<uint16_t>(pb_entry.entry_id().id()); |
| entry = type->FindOrCreateEntry(pb_entry.name(), entry_id); |
| } else { |
| entry = type->FindOrCreateEntry(pb_entry.name()); |
| } |
| |
| // 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(); |
| |
| 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); |
| } |
| |
| 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->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; |
| } |
| } |
| |
| // Deserialize the overlayable groups of the table |
| std::vector<std::shared_ptr<Overlayable>> overlayables; |
| for (const pb::Overlayable& pb_overlayable : pb_table.overlayable()) { |
| auto group = std::make_shared<Overlayable>(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(); |
| } |
| return true; |
| } |
| |
| template <typename T> |
| static void DeserializeItemMetaDataFromPb(const T& pb_item, const android::ResStringPool& src_pool, |
| Value* out_value) { |
| if (pb_item.has_source()) { |
| 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<Value> DeserializeValueFromPb(const pb::Value& pb_value, |
| const android::ResStringPool& src_pool, |
| const ConfigDescription& config, |
| StringPool* value_pool, io::IFileCollection* files, |
| std::string* out_error) { |
| std::unique_ptr<Value> 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<Attribute> attr = util::make_unique<Attribute>(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<Style> style = util::make_unique<Style>(); |
| if (pb_style.has_parent()) { |
| style->parent = Reference(); |
| if (!DeserializeReferenceFromPb(pb_style.parent(), &style->parent.value(), out_error)) { |
| return {}; |
| } |
| |
| if (pb_style.has_parent_source()) { |
| Source parent_source; |
| DeserializeSourceFromPb(pb_style.parent_source(), src_pool, &parent_source); |
| style->parent.value().SetSource(std::move(parent_source)); |
| } |
| } |
| |
| for (const pb::Style_Entry& pb_entry : pb_style.entry()) { |
| Style::Entry entry; |
| if (!DeserializeReferenceFromPb(pb_entry.key(), &entry.key, out_error)) { |
| return {}; |
| } |
| DeserializeItemMetaDataFromPb(pb_entry, src_pool, &entry.key); |
| entry.value = DeserializeItemFromPb(pb_entry.item(), src_pool, config, value_pool, files, |
| out_error); |
| if (entry.value == nullptr) { |
| return {}; |
| } |
| |
| // Copy the meta-data into the value as well. |
| DeserializeItemMetaDataFromPb(pb_entry, src_pool, entry.value.get()); |
| style->entries.push_back(std::move(entry)); |
| } |
| value = std::move(style); |
| } break; |
| |
| case pb::CompoundValue::kStyleable: { |
| const pb::Styleable& pb_styleable = pb_compound_value.styleable(); |
| std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>(); |
| for (const pb::Styleable_Entry& pb_entry : pb_styleable.entry()) { |
| Reference attr_ref; |
| DeserializeItemMetaDataFromPb(pb_entry, src_pool, &attr_ref); |
| DeserializeReferenceFromPb(pb_entry.attr(), &attr_ref, out_error); |
| styleable->entries.push_back(std::move(attr_ref)); |
| } |
| value = std::move(styleable); |
| } break; |
| |
| case pb::CompoundValue::kArray: { |
| const pb::Array& pb_array = pb_compound_value.array(); |
| std::unique_ptr<Array> array = util::make_unique<Array>(); |
| for (const pb::Array_Element& pb_entry : pb_array.element()) { |
| std::unique_ptr<Item> item = DeserializeItemFromPb(pb_entry.item(), src_pool, config, |
| value_pool, files, out_error); |
| if (item == nullptr) { |
| return {}; |
| } |
| |
| DeserializeItemMetaDataFromPb(pb_entry, src_pool, item.get()); |
| array->elements.push_back(std::move(item)); |
| } |
| value = std::move(array); |
| } break; |
| |
| case pb::CompoundValue::kPlural: { |
| const pb::Plural& pb_plural = pb_compound_value.plural(); |
| std::unique_ptr<Plural> plural = util::make_unique<Plural>(); |
| for (const pb::Plural_Entry& pb_entry : pb_plural.entry()) { |
| size_t plural_idx = DeserializePluralEnumFromPb(pb_entry.arity()); |
| plural->values[plural_idx] = DeserializeItemFromPb(pb_entry.item(), src_pool, config, |
| value_pool, files, out_error); |
| if (!plural->values[plural_idx]) { |
| return {}; |
| } |
| |
| DeserializeItemMetaDataFromPb(pb_entry, src_pool, plural->values[plural_idx].get()); |
| } |
| value = std::move(plural); |
| } break; |
| |
| default: |
| LOG(FATAL) << "unknown compound value: " << (int)pb_compound_value.value_case(); |
| break; |
| } |
| } else { |
| LOG(FATAL) << "unknown value: " << (int)pb_value.value_case(); |
| return {}; |
| } |
| |
| CHECK(value) << "forgot to set value"; |
| |
| value->SetWeak(pb_value.weak()); |
| DeserializeItemMetaDataFromPb(pb_value, src_pool, value.get()); |
| return value; |
| } |
| |
| std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item, |
| const android::ResStringPool& src_pool, |
| const ConfigDescription& config, StringPool* value_pool, |
| io::IFileCollection* files, std::string* out_error) { |
| switch (pb_item.value_case()) { |
| case pb::Item::kRef: { |
| const pb::Reference& pb_ref = pb_item.ref(); |
| std::unique_ptr<Reference> ref = util::make_unique<Reference>(); |
| if (!DeserializeReferenceFromPb(pb_ref, ref.get(), out_error)) { |
| return {}; |
| } |
| return std::move(ref); |
| } break; |
| |
| case pb::Item::kPrim: { |
| const pb::Primitive& pb_prim = pb_item.prim(); |
| android::Res_value val = {}; |
| switch (pb_prim.oneof_value_case()) { |
| case pb::Primitive::kNullValue: { |
| val.dataType = android::Res_value::TYPE_NULL; |
| val.data = android::Res_value::DATA_NULL_UNDEFINED; |
| } break; |
| case pb::Primitive::kEmptyValue: { |
| val.dataType = android::Res_value::TYPE_NULL; |
| val.data = android::Res_value::DATA_NULL_EMPTY; |
| } break; |
| case pb::Primitive::kFloatValue: { |
| val.dataType = android::Res_value::TYPE_FLOAT; |
| float float_val = pb_prim.float_value(); |
| val.data = *(uint32_t*)&float_val; |
| } break; |
| case pb::Primitive::kDimensionValue: { |
| val.dataType = android::Res_value::TYPE_DIMENSION; |
| val.data = pb_prim.dimension_value(); |
| } break; |
| case pb::Primitive::kFractionValue: { |
| val.dataType = android::Res_value::TYPE_FRACTION; |
| val.data = pb_prim.fraction_value(); |
| } break; |
| case pb::Primitive::kIntDecimalValue: { |
| val.dataType = android::Res_value::TYPE_INT_DEC; |
| val.data = static_cast<uint32_t>(pb_prim.int_decimal_value()); |
| } break; |
| case pb::Primitive::kIntHexadecimalValue: { |
| val.dataType = android::Res_value::TYPE_INT_HEX; |
| val.data = pb_prim.int_hexadecimal_value(); |
| } break; |
| case pb::Primitive::kBooleanValue: { |
| val.dataType = android::Res_value::TYPE_INT_BOOLEAN; |
| val.data = pb_prim.boolean_value() ? 0xFFFFFFFF : 0x0; |
| } break; |
| case pb::Primitive::kColorArgb8Value: { |
| val.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8; |
| val.data = pb_prim.color_argb8_value(); |
| } break; |
| case pb::Primitive::kColorRgb8Value: { |
| val.dataType = android::Res_value::TYPE_INT_COLOR_RGB8; |
| val.data = pb_prim.color_rgb8_value(); |
| } break; |
| case pb::Primitive::kColorArgb4Value: { |
| val.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4; |
| val.data = pb_prim.color_argb4_value(); |
| } break; |
| case pb::Primitive::kColorRgb4Value: { |
| val.dataType = android::Res_value::TYPE_INT_COLOR_RGB4; |
| val.data = pb_prim.color_rgb4_value(); |
| } break; |
| case pb::Primitive::kDimensionValueDeprecated: { // DEPRECATED |
| val.dataType = android::Res_value::TYPE_DIMENSION; |
| float dimen_val = pb_prim.dimension_value_deprecated(); |
| val.data = *(uint32_t*)&dimen_val; |
| } break; |
| case pb::Primitive::kFractionValueDeprecated: { // DEPRECATED |
| val.dataType = android::Res_value::TYPE_FRACTION; |
| float fraction_val = pb_prim.fraction_value_deprecated(); |
| val.data = *(uint32_t*)&fraction_val; |
| } break; |
| default: { |
| LOG(FATAL) << "Unexpected Primitive type: " |
| << static_cast<uint32_t>(pb_prim.oneof_value_case()); |
| return {}; |
| } break; |
| } |
| return util::make_unique<BinaryPrimitive>(val); |
| } break; |
| |
| case pb::Item::kId: { |
| return util::make_unique<Id>(); |
| } break; |
| |
| case pb::Item::kStr: { |
| return util::make_unique<String>( |
| value_pool->MakeRef(pb_item.str().value(), StringPool::Context(config))); |
| } break; |
| |
| case pb::Item::kRawStr: { |
| return util::make_unique<RawString>( |
| value_pool->MakeRef(pb_item.raw_str().value(), StringPool::Context(config))); |
| } break; |
| |
| case pb::Item::kStyledStr: { |
| const pb::StyledString& pb_str = pb_item.styled_str(); |
| StyleString style_str{pb_str.value()}; |
| for (const pb::StyledString::Span& pb_span : pb_str.span()) { |
| style_str.spans.push_back(Span{pb_span.tag(), pb_span.first_char(), pb_span.last_char()}); |
| } |
| return util::make_unique<StyledString>(value_pool->MakeRef( |
| style_str, StringPool::Context(StringPool::Context::kNormalPriority, config))); |
| } break; |
| |
| case pb::Item::kFile: { |
| const pb::FileReference& pb_file = pb_item.file(); |
| std::unique_ptr<FileReference> file_ref = |
| util::make_unique<FileReference>(value_pool->MakeRef( |
| pb_file.path(), StringPool::Context(StringPool::Context::kHighPriority, config))); |
| file_ref->type = DeserializeFileReferenceTypeFromPb(pb_file.type()); |
| if (files != nullptr) { |
| file_ref->file = files->FindFile(*file_ref->path); |
| } |
| return std::move(file_ref); |
| } break; |
| |
| default: |
| LOG(FATAL) << "unknown item: " << (int)pb_item.value_case(); |
| break; |
| } |
| return {}; |
| } |
| |
| std::unique_ptr<xml::XmlResource> DeserializeXmlResourceFromPb(const pb::XmlNode& pb_node, |
| std::string* out_error) { |
| if (!pb_node.has_element()) { |
| return {}; |
| } |
| |
| std::unique_ptr<xml::XmlResource> resource = util::make_unique<xml::XmlResource>(); |
| resource->root = util::make_unique<xml::Element>(); |
| if (!DeserializeXmlFromPb(pb_node, resource->root.get(), &resource->string_pool, out_error)) { |
| return {}; |
| } |
| return resource; |
| } |
| |
| bool DeserializeXmlFromPb(const pb::XmlNode& pb_node, xml::Element* out_el, StringPool* value_pool, |
| std::string* out_error) { |
| const pb::XmlElement& pb_el = pb_node.element(); |
| out_el->name = pb_el.name(); |
| out_el->namespace_uri = pb_el.namespace_uri(); |
| out_el->line_number = pb_node.source().line_number(); |
| out_el->column_number = pb_node.source().column_number(); |
| |
| for (const pb::XmlNamespace& pb_ns : pb_el.namespace_declaration()) { |
| xml::NamespaceDecl decl; |
| decl.uri = pb_ns.uri(); |
| decl.prefix = pb_ns.prefix(); |
| decl.line_number = pb_ns.source().line_number(); |
| decl.column_number = pb_ns.source().column_number(); |
| out_el->namespace_decls.push_back(std::move(decl)); |
| } |
| |
| for (const pb::XmlAttribute& pb_attr : pb_el.attribute()) { |
| xml::Attribute attr; |
| attr.name = pb_attr.name(); |
| attr.namespace_uri = pb_attr.namespace_uri(); |
| attr.value = pb_attr.value(); |
| if (pb_attr.resource_id() != 0u) { |
| attr.compiled_attribute = xml::AaptAttribute{Attribute(), ResourceId(pb_attr.resource_id())}; |
| } |
| if (pb_attr.has_compiled_item()) { |
| attr.compiled_value = |
| DeserializeItemFromPb(pb_attr.compiled_item(), {}, {}, value_pool, nullptr, out_error); |
| if (attr.compiled_value == nullptr) { |
| return {}; |
| } |
| attr.compiled_value->SetSource(Source().WithLine(pb_attr.source().line_number())); |
| } |
| out_el->attributes.push_back(std::move(attr)); |
| } |
| |
| // Deserialize the children. |
| for (const pb::XmlNode& pb_child : pb_el.child()) { |
| switch (pb_child.node_case()) { |
| case pb::XmlNode::NodeCase::kText: { |
| std::unique_ptr<xml::Text> text = util::make_unique<xml::Text>(); |
| text->line_number = pb_child.source().line_number(); |
| text->column_number = pb_child.source().column_number(); |
| text->text = pb_child.text(); |
| out_el->AppendChild(std::move(text)); |
| } break; |
| |
| case pb::XmlNode::NodeCase::kElement: { |
| std::unique_ptr<xml::Element> child_el = util::make_unique<xml::Element>(); |
| if (!DeserializeXmlFromPb(pb_child, child_el.get(), value_pool, out_error)) { |
| return false; |
| } |
| out_el->AppendChild(std::move(child_el)); |
| } break; |
| |
| default: |
| LOG(FATAL) << "unknown XmlNode " << (int)pb_child.node_case(); |
| break; |
| } |
| } |
| return true; |
| } |
| |
| } // namespace aapt |