There are cases where an app can ship overlays for itself, but the "signature" policy as described would open up a vulnerability by allowing the system actor to create and sign any arbitrary overlay that will apply to the target. To prevent this, redefine "signature" as target package only, and introduce "actor" for checking against the actor signature. Any app that wishes to use both can include both policies. Bug: 130563563 Test: m aapt2_tests idmapt2_tests and run from host test output Test: atest libandroidfw_tests Change-Id: I1c583a5b37f4abbeb18fc6a35c502377d8977a41
738 lines
27 KiB
C++
738 lines
27 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/ProtoSerialize.h"
|
|
|
|
#include "ValueVisitor.h"
|
|
#include "util/BigBuffer.h"
|
|
|
|
using android::ConfigDescription;
|
|
|
|
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
|
|
|
|
namespace aapt {
|
|
|
|
void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool, IDiagnostics* diag) {
|
|
BigBuffer buffer(1024);
|
|
StringPool::FlattenUtf8(&buffer, pool, diag);
|
|
|
|
std::string* data = out_pb_pool->mutable_data();
|
|
data->reserve(buffer.size());
|
|
|
|
size_t offset = 0;
|
|
for (const BigBuffer::Block& block : buffer) {
|
|
data->insert(data->begin() + offset, block.buffer.get(), block.buffer.get() + block.size);
|
|
offset += block.size;
|
|
}
|
|
}
|
|
|
|
void SerializeSourceToPb(const Source& source, StringPool* src_pool, pb::Source* out_pb_source) {
|
|
StringPool::Ref ref = src_pool->MakeRef(source.path);
|
|
out_pb_source->set_path_idx(static_cast<uint32_t>(ref.index()));
|
|
if (source.line) {
|
|
out_pb_source->mutable_position()->set_line_number(static_cast<uint32_t>(source.line.value()));
|
|
}
|
|
}
|
|
|
|
static pb::Visibility::Level SerializeVisibilityToPb(Visibility::Level state) {
|
|
switch (state) {
|
|
case Visibility::Level::kPrivate:
|
|
return pb::Visibility::PRIVATE;
|
|
case Visibility::Level::kPublic:
|
|
return pb::Visibility::PUBLIC;
|
|
default:
|
|
break;
|
|
}
|
|
return pb::Visibility::UNKNOWN;
|
|
}
|
|
|
|
void SerializeConfig(const ConfigDescription& config, pb::Configuration* out_pb_config) {
|
|
out_pb_config->set_mcc(config.mcc);
|
|
out_pb_config->set_mnc(config.mnc);
|
|
out_pb_config->set_locale(config.GetBcp47LanguageTag());
|
|
|
|
switch (config.screenLayout & ConfigDescription::MASK_LAYOUTDIR) {
|
|
case ConfigDescription::LAYOUTDIR_LTR:
|
|
out_pb_config->set_layout_direction(pb::Configuration_LayoutDirection_LAYOUT_DIRECTION_LTR);
|
|
break;
|
|
|
|
case ConfigDescription::LAYOUTDIR_RTL:
|
|
out_pb_config->set_layout_direction(pb::Configuration_LayoutDirection_LAYOUT_DIRECTION_RTL);
|
|
break;
|
|
}
|
|
|
|
out_pb_config->set_screen_width(config.screenWidth);
|
|
out_pb_config->set_screen_height(config.screenHeight);
|
|
out_pb_config->set_screen_width_dp(config.screenWidthDp);
|
|
out_pb_config->set_screen_height_dp(config.screenHeightDp);
|
|
out_pb_config->set_smallest_screen_width_dp(config.smallestScreenWidthDp);
|
|
|
|
switch (config.screenLayout & ConfigDescription::MASK_SCREENSIZE) {
|
|
case ConfigDescription::SCREENSIZE_SMALL:
|
|
out_pb_config->set_screen_layout_size(
|
|
pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_SMALL);
|
|
break;
|
|
|
|
case ConfigDescription::SCREENSIZE_NORMAL:
|
|
out_pb_config->set_screen_layout_size(
|
|
pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_NORMAL);
|
|
break;
|
|
|
|
case ConfigDescription::SCREENSIZE_LARGE:
|
|
out_pb_config->set_screen_layout_size(
|
|
pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_LARGE);
|
|
break;
|
|
|
|
case ConfigDescription::SCREENSIZE_XLARGE:
|
|
out_pb_config->set_screen_layout_size(
|
|
pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_XLARGE);
|
|
break;
|
|
}
|
|
|
|
switch (config.screenLayout & ConfigDescription::MASK_SCREENLONG) {
|
|
case ConfigDescription::SCREENLONG_YES:
|
|
out_pb_config->set_screen_layout_long(
|
|
pb::Configuration_ScreenLayoutLong_SCREEN_LAYOUT_LONG_LONG);
|
|
break;
|
|
|
|
case ConfigDescription::SCREENLONG_NO:
|
|
out_pb_config->set_screen_layout_long(
|
|
pb::Configuration_ScreenLayoutLong_SCREEN_LAYOUT_LONG_NOTLONG);
|
|
break;
|
|
}
|
|
|
|
switch (config.screenLayout2 & ConfigDescription::MASK_SCREENROUND) {
|
|
case ConfigDescription::SCREENROUND_YES:
|
|
out_pb_config->set_screen_round(pb::Configuration_ScreenRound_SCREEN_ROUND_ROUND);
|
|
break;
|
|
|
|
case ConfigDescription::SCREENROUND_NO:
|
|
out_pb_config->set_screen_round(pb::Configuration_ScreenRound_SCREEN_ROUND_NOTROUND);
|
|
break;
|
|
}
|
|
|
|
switch (config.colorMode & ConfigDescription::MASK_WIDE_COLOR_GAMUT) {
|
|
case ConfigDescription::WIDE_COLOR_GAMUT_YES:
|
|
out_pb_config->set_wide_color_gamut(pb::Configuration_WideColorGamut_WIDE_COLOR_GAMUT_WIDECG);
|
|
break;
|
|
|
|
case ConfigDescription::WIDE_COLOR_GAMUT_NO:
|
|
out_pb_config->set_wide_color_gamut(
|
|
pb::Configuration_WideColorGamut_WIDE_COLOR_GAMUT_NOWIDECG);
|
|
break;
|
|
}
|
|
|
|
switch (config.colorMode & ConfigDescription::MASK_HDR) {
|
|
case ConfigDescription::HDR_YES:
|
|
out_pb_config->set_hdr(pb::Configuration_Hdr_HDR_HIGHDR);
|
|
break;
|
|
|
|
case ConfigDescription::HDR_NO:
|
|
out_pb_config->set_hdr(pb::Configuration_Hdr_HDR_LOWDR);
|
|
break;
|
|
}
|
|
|
|
switch (config.orientation) {
|
|
case ConfigDescription::ORIENTATION_PORT:
|
|
out_pb_config->set_orientation(pb::Configuration_Orientation_ORIENTATION_PORT);
|
|
break;
|
|
|
|
case ConfigDescription::ORIENTATION_LAND:
|
|
out_pb_config->set_orientation(pb::Configuration_Orientation_ORIENTATION_LAND);
|
|
break;
|
|
|
|
case ConfigDescription::ORIENTATION_SQUARE:
|
|
out_pb_config->set_orientation(pb::Configuration_Orientation_ORIENTATION_SQUARE);
|
|
break;
|
|
}
|
|
|
|
switch (config.uiMode & ConfigDescription::MASK_UI_MODE_TYPE) {
|
|
case ConfigDescription::UI_MODE_TYPE_NORMAL:
|
|
out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_NORMAL);
|
|
break;
|
|
|
|
case ConfigDescription::UI_MODE_TYPE_DESK:
|
|
out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_DESK);
|
|
break;
|
|
|
|
case ConfigDescription::UI_MODE_TYPE_CAR:
|
|
out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_CAR);
|
|
break;
|
|
|
|
case ConfigDescription::UI_MODE_TYPE_TELEVISION:
|
|
out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_TELEVISION);
|
|
break;
|
|
|
|
case ConfigDescription::UI_MODE_TYPE_APPLIANCE:
|
|
out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_APPLIANCE);
|
|
break;
|
|
|
|
case ConfigDescription::UI_MODE_TYPE_WATCH:
|
|
out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_WATCH);
|
|
break;
|
|
|
|
case ConfigDescription::UI_MODE_TYPE_VR_HEADSET:
|
|
out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_VRHEADSET);
|
|
break;
|
|
}
|
|
|
|
switch (config.uiMode & ConfigDescription::MASK_UI_MODE_NIGHT) {
|
|
case ConfigDescription::UI_MODE_NIGHT_YES:
|
|
out_pb_config->set_ui_mode_night(pb::Configuration_UiModeNight_UI_MODE_NIGHT_NIGHT);
|
|
break;
|
|
|
|
case ConfigDescription::UI_MODE_NIGHT_NO:
|
|
out_pb_config->set_ui_mode_night(pb::Configuration_UiModeNight_UI_MODE_NIGHT_NOTNIGHT);
|
|
break;
|
|
}
|
|
|
|
out_pb_config->set_density(config.density);
|
|
|
|
switch (config.touchscreen) {
|
|
case ConfigDescription::TOUCHSCREEN_NOTOUCH:
|
|
out_pb_config->set_touchscreen(pb::Configuration_Touchscreen_TOUCHSCREEN_NOTOUCH);
|
|
break;
|
|
|
|
case ConfigDescription::TOUCHSCREEN_STYLUS:
|
|
out_pb_config->set_touchscreen(pb::Configuration_Touchscreen_TOUCHSCREEN_STYLUS);
|
|
break;
|
|
|
|
case ConfigDescription::TOUCHSCREEN_FINGER:
|
|
out_pb_config->set_touchscreen(pb::Configuration_Touchscreen_TOUCHSCREEN_FINGER);
|
|
break;
|
|
}
|
|
|
|
switch (config.inputFlags & ConfigDescription::MASK_KEYSHIDDEN) {
|
|
case ConfigDescription::KEYSHIDDEN_NO:
|
|
out_pb_config->set_keys_hidden(pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSEXPOSED);
|
|
break;
|
|
|
|
case ConfigDescription::KEYSHIDDEN_YES:
|
|
out_pb_config->set_keys_hidden(pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSHIDDEN);
|
|
break;
|
|
|
|
case ConfigDescription::KEYSHIDDEN_SOFT:
|
|
out_pb_config->set_keys_hidden(pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSSOFT);
|
|
break;
|
|
}
|
|
|
|
switch (config.keyboard) {
|
|
case ConfigDescription::KEYBOARD_NOKEYS:
|
|
out_pb_config->set_keyboard(pb::Configuration_Keyboard_KEYBOARD_NOKEYS);
|
|
break;
|
|
|
|
case ConfigDescription::KEYBOARD_QWERTY:
|
|
out_pb_config->set_keyboard(pb::Configuration_Keyboard_KEYBOARD_QWERTY);
|
|
break;
|
|
|
|
case ConfigDescription::KEYBOARD_12KEY:
|
|
out_pb_config->set_keyboard(pb::Configuration_Keyboard_KEYBOARD_TWELVEKEY);
|
|
break;
|
|
}
|
|
|
|
switch (config.inputFlags & ConfigDescription::MASK_NAVHIDDEN) {
|
|
case ConfigDescription::NAVHIDDEN_NO:
|
|
out_pb_config->set_nav_hidden(pb::Configuration_NavHidden_NAV_HIDDEN_NAVEXPOSED);
|
|
break;
|
|
|
|
case ConfigDescription::NAVHIDDEN_YES:
|
|
out_pb_config->set_nav_hidden(pb::Configuration_NavHidden_NAV_HIDDEN_NAVHIDDEN);
|
|
break;
|
|
}
|
|
|
|
switch (config.navigation) {
|
|
case ConfigDescription::NAVIGATION_NONAV:
|
|
out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_NONAV);
|
|
break;
|
|
|
|
case ConfigDescription::NAVIGATION_DPAD:
|
|
out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_DPAD);
|
|
break;
|
|
|
|
case ConfigDescription::NAVIGATION_TRACKBALL:
|
|
out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_TRACKBALL);
|
|
break;
|
|
|
|
case ConfigDescription::NAVIGATION_WHEEL:
|
|
out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_WHEEL);
|
|
break;
|
|
}
|
|
|
|
out_pb_config->set_sdk_version(config.sdkVersion);
|
|
}
|
|
|
|
static void SerializeOverlayableItemToPb(const OverlayableItem& overlayable_item,
|
|
std::vector<Overlayable*>& serialized_overlayables,
|
|
StringPool* source_pool, pb::Entry* pb_entry,
|
|
pb::ResourceTable* pb_table) {
|
|
// Retrieve the index of the overlayable in the list of groups that have already been serialized.
|
|
size_t i;
|
|
for (i = 0 ; i < serialized_overlayables.size(); i++) {
|
|
if (overlayable_item.overlayable.get() == serialized_overlayables[i]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Serialize the overlayable if it has not been serialized already.
|
|
if (i == serialized_overlayables.size()) {
|
|
serialized_overlayables.push_back(overlayable_item.overlayable.get());
|
|
pb::Overlayable* pb_overlayable = pb_table->add_overlayable();
|
|
pb_overlayable->set_name(overlayable_item.overlayable->name);
|
|
pb_overlayable->set_actor(overlayable_item.overlayable->actor);
|
|
if (source_pool != nullptr) {
|
|
SerializeSourceToPb(overlayable_item.overlayable->source, source_pool,
|
|
pb_overlayable->mutable_source());
|
|
}
|
|
}
|
|
|
|
pb::OverlayableItem* pb_overlayable_item = pb_entry->mutable_overlayable_item();
|
|
pb_overlayable_item->set_overlayable_idx(i);
|
|
|
|
if (overlayable_item.policies & PolicyFlags::PUBLIC) {
|
|
pb_overlayable_item->add_policy(pb::OverlayableItem::PUBLIC);
|
|
}
|
|
if (overlayable_item.policies & PolicyFlags::PRODUCT_PARTITION) {
|
|
pb_overlayable_item->add_policy(pb::OverlayableItem::PRODUCT);
|
|
}
|
|
if (overlayable_item.policies & PolicyFlags::SYSTEM_PARTITION) {
|
|
pb_overlayable_item->add_policy(pb::OverlayableItem::SYSTEM);
|
|
}
|
|
if (overlayable_item.policies & PolicyFlags::VENDOR_PARTITION) {
|
|
pb_overlayable_item->add_policy(pb::OverlayableItem::VENDOR);
|
|
}
|
|
if (overlayable_item.policies & PolicyFlags::SIGNATURE) {
|
|
pb_overlayable_item->add_policy(pb::OverlayableItem::SIGNATURE);
|
|
}
|
|
if (overlayable_item.policies & PolicyFlags::ODM_PARTITION) {
|
|
pb_overlayable_item->add_policy(pb::OverlayableItem::ODM);
|
|
}
|
|
if (overlayable_item.policies & PolicyFlags::OEM_PARTITION) {
|
|
pb_overlayable_item->add_policy(pb::OverlayableItem::OEM);
|
|
}
|
|
if (overlayable_item.policies & PolicyFlags::ACTOR_SIGNATURE) {
|
|
pb_overlayable_item->add_policy(pb::OverlayableItem::ACTOR);
|
|
}
|
|
|
|
if (source_pool != nullptr) {
|
|
SerializeSourceToPb(overlayable_item.source, source_pool,
|
|
pb_overlayable_item->mutable_source());
|
|
}
|
|
pb_overlayable_item->set_comment(overlayable_item.comment);
|
|
}
|
|
|
|
void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table,
|
|
IDiagnostics* diag, SerializeTableOptions options) {
|
|
auto source_pool = (options.exclude_sources) ? nullptr : util::make_unique<StringPool>();
|
|
|
|
pb::ToolFingerprint* pb_fingerprint = out_table->add_tool_fingerprint();
|
|
pb_fingerprint->set_tool(util::GetToolName());
|
|
pb_fingerprint->set_version(util::GetToolFingerprint());
|
|
|
|
std::vector<Overlayable*> overlayables;
|
|
for (const std::unique_ptr<ResourceTablePackage>& package : table.packages) {
|
|
pb::Package* pb_package = out_table->add_package();
|
|
if (package->id) {
|
|
pb_package->mutable_package_id()->set_id(package->id.value());
|
|
}
|
|
pb_package->set_package_name(package->name);
|
|
|
|
for (const std::unique_ptr<ResourceTableType>& type : package->types) {
|
|
pb::Type* pb_type = pb_package->add_type();
|
|
if (type->id) {
|
|
pb_type->mutable_type_id()->set_id(type->id.value());
|
|
}
|
|
pb_type->set_name(to_string(type->type).to_string());
|
|
|
|
for (const std::unique_ptr<ResourceEntry>& entry : type->entries) {
|
|
pb::Entry* pb_entry = pb_type->add_entry();
|
|
if (entry->id) {
|
|
pb_entry->mutable_entry_id()->set_id(entry->id.value());
|
|
}
|
|
pb_entry->set_name(entry->name);
|
|
|
|
// Write the Visibility struct.
|
|
pb::Visibility* pb_visibility = pb_entry->mutable_visibility();
|
|
pb_visibility->set_level(SerializeVisibilityToPb(entry->visibility.level));
|
|
if (source_pool != nullptr) {
|
|
SerializeSourceToPb(entry->visibility.source, source_pool.get(),
|
|
pb_visibility->mutable_source());
|
|
}
|
|
pb_visibility->set_comment(entry->visibility.comment);
|
|
|
|
if (entry->allow_new) {
|
|
pb::AllowNew* pb_allow_new = pb_entry->mutable_allow_new();
|
|
if (source_pool != nullptr) {
|
|
SerializeSourceToPb(entry->allow_new.value().source, source_pool.get(),
|
|
pb_allow_new->mutable_source());
|
|
}
|
|
pb_allow_new->set_comment(entry->allow_new.value().comment);
|
|
}
|
|
|
|
if (entry->overlayable_item) {
|
|
SerializeOverlayableItemToPb(entry->overlayable_item.value(), overlayables,
|
|
source_pool.get(), pb_entry, out_table);
|
|
}
|
|
|
|
for (const std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
|
|
pb::ConfigValue* pb_config_value = pb_entry->add_config_value();
|
|
SerializeConfig(config_value->config, pb_config_value->mutable_config());
|
|
pb_config_value->mutable_config()->set_product(config_value->product);
|
|
SerializeValueToPb(*config_value->value, pb_config_value->mutable_value(),
|
|
source_pool.get());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (source_pool != nullptr) {
|
|
SerializeStringPoolToPb(*source_pool, out_table->mutable_source_pool(), diag);
|
|
}
|
|
}
|
|
|
|
static pb::Reference_Type SerializeReferenceTypeToPb(Reference::Type type) {
|
|
switch (type) {
|
|
case Reference::Type::kResource:
|
|
return pb::Reference_Type_REFERENCE;
|
|
case Reference::Type::kAttribute:
|
|
return pb::Reference_Type_ATTRIBUTE;
|
|
default:
|
|
break;
|
|
}
|
|
return pb::Reference_Type_REFERENCE;
|
|
}
|
|
|
|
static void SerializeReferenceToPb(const Reference& ref, pb::Reference* pb_ref) {
|
|
pb_ref->set_id(ref.id.value_or_default(ResourceId(0x0)).id);
|
|
|
|
if (ref.name) {
|
|
pb_ref->set_name(ref.name.value().to_string());
|
|
}
|
|
|
|
pb_ref->set_private_(ref.private_reference);
|
|
pb_ref->set_type(SerializeReferenceTypeToPb(ref.reference_type));
|
|
if (ref.is_dynamic) {
|
|
pb_ref->mutable_is_dynamic()->set_value(ref.is_dynamic);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
static void SerializeItemMetaDataToPb(const Item& item, T* pb_item, StringPool* src_pool) {
|
|
if (src_pool != nullptr) {
|
|
SerializeSourceToPb(item.GetSource(), src_pool, pb_item->mutable_source());
|
|
}
|
|
pb_item->set_comment(item.GetComment());
|
|
}
|
|
|
|
static pb::Plural_Arity SerializePluralEnumToPb(size_t plural_idx) {
|
|
switch (plural_idx) {
|
|
case Plural::Zero:
|
|
return pb::Plural_Arity_ZERO;
|
|
case Plural::One:
|
|
return pb::Plural_Arity_ONE;
|
|
case Plural::Two:
|
|
return pb::Plural_Arity_TWO;
|
|
case Plural::Few:
|
|
return pb::Plural_Arity_FEW;
|
|
case Plural::Many:
|
|
return pb::Plural_Arity_MANY;
|
|
default:
|
|
break;
|
|
}
|
|
return pb::Plural_Arity_OTHER;
|
|
}
|
|
|
|
static pb::FileReference::Type SerializeFileReferenceTypeToPb(const ResourceFile::Type& type) {
|
|
switch (type) {
|
|
case ResourceFile::Type::kBinaryXml:
|
|
return pb::FileReference::BINARY_XML;
|
|
case ResourceFile::Type::kProtoXml:
|
|
return pb::FileReference::PROTO_XML;
|
|
case ResourceFile::Type::kPng:
|
|
return pb::FileReference::PNG;
|
|
default:
|
|
return pb::FileReference::UNKNOWN;
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
class ValueSerializer : public ConstValueVisitor {
|
|
public:
|
|
using ConstValueVisitor::Visit;
|
|
|
|
ValueSerializer(pb::Value* out_value, StringPool* src_pool)
|
|
: out_value_(out_value), src_pool_(src_pool) {
|
|
}
|
|
|
|
void Visit(const Reference* ref) override {
|
|
SerializeReferenceToPb(*ref, out_value_->mutable_item()->mutable_ref());
|
|
}
|
|
|
|
void Visit(const String* str) override {
|
|
out_value_->mutable_item()->mutable_str()->set_value(*str->value);
|
|
}
|
|
|
|
void Visit(const RawString* str) override {
|
|
out_value_->mutable_item()->mutable_raw_str()->set_value(*str->value);
|
|
}
|
|
|
|
void Visit(const StyledString* str) override {
|
|
pb::StyledString* pb_str = out_value_->mutable_item()->mutable_styled_str();
|
|
pb_str->set_value(str->value->value);
|
|
for (const StringPool::Span& span : str->value->spans) {
|
|
pb::StyledString::Span* pb_span = pb_str->add_span();
|
|
pb_span->set_tag(*span.name);
|
|
pb_span->set_first_char(span.first_char);
|
|
pb_span->set_last_char(span.last_char);
|
|
}
|
|
}
|
|
|
|
void Visit(const FileReference* file) override {
|
|
pb::FileReference* pb_file = out_value_->mutable_item()->mutable_file();
|
|
pb_file->set_path(*file->path);
|
|
pb_file->set_type(SerializeFileReferenceTypeToPb(file->type));
|
|
}
|
|
|
|
void Visit(const Id* /*id*/) override {
|
|
out_value_->mutable_item()->mutable_id();
|
|
}
|
|
|
|
void Visit(const BinaryPrimitive* prim) override {
|
|
android::Res_value val = {};
|
|
prim->Flatten(&val);
|
|
|
|
pb::Primitive* pb_prim = out_value_->mutable_item()->mutable_prim();
|
|
|
|
switch (val.dataType) {
|
|
case android::Res_value::TYPE_NULL: {
|
|
if (val.data == android::Res_value::DATA_NULL_UNDEFINED) {
|
|
pb_prim->set_allocated_null_value(new pb::Primitive_NullType());
|
|
} else if (val.data == android::Res_value::DATA_NULL_EMPTY) {
|
|
pb_prim->set_allocated_empty_value(new pb::Primitive_EmptyType());
|
|
} else {
|
|
LOG(FATAL) << "Unexpected data value for TYPE_NULL BinaryPrimitive: " << val.data;
|
|
}
|
|
} break;
|
|
case android::Res_value::TYPE_FLOAT: {
|
|
pb_prim->set_float_value(*(float*)&val.data);
|
|
} break;
|
|
case android::Res_value::TYPE_DIMENSION: {
|
|
pb_prim->set_dimension_value(val.data);
|
|
} break;
|
|
case android::Res_value::TYPE_FRACTION: {
|
|
pb_prim->set_fraction_value(val.data);
|
|
} break;
|
|
case android::Res_value::TYPE_INT_DEC: {
|
|
pb_prim->set_int_decimal_value(static_cast<int32_t>(val.data));
|
|
} break;
|
|
case android::Res_value::TYPE_INT_HEX: {
|
|
pb_prim->set_int_hexadecimal_value(val.data);
|
|
} break;
|
|
case android::Res_value::TYPE_INT_BOOLEAN: {
|
|
pb_prim->set_boolean_value(static_cast<bool>(val.data));
|
|
} break;
|
|
case android::Res_value::TYPE_INT_COLOR_ARGB8: {
|
|
pb_prim->set_color_argb8_value(val.data);
|
|
} break;
|
|
case android::Res_value::TYPE_INT_COLOR_RGB8: {
|
|
pb_prim->set_color_rgb8_value(val.data);
|
|
} break;
|
|
case android::Res_value::TYPE_INT_COLOR_ARGB4: {
|
|
pb_prim->set_color_argb4_value(val.data);
|
|
} break;
|
|
case android::Res_value::TYPE_INT_COLOR_RGB4: {
|
|
pb_prim->set_color_rgb4_value(val.data);
|
|
} break;
|
|
default:
|
|
LOG(FATAL) << "Unexpected BinaryPrimitive type: " << val.dataType;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Visit(const Attribute* attr) override {
|
|
pb::Attribute* pb_attr = out_value_->mutable_compound_value()->mutable_attr();
|
|
pb_attr->set_format_flags(attr->type_mask);
|
|
pb_attr->set_min_int(attr->min_int);
|
|
pb_attr->set_max_int(attr->max_int);
|
|
|
|
for (auto& symbol : attr->symbols) {
|
|
pb::Attribute_Symbol* pb_symbol = pb_attr->add_symbol();
|
|
SerializeItemMetaDataToPb(symbol.symbol, pb_symbol, src_pool_);
|
|
SerializeReferenceToPb(symbol.symbol, pb_symbol->mutable_name());
|
|
pb_symbol->set_value(symbol.value);
|
|
pb_symbol->set_type(symbol.type);
|
|
}
|
|
}
|
|
|
|
void Visit(const Style* style) override {
|
|
pb::Style* pb_style = out_value_->mutable_compound_value()->mutable_style();
|
|
if (style->parent) {
|
|
const Reference& parent = style->parent.value();
|
|
SerializeReferenceToPb(parent, pb_style->mutable_parent());
|
|
if (src_pool_ != nullptr) {
|
|
SerializeSourceToPb(parent.GetSource(), src_pool_, pb_style->mutable_parent_source());
|
|
}
|
|
}
|
|
|
|
for (const Style::Entry& entry : style->entries) {
|
|
pb::Style_Entry* pb_entry = pb_style->add_entry();
|
|
SerializeReferenceToPb(entry.key, pb_entry->mutable_key());
|
|
SerializeItemMetaDataToPb(entry.key, pb_entry, src_pool_);
|
|
SerializeItemToPb(*entry.value, pb_entry->mutable_item());
|
|
}
|
|
}
|
|
|
|
void Visit(const Styleable* styleable) override {
|
|
pb::Styleable* pb_styleable = out_value_->mutable_compound_value()->mutable_styleable();
|
|
for (const Reference& entry : styleable->entries) {
|
|
pb::Styleable_Entry* pb_entry = pb_styleable->add_entry();
|
|
SerializeItemMetaDataToPb(entry, pb_entry, src_pool_);
|
|
SerializeReferenceToPb(entry, pb_entry->mutable_attr());
|
|
}
|
|
}
|
|
|
|
void Visit(const Array* array) override {
|
|
pb::Array* pb_array = out_value_->mutable_compound_value()->mutable_array();
|
|
for (const std::unique_ptr<Item>& element : array->elements) {
|
|
pb::Array_Element* pb_element = pb_array->add_element();
|
|
SerializeItemMetaDataToPb(*element, pb_element, src_pool_);
|
|
SerializeItemToPb(*element, pb_element->mutable_item());
|
|
}
|
|
}
|
|
|
|
void Visit(const Plural* plural) override {
|
|
pb::Plural* pb_plural = out_value_->mutable_compound_value()->mutable_plural();
|
|
const size_t count = plural->values.size();
|
|
for (size_t i = 0; i < count; i++) {
|
|
if (!plural->values[i]) {
|
|
// No plural value set here.
|
|
continue;
|
|
}
|
|
|
|
pb::Plural_Entry* pb_entry = pb_plural->add_entry();
|
|
pb_entry->set_arity(SerializePluralEnumToPb(i));
|
|
SerializeItemMetaDataToPb(*plural->values[i], pb_entry, src_pool_);
|
|
SerializeItemToPb(*plural->values[i], pb_entry->mutable_item());
|
|
}
|
|
}
|
|
|
|
void VisitAny(const Value* unknown) override {
|
|
LOG(FATAL) << "unimplemented value: " << *unknown;
|
|
}
|
|
|
|
private:
|
|
pb::Value* out_value_;
|
|
StringPool* src_pool_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
void SerializeValueToPb(const Value& value, pb::Value* out_value, StringPool* src_pool) {
|
|
ValueSerializer serializer(out_value, src_pool);
|
|
value.Accept(&serializer);
|
|
|
|
// Serialize the meta-data of the Value.
|
|
out_value->set_comment(value.GetComment());
|
|
out_value->set_weak(value.IsWeak());
|
|
if (src_pool != nullptr) {
|
|
SerializeSourceToPb(value.GetSource(), src_pool, out_value->mutable_source());
|
|
}
|
|
}
|
|
|
|
void SerializeItemToPb(const Item& item, pb::Item* out_item) {
|
|
pb::Value value;
|
|
ValueSerializer serializer(&value, nullptr);
|
|
item.Accept(&serializer);
|
|
out_item->MergeFrom(value.item());
|
|
}
|
|
|
|
void SerializeCompiledFileToPb(const ResourceFile& file, pb::internal::CompiledFile* out_file) {
|
|
out_file->set_resource_name(file.name.to_string());
|
|
out_file->set_source_path(file.source.path);
|
|
out_file->set_type(SerializeFileReferenceTypeToPb(file.type));
|
|
SerializeConfig(file.config, out_file->mutable_config());
|
|
|
|
for (const SourcedResourceName& exported : file.exported_symbols) {
|
|
pb::internal::CompiledFile_Symbol* pb_symbol = out_file->add_exported_symbol();
|
|
pb_symbol->set_resource_name(exported.name.to_string());
|
|
pb_symbol->mutable_source()->set_line_number(exported.line);
|
|
}
|
|
}
|
|
|
|
static void SerializeXmlCommon(const xml::Node& node, pb::XmlNode* out_node) {
|
|
pb::SourcePosition* pb_src = out_node->mutable_source();
|
|
pb_src->set_line_number(node.line_number);
|
|
pb_src->set_column_number(node.column_number);
|
|
}
|
|
|
|
void SerializeXmlToPb(const xml::Element& el, pb::XmlNode* out_node,
|
|
const SerializeXmlOptions options) {
|
|
SerializeXmlCommon(el, out_node);
|
|
|
|
pb::XmlElement* pb_element = out_node->mutable_element();
|
|
pb_element->set_name(el.name);
|
|
pb_element->set_namespace_uri(el.namespace_uri);
|
|
|
|
for (const xml::NamespaceDecl& ns : el.namespace_decls) {
|
|
pb::XmlNamespace* pb_ns = pb_element->add_namespace_declaration();
|
|
pb_ns->set_prefix(ns.prefix);
|
|
pb_ns->set_uri(ns.uri);
|
|
pb::SourcePosition* pb_src = pb_ns->mutable_source();
|
|
pb_src->set_line_number(ns.line_number);
|
|
pb_src->set_column_number(ns.column_number);
|
|
}
|
|
|
|
for (const xml::Attribute& attr : el.attributes) {
|
|
pb::XmlAttribute* pb_attr = pb_element->add_attribute();
|
|
pb_attr->set_name(attr.name);
|
|
pb_attr->set_namespace_uri(attr.namespace_uri);
|
|
pb_attr->set_value(attr.value);
|
|
if (attr.compiled_attribute) {
|
|
const ResourceId attr_id = attr.compiled_attribute.value().id.value_or_default({});
|
|
pb_attr->set_resource_id(attr_id.id);
|
|
}
|
|
if (attr.compiled_value != nullptr) {
|
|
SerializeItemToPb(*attr.compiled_value, pb_attr->mutable_compiled_item());
|
|
pb::SourcePosition* pb_src = pb_attr->mutable_source();
|
|
pb_src->set_line_number(attr.compiled_value->GetSource().line.value_or_default(0));
|
|
}
|
|
}
|
|
|
|
for (const std::unique_ptr<xml::Node>& child : el.children) {
|
|
if (const xml::Element* child_el = xml::NodeCast<xml::Element>(child.get())) {
|
|
SerializeXmlToPb(*child_el, pb_element->add_child());
|
|
} else if (const xml::Text* text_el = xml::NodeCast<xml::Text>(child.get())) {
|
|
if (options.remove_empty_text_nodes && util::TrimWhitespace(text_el->text).empty()) {
|
|
// Do not serialize whitespace text nodes if told not to
|
|
continue;
|
|
}
|
|
|
|
pb::XmlNode *pb_child_node = pb_element->add_child();
|
|
SerializeXmlCommon(*text_el, pb_child_node);
|
|
pb_child_node->set_text(text_el->text);
|
|
} else {
|
|
LOG(FATAL) << "unhandled XmlNode type";
|
|
}
|
|
}
|
|
}
|
|
|
|
void SerializeXmlResourceToPb(const xml::XmlResource& resource, pb::XmlNode* out_node,
|
|
const SerializeXmlOptions options) {
|
|
SerializeXmlToPb(*resource.root, out_node, options);
|
|
}
|
|
|
|
} // namespace aapt
|