// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h" #include #include "flutter/shell/platform/linux/fl_standard_message_codec_private.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h" // See lib/src/services/message_codecs.dart in Flutter source for description of // encoding. // Envelope codes. static constexpr guint8 kEnvelopeTypeSuccess = 0; static constexpr guint8 kEnvelopeTypeError = 1; struct _FlStandardMethodCodec { FlMethodCodec parent_instance; FlStandardMessageCodec* codec; }; G_DEFINE_TYPE(FlStandardMethodCodec, fl_standard_method_codec, fl_method_codec_get_type()) static void fl_standard_method_codec_dispose(GObject* object) { FlStandardMethodCodec* self = FL_STANDARD_METHOD_CODEC(object); g_clear_object(&self->codec); G_OBJECT_CLASS(fl_standard_method_codec_parent_class)->dispose(object); } // Implements FlMethodCodec::encode_method_call. static GBytes* fl_standard_method_codec_encode_method_call(FlMethodCodec* codec, const gchar* name, FlValue* args, GError** error) { FlStandardMethodCodec* self = FL_STANDARD_METHOD_CODEC(codec); g_autoptr(GByteArray) buffer = g_byte_array_new(); g_autoptr(FlValue) name_value = fl_value_new_string(name); if (!fl_standard_message_codec_write_value(self->codec, buffer, name_value, error)) { return nullptr; } if (!fl_standard_message_codec_write_value(self->codec, buffer, args, error)) { return nullptr; } return g_byte_array_free_to_bytes( static_cast(g_steal_pointer(&buffer))); } // Implements FlMethodCodec::decode_method_call. static gboolean fl_standard_method_codec_decode_method_call( FlMethodCodec* codec, GBytes* message, gchar** name, FlValue** args, GError** error) { FlStandardMethodCodec* self = FL_STANDARD_METHOD_CODEC(codec); size_t offset = 0; g_autoptr(FlValue) name_value = fl_standard_message_codec_read_value( self->codec, message, &offset, error); if (name_value == nullptr) { return FALSE; } if (fl_value_get_type(name_value) != FL_VALUE_TYPE_STRING) { g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, "Method call name wrong type"); return FALSE; } g_autoptr(FlValue) args_value = fl_standard_message_codec_read_value( self->codec, message, &offset, error); if (args_value == nullptr) { return FALSE; } if (offset != g_bytes_get_size(message)) { g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, "Unexpected extra data"); return FALSE; } *name = g_strdup(fl_value_get_string(name_value)); *args = fl_value_ref(args_value); return TRUE; } // Implements FlMethodCodec::encode_success_envelope. static GBytes* fl_standard_method_codec_encode_success_envelope( FlMethodCodec* codec, FlValue* result, GError** error) { FlStandardMethodCodec* self = FL_STANDARD_METHOD_CODEC(codec); g_autoptr(GByteArray) buffer = g_byte_array_new(); guint8 type = kEnvelopeTypeSuccess; g_byte_array_append(buffer, &type, 1); if (!fl_standard_message_codec_write_value(self->codec, buffer, result, error)) { return nullptr; } return g_byte_array_free_to_bytes( static_cast(g_steal_pointer(&buffer))); } // Implements FlMethodCodec::encode_error_envelope. static GBytes* fl_standard_method_codec_encode_error_envelope( FlMethodCodec* codec, const gchar* code, const gchar* message, FlValue* details, GError** error) { FlStandardMethodCodec* self = FL_STANDARD_METHOD_CODEC(codec); g_autoptr(GByteArray) buffer = g_byte_array_new(); guint8 type = kEnvelopeTypeError; g_byte_array_append(buffer, &type, 1); g_autoptr(FlValue) code_value = fl_value_new_string(code); if (!fl_standard_message_codec_write_value(self->codec, buffer, code_value, error)) { return nullptr; } g_autoptr(FlValue) message_value = message != nullptr ? fl_value_new_string(message) : nullptr; if (!fl_standard_message_codec_write_value(self->codec, buffer, message_value, error)) { return nullptr; } if (!fl_standard_message_codec_write_value(self->codec, buffer, details, error)) { return nullptr; } return g_byte_array_free_to_bytes( static_cast(g_steal_pointer(&buffer))); } // Implements FlMethodCodec::encode_decode_reponse. static FlMethodResponse* fl_standard_method_codec_decode_response( FlMethodCodec* codec, GBytes* message, GError** error) { FlStandardMethodCodec* self = FL_STANDARD_METHOD_CODEC(codec); if (g_bytes_get_size(message) == 0) { g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA, "Empty response"); return nullptr; } // First byte is response type. const guint8* data = static_cast(g_bytes_get_data(message, nullptr)); guint8 type = data[0]; size_t offset = 1; g_autoptr(FlMethodResponse) response = nullptr; if (type == kEnvelopeTypeError) { g_autoptr(FlValue) code = fl_standard_message_codec_read_value( self->codec, message, &offset, error); if (code == nullptr) { return nullptr; } if (fl_value_get_type(code) != FL_VALUE_TYPE_STRING) { g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, "Error code wrong type"); return nullptr; } g_autoptr(FlValue) error_message = fl_standard_message_codec_read_value( self->codec, message, &offset, error); if (error_message == nullptr) { return nullptr; } if (fl_value_get_type(error_message) != FL_VALUE_TYPE_STRING && fl_value_get_type(error_message) != FL_VALUE_TYPE_NULL) { g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, "Error message wrong type"); return nullptr; } g_autoptr(FlValue) details = fl_standard_message_codec_read_value( self->codec, message, &offset, error); if (details == nullptr) { return nullptr; } response = FL_METHOD_RESPONSE(fl_method_error_response_new( fl_value_get_string(code), fl_value_get_type(error_message) == FL_VALUE_TYPE_STRING ? fl_value_get_string(error_message) : nullptr, fl_value_get_type(details) != FL_VALUE_TYPE_NULL ? details : nullptr)); } else if (type == kEnvelopeTypeSuccess) { g_autoptr(FlValue) result = fl_standard_message_codec_read_value( self->codec, message, &offset, error); if (result == nullptr) { return nullptr; } response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); } else { g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, "Unknown envelope type %02x", type); return nullptr; } if (offset != g_bytes_get_size(message)) { g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, "Unexpected extra data"); return nullptr; } return FL_METHOD_RESPONSE(g_object_ref(response)); } static void fl_standard_method_codec_class_init( FlStandardMethodCodecClass* klass) { G_OBJECT_CLASS(klass)->dispose = fl_standard_method_codec_dispose; FL_METHOD_CODEC_CLASS(klass)->encode_method_call = fl_standard_method_codec_encode_method_call; FL_METHOD_CODEC_CLASS(klass)->decode_method_call = fl_standard_method_codec_decode_method_call; FL_METHOD_CODEC_CLASS(klass)->encode_success_envelope = fl_standard_method_codec_encode_success_envelope; FL_METHOD_CODEC_CLASS(klass)->encode_error_envelope = fl_standard_method_codec_encode_error_envelope; FL_METHOD_CODEC_CLASS(klass)->decode_response = fl_standard_method_codec_decode_response; } static void fl_standard_method_codec_init(FlStandardMethodCodec* self) { self->codec = fl_standard_message_codec_new(); } G_MODULE_EXPORT FlStandardMethodCodec* fl_standard_method_codec_new() { return FL_STANDARD_METHOD_CODEC( g_object_new(fl_standard_method_codec_get_type(), nullptr)); }