| // 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 <gmodule.h> |
| |
| #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<GByteArray*>(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<GByteArray*>(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<GByteArray*>(g_steal_pointer(&buffer))); |
| } |
| |
| // Implements FlMethodCodec::encode_decode_response. |
| 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<const guint8*>(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)); |
| } |