Files
frameworks_base/tools/aapt2/format/proto/ProtoDeserialize.cpp
Clark DuVall e9fedbe223 Fix serializing dynamic references to proto
The is_dynamic bit on references was getting lost when converting to
proto and back. This was preventing bundles from being used as shared
libraries, since layout inflation would fail when it hit a dynamic
reference.

Bug: 146491000
Test: Build a shared library with a layout that has a dynamic reference,
      and attempt to inflate that layout.

Change-Id: Ia0e615670d2ac52f9266e3eec8813af7687d3323
2020-01-13 09:55:53 -08:00

1021 lines
37 KiB
C++

/*
* 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;
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 |= OverlayableItem::Policy::kPublic;
break;
case pb::OverlayableItem::SYSTEM:
out_overlayable->policies |= OverlayableItem::Policy::kSystem;
break;
case pb::OverlayableItem::VENDOR:
out_overlayable->policies |= OverlayableItem::Policy::kVendor;
break;
case pb::OverlayableItem::PRODUCT:
out_overlayable->policies |= OverlayableItem::Policy::kProduct;
break;
case pb::OverlayableItem::SIGNATURE:
out_overlayable->policies |= OverlayableItem::Policy::kSignature;
break;
case pb::OverlayableItem::ODM:
out_overlayable->policies |= OverlayableItem::Policy::kOdm;
break;
case pb::OverlayableItem::OEM:
out_overlayable->policies |= OverlayableItem::Policy::kOem;
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 = type->FindOrCreateEntry(pb_entry.name());
if (pb_entry.has_entry_id()) {
entry->id = static_cast<uint16_t>(pb_entry.entry_id().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();
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",
pb_overlayable_item.overlayable_idx());
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