Add FlMethodChannel, FlMethodCodec, FlStandardMethodCodec and FlJsonMethodCodec (#18220)
* Add FlMethodChannel, FlMethodCall, FlMethodResponse and FlMethodCodec
* Add FlJsonMethodCodec
* Add FlStandardMethodCodec
diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index 7ed3bbe..ef5efd6 100755
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -1200,8 +1200,19 @@
FILE: ../../../flutter/shell/platform/linux/fl_engine_private.h
FILE: ../../../flutter/shell/platform/linux/fl_json_message_codec.cc
FILE: ../../../flutter/shell/platform/linux/fl_json_message_codec_test.cc
+FILE: ../../../flutter/shell/platform/linux/fl_json_method_codec.cc
+FILE: ../../../flutter/shell/platform/linux/fl_json_method_codec_test.cc
FILE: ../../../flutter/shell/platform/linux/fl_message_codec.cc
FILE: ../../../flutter/shell/platform/linux/fl_message_codec_test.cc
+FILE: ../../../flutter/shell/platform/linux/fl_method_call.cc
+FILE: ../../../flutter/shell/platform/linux/fl_method_call_private.h
+FILE: ../../../flutter/shell/platform/linux/fl_method_channel.cc
+FILE: ../../../flutter/shell/platform/linux/fl_method_channel_private.h
+FILE: ../../../flutter/shell/platform/linux/fl_method_codec.cc
+FILE: ../../../flutter/shell/platform/linux/fl_method_codec_private.h
+FILE: ../../../flutter/shell/platform/linux/fl_method_codec_test.cc
+FILE: ../../../flutter/shell/platform/linux/fl_method_response.cc
+FILE: ../../../flutter/shell/platform/linux/fl_method_response_test.cc
FILE: ../../../flutter/shell/platform/linux/fl_renderer.cc
FILE: ../../../flutter/shell/platform/linux/fl_renderer.h
FILE: ../../../flutter/shell/platform/linux/fl_renderer_x11.cc
@@ -1209,6 +1220,8 @@
FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec.cc
FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec_private.h
FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec_test.cc
+FILE: ../../../flutter/shell/platform/linux/fl_standard_method_codec.cc
+FILE: ../../../flutter/shell/platform/linux/fl_standard_method_codec_test.cc
FILE: ../../../flutter/shell/platform/linux/fl_string_codec.cc
FILE: ../../../flutter/shell/platform/linux/fl_string_codec_test.cc
FILE: ../../../flutter/shell/platform/linux/fl_value.cc
@@ -1220,8 +1233,14 @@
FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h
FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_engine.h
FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h
+FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h
FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_message_codec.h
+FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_method_call.h
+FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h
+FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_method_codec.h
+FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_method_response.h
FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h
+FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h
FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_string_codec.h
FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_value.h
FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_view.h
diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn
index aa2c35a..d32c572 100644
--- a/shell/platform/linux/BUILD.gn
+++ b/shell/platform/linux/BUILD.gn
@@ -50,8 +50,14 @@
"public/flutter_linux/fl_dart_project.h",
"public/flutter_linux/fl_engine.h",
"public/flutter_linux/fl_json_message_codec.h",
+ "public/flutter_linux/fl_json_method_codec.h",
"public/flutter_linux/fl_message_codec.h",
+ "public/flutter_linux/fl_method_call.h",
+ "public/flutter_linux/fl_method_channel.h",
+ "public/flutter_linux/fl_method_codec.h",
+ "public/flutter_linux/fl_method_response.h",
"public/flutter_linux/fl_standard_message_codec.h",
+ "public/flutter_linux/fl_standard_method_codec.h",
"public/flutter_linux/fl_string_codec.h",
"public/flutter_linux/fl_value.h",
"public/flutter_linux/fl_view.h",
@@ -72,10 +78,16 @@
"fl_dart_project.cc",
"fl_engine.cc",
"fl_json_message_codec.cc",
+ "fl_json_method_codec.cc",
"fl_message_codec.cc",
+ "fl_method_call.cc",
+ "fl_method_channel.cc",
+ "fl_method_codec.cc",
+ "fl_method_response.cc",
"fl_renderer.cc",
"fl_renderer_x11.cc",
"fl_standard_message_codec.cc",
+ "fl_standard_method_codec.cc",
"fl_string_codec.cc",
"fl_value.cc",
"fl_view.cc",
@@ -107,8 +119,12 @@
"fl_binary_codec_test.cc",
"fl_dart_project_test.cc",
"fl_json_message_codec_test.cc",
+ "fl_json_method_codec_test.cc",
"fl_message_codec_test.cc",
+ "fl_method_codec_test.cc",
+ "fl_method_response_test.cc",
"fl_standard_message_codec_test.cc",
+ "fl_standard_method_codec_test.cc",
"fl_string_codec_test.cc",
"fl_value_test.cc",
"testing/fl_test.cc",
diff --git a/shell/platform/linux/fl_json_method_codec.cc b/shell/platform/linux/fl_json_method_codec.cc
new file mode 100644
index 0000000..0a0cd73
--- /dev/null
+++ b/shell/platform/linux/fl_json_method_codec.cc
@@ -0,0 +1,205 @@
+// 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_json_method_codec.h"
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h"
+
+#include <gmodule.h>
+
+static constexpr char kMethodKey[] = "method";
+static constexpr char kArgsKey[] = "args";
+
+struct _FlJsonMethodCodec {
+ FlMethodCodec parent_instance;
+
+ FlJsonMessageCodec* codec;
+};
+
+G_DEFINE_TYPE(FlJsonMethodCodec,
+ fl_json_method_codec,
+ fl_method_codec_get_type())
+
+static void fl_json_method_codec_dispose(GObject* object) {
+ FlJsonMethodCodec* self = FL_JSON_METHOD_CODEC(object);
+
+ g_clear_object(&self->codec);
+
+ G_OBJECT_CLASS(fl_json_method_codec_parent_class)->dispose(object);
+}
+
+// Implements FlMethodCodec::encode_method_call
+static GBytes* fl_json_method_codec_encode_method_call(FlMethodCodec* codec,
+ const gchar* name,
+ FlValue* args,
+ GError** error) {
+ FlJsonMethodCodec* self = FL_JSON_METHOD_CODEC(codec);
+
+ g_autoptr(FlValue) message = fl_value_new_map();
+ fl_value_set_take(message, fl_value_new_string(kMethodKey),
+ fl_value_new_string(name));
+ fl_value_set_take(message, fl_value_new_string(kArgsKey),
+ args != nullptr ? fl_value_ref(args) : fl_value_new_null());
+
+ return fl_message_codec_encode_message(FL_MESSAGE_CODEC(self->codec), message,
+ error);
+}
+
+// Implements FlMethodCodec::decode_method_call
+static gboolean fl_json_method_codec_decode_method_call(FlMethodCodec* codec,
+ GBytes* message,
+ gchar** name,
+ FlValue** args,
+ GError** error) {
+ FlJsonMethodCodec* self = FL_JSON_METHOD_CODEC(codec);
+
+ g_autoptr(FlValue) value = fl_message_codec_decode_message(
+ FL_MESSAGE_CODEC(self->codec), message, error);
+ if (value == nullptr)
+ return FALSE;
+
+ if (fl_value_get_type(value) != FL_VALUE_TYPE_MAP) {
+ g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+ "Expected JSON map in method resonse, got %d instead",
+ fl_value_get_type(value));
+ return FALSE;
+ }
+
+ FlValue* method_value = fl_value_lookup_string(value, kMethodKey);
+ if (method_value == nullptr) {
+ g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+ "Missing JSON method field in method resonse");
+ return FALSE;
+ }
+ if (fl_value_get_type(method_value) != FL_VALUE_TYPE_STRING) {
+ g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+ "Expected JSON string for method name, got %d instead",
+ fl_value_get_type(method_value));
+ return FALSE;
+ }
+ FlValue* args_value = fl_value_lookup_string(value, kArgsKey);
+
+ *name = g_strdup(fl_value_get_string(method_value));
+ *args = args_value != nullptr ? fl_value_ref(args_value) : nullptr;
+
+ return TRUE;
+}
+
+// Implements FlMethodCodec::encode_success_envelope
+static GBytes* fl_json_method_codec_encode_success_envelope(
+ FlMethodCodec* codec,
+ FlValue* result,
+ GError** error) {
+ FlJsonMethodCodec* self = FL_JSON_METHOD_CODEC(codec);
+
+ g_autoptr(FlValue) message = fl_value_new_list();
+ fl_value_append_take(
+ message, result != nullptr ? fl_value_ref(result) : fl_value_new_null());
+
+ return fl_message_codec_encode_message(FL_MESSAGE_CODEC(self->codec), message,
+ error);
+}
+
+// Implements FlMethodCodec::encode_error_envelope
+static GBytes* fl_json_method_codec_encode_error_envelope(
+ FlMethodCodec* codec,
+ const gchar* code,
+ const gchar* error_message,
+ FlValue* details,
+ GError** error) {
+ FlJsonMethodCodec* self = FL_JSON_METHOD_CODEC(codec);
+
+ g_autoptr(FlValue) message = fl_value_new_list();
+ fl_value_append_take(message, fl_value_new_string(code));
+ fl_value_append_take(message, error_message != nullptr
+ ? fl_value_new_string(error_message)
+ : fl_value_new_null());
+ fl_value_append_take(message, details != nullptr ? fl_value_ref(details)
+ : fl_value_new_null());
+
+ return fl_message_codec_encode_message(FL_MESSAGE_CODEC(self->codec), message,
+ error);
+}
+
+// Implements FlMethodCodec::decode_response
+static FlMethodResponse* fl_json_method_codec_decode_response(
+ FlMethodCodec* codec,
+ GBytes* message,
+ GError** error) {
+ FlJsonMethodCodec* self = FL_JSON_METHOD_CODEC(codec);
+
+ g_autoptr(FlValue) value = fl_message_codec_decode_message(
+ FL_MESSAGE_CODEC(self->codec), message, error);
+ if (value == nullptr)
+ return nullptr;
+
+ if (fl_value_get_type(value) != FL_VALUE_TYPE_LIST) {
+ g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+ "Expected JSON list in method resonse, got %d instead",
+ fl_value_get_type(value));
+ return nullptr;
+ }
+
+ size_t length = fl_value_get_length(value);
+ if (length == 1) {
+ return FL_METHOD_RESPONSE(
+ fl_method_success_response_new(fl_value_get_list_value(value, 0)));
+ } else if (length == 3) {
+ FlValue* code_value = fl_value_get_list_value(value, 0);
+ if (fl_value_get_type(code_value) != FL_VALUE_TYPE_STRING) {
+ g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+ "Error code wrong type");
+ return nullptr;
+ }
+ const gchar* code = fl_value_get_string(code_value);
+
+ FlValue* message_value = fl_value_get_list_value(value, 1);
+ if (fl_value_get_type(message_value) != FL_VALUE_TYPE_STRING &&
+ fl_value_get_type(message_value) != FL_VALUE_TYPE_NULL) {
+ g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+ "Error message wrong type");
+ return nullptr;
+ }
+ const gchar* message =
+ fl_value_get_type(message_value) == FL_VALUE_TYPE_STRING
+ ? fl_value_get_string(message_value)
+ : nullptr;
+
+ FlValue* args = fl_value_get_list_value(value, 2);
+ if (fl_value_get_type(args) == FL_VALUE_TYPE_NULL)
+ args = nullptr;
+
+ return FL_METHOD_RESPONSE(
+ fl_method_error_response_new(code, message, args));
+ } else {
+ g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+ "Got response envelope of length %zi, expected 1 (success) or "
+ "3 (error)",
+ length);
+ return nullptr;
+ }
+}
+
+static void fl_json_method_codec_class_init(FlJsonMethodCodecClass* klass) {
+ G_OBJECT_CLASS(klass)->dispose = fl_json_method_codec_dispose;
+ FL_METHOD_CODEC_CLASS(klass)->encode_method_call =
+ fl_json_method_codec_encode_method_call;
+ FL_METHOD_CODEC_CLASS(klass)->decode_method_call =
+ fl_json_method_codec_decode_method_call;
+ FL_METHOD_CODEC_CLASS(klass)->encode_success_envelope =
+ fl_json_method_codec_encode_success_envelope;
+ FL_METHOD_CODEC_CLASS(klass)->encode_error_envelope =
+ fl_json_method_codec_encode_error_envelope;
+ FL_METHOD_CODEC_CLASS(klass)->decode_response =
+ fl_json_method_codec_decode_response;
+}
+
+static void fl_json_method_codec_init(FlJsonMethodCodec* self) {
+ self->codec = fl_json_message_codec_new();
+}
+
+G_MODULE_EXPORT FlJsonMethodCodec* fl_json_method_codec_new() {
+ return static_cast<FlJsonMethodCodec*>(
+ g_object_new(fl_json_method_codec_get_type(), nullptr));
+}
diff --git a/shell/platform/linux/fl_json_method_codec_test.cc b/shell/platform/linux/fl_json_method_codec_test.cc
new file mode 100644
index 0000000..c994500
--- /dev/null
+++ b/shell/platform/linux/fl_json_method_codec_test.cc
@@ -0,0 +1,398 @@
+// 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_json_method_codec.h"
+#include "flutter/shell/platform/linux/fl_method_codec_private.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_message_codec.h"
+#include "gtest/gtest.h"
+
+// Converts a binary blob to a string.
+static gchar* message_to_text(GBytes* message) {
+ size_t data_length;
+ const gchar* data =
+ static_cast<const gchar*>(g_bytes_get_data(message, &data_length));
+ return g_strndup(data, data_length);
+}
+
+// Converts a string to a binary blob.
+static GBytes* text_to_message(const gchar* text) {
+ return g_bytes_new(text, strlen(text));
+}
+
+// Encodes a method call using JsonMethodCodec to a UTF-8 string.
+static gchar* encode_method_call(const gchar* name, FlValue* args) {
+ g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
+ FL_METHOD_CODEC(codec), name, args, &error);
+ EXPECT_NE(message, nullptr);
+ EXPECT_EQ(error, nullptr);
+
+ return message_to_text(message);
+}
+
+// Encodes a success envelope response using JsonMethodCodec to a UTF-8 string.
+static gchar* encode_success_envelope(FlValue* result) {
+ g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(GBytes) message = fl_method_codec_encode_success_envelope(
+ FL_METHOD_CODEC(codec), result, &error);
+ EXPECT_NE(message, nullptr);
+ EXPECT_EQ(error, nullptr);
+
+ return message_to_text(message);
+}
+
+// Encodes a error envelope response using JsonMethodCodec to a UTF8 string.
+static gchar* encode_error_envelope(const gchar* error_code,
+ const gchar* error_message,
+ FlValue* details) {
+ g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(GBytes) message = fl_method_codec_encode_error_envelope(
+ FL_METHOD_CODEC(codec), error_code, error_message, details, &error);
+ EXPECT_NE(message, nullptr);
+ EXPECT_EQ(error, nullptr);
+
+ return message_to_text(message);
+}
+
+// Decodes a method call using JsonMethodCodec with a UTF8 string.
+static void decode_method_call(const char* text, gchar** name, FlValue** args) {
+ g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
+ g_autoptr(GBytes) data = text_to_message(text);
+ g_autoptr(GError) error = nullptr;
+ gboolean result = fl_method_codec_decode_method_call(
+ FL_METHOD_CODEC(codec), data, name, args, &error);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(error, nullptr);
+}
+
+// Decodes a method call using JsonMethodCodec. Expect the given error.
+static void decode_error_method_call(const char* text,
+ GQuark domain,
+ gint code) {
+ g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
+ g_autoptr(GBytes) data = text_to_message(text);
+ g_autoptr(GError) error = nullptr;
+ g_autofree gchar* name = nullptr;
+ g_autoptr(FlValue) args = nullptr;
+ gboolean result = fl_method_codec_decode_method_call(
+ FL_METHOD_CODEC(codec), data, &name, &args, &error);
+ EXPECT_FALSE(result);
+ EXPECT_EQ(name, nullptr);
+ EXPECT_EQ(args, nullptr);
+ EXPECT_TRUE(g_error_matches(error, domain, code));
+}
+
+// Decodes a response using JsonMethodCodec. Expect the response is a result.
+static void decode_response_with_success(const char* text, FlValue* result) {
+ g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
+ g_autoptr(GBytes) message = text_to_message(text);
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(FlMethodResponse) response =
+ fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+ ASSERT_NE(response, nullptr);
+ EXPECT_EQ(error, nullptr);
+ ASSERT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
+ EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result(
+ FL_METHOD_SUCCESS_RESPONSE(response)),
+ result));
+}
+
+// Decodes a response using JsonMethodCodec. Expect the response contains the
+// given error.
+static void decode_response_with_error(const char* text,
+ const gchar* code,
+ const gchar* error_message,
+ FlValue* details) {
+ g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
+ g_autoptr(GBytes) message = text_to_message(text);
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(FlMethodResponse) response =
+ fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+ ASSERT_NE(response, nullptr);
+ EXPECT_EQ(error, nullptr);
+ ASSERT_TRUE(FL_IS_METHOD_ERROR_RESPONSE(response));
+ EXPECT_STREQ(
+ fl_method_error_response_get_code(FL_METHOD_ERROR_RESPONSE(response)),
+ code);
+ if (error_message == nullptr)
+ EXPECT_EQ(fl_method_error_response_get_message(
+ FL_METHOD_ERROR_RESPONSE(response)),
+ nullptr);
+ else
+ EXPECT_STREQ(fl_method_error_response_get_message(
+ FL_METHOD_ERROR_RESPONSE(response)),
+ error_message);
+ if (details == nullptr)
+ EXPECT_EQ(fl_method_error_response_get_details(
+ FL_METHOD_ERROR_RESPONSE(response)),
+ nullptr);
+ else
+ EXPECT_TRUE(fl_value_equal(fl_method_error_response_get_details(
+ FL_METHOD_ERROR_RESPONSE(response)),
+ details));
+}
+
+// Decode a response using JsonMethodCodec. Expect the given error.
+static void decode_error_response(const char* text, GQuark domain, gint code) {
+ g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
+ g_autoptr(GBytes) message = text_to_message(text);
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(FlMethodResponse) response =
+ fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+ EXPECT_EQ(response, nullptr);
+ EXPECT_TRUE(g_error_matches(error, domain, code));
+}
+
+TEST(FlJsonMethodCodecTest, EncodeMethodCallNullptrArgs) {
+ g_autofree gchar* text = encode_method_call("hello", nullptr);
+ EXPECT_STREQ(text, "{\"method\":\"hello\",\"args\":null}");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeMethodCallNullArgs) {
+ g_autoptr(FlValue) value = fl_value_new_null();
+ g_autofree gchar* text = encode_method_call("hello", value);
+ EXPECT_STREQ(text, "{\"method\":\"hello\",\"args\":null}");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeMethodCallStringArgs) {
+ g_autoptr(FlValue) args = fl_value_new_string("world");
+ g_autofree gchar* text = encode_method_call("hello", args);
+ EXPECT_STREQ(text, "{\"method\":\"hello\",\"args\":\"world\"}");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeMethodCallListArgs) {
+ g_autoptr(FlValue) args = fl_value_new_list();
+ fl_value_append_take(args, fl_value_new_string("count"));
+ fl_value_append_take(args, fl_value_new_int(42));
+ g_autofree gchar* text = encode_method_call("hello", args);
+ EXPECT_STREQ(text, "{\"method\":\"hello\",\"args\":[\"count\",42]}");
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallNoArgs) {
+ g_autofree gchar* name = nullptr;
+ g_autoptr(FlValue) args = nullptr;
+ decode_method_call("{\"method\":\"hello\"}", &name, &args);
+ EXPECT_STREQ(name, "hello");
+ ASSERT_EQ(args, nullptr);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallNullArgs) {
+ g_autofree gchar* name = nullptr;
+ g_autoptr(FlValue) args = nullptr;
+ decode_method_call("{\"method\":\"hello\",\"args\":null}", &name, &args);
+ EXPECT_STREQ(name, "hello");
+ ASSERT_EQ(fl_value_get_type(args), FL_VALUE_TYPE_NULL);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallStringArgs) {
+ g_autofree gchar* name = nullptr;
+ g_autoptr(FlValue) args = nullptr;
+ decode_method_call("{\"method\":\"hello\",\"args\":\"world\"}", &name, &args);
+ EXPECT_STREQ(name, "hello");
+ ASSERT_EQ(fl_value_get_type(args), FL_VALUE_TYPE_STRING);
+ EXPECT_STREQ(fl_value_get_string(args), "world");
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallListArgs) {
+ g_autofree gchar* name = nullptr;
+ g_autoptr(FlValue) args = nullptr;
+ decode_method_call("{\"method\":\"hello\",\"args\":[\"count\",42]}", &name,
+ &args);
+ EXPECT_STREQ(name, "hello");
+ ASSERT_EQ(fl_value_get_type(args), FL_VALUE_TYPE_LIST);
+ EXPECT_EQ(fl_value_get_length(args), static_cast<size_t>(2));
+
+ FlValue* arg0 = fl_value_get_list_value(args, 0);
+ ASSERT_EQ(fl_value_get_type(arg0), FL_VALUE_TYPE_STRING);
+ EXPECT_STREQ(fl_value_get_string(arg0), "count");
+
+ FlValue* arg1 = fl_value_get_list_value(args, 1);
+ ASSERT_EQ(fl_value_get_type(arg1), FL_VALUE_TYPE_INT);
+ EXPECT_EQ(fl_value_get_int(arg1), 42);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallNoData) {
+ decode_error_method_call("", FL_JSON_MESSAGE_CODEC_ERROR,
+ FL_JSON_MESSAGE_CODEC_ERROR_INVALID_JSON);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallNoMethodOrArgs) {
+ decode_error_method_call("{}", FL_MESSAGE_CODEC_ERROR,
+ FL_MESSAGE_CODEC_ERROR_FAILED);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallInvalidJson) {
+ decode_error_method_call("X", FL_JSON_MESSAGE_CODEC_ERROR,
+ FL_JSON_MESSAGE_CODEC_ERROR_INVALID_JSON);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallWrongType) {
+ decode_error_method_call("42", FL_MESSAGE_CODEC_ERROR,
+ FL_MESSAGE_CODEC_ERROR_FAILED);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallNoMethod) {
+ decode_error_method_call("{\"args\":\"world\"}", FL_MESSAGE_CODEC_ERROR,
+ FL_MESSAGE_CODEC_ERROR_FAILED);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallNoTerminator) {
+ decode_error_method_call("{\"method\":\"hello\",\"args\":\"world\"",
+ FL_JSON_MESSAGE_CODEC_ERROR,
+ FL_JSON_MESSAGE_CODEC_ERROR_INVALID_JSON);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallExtraData) {
+ decode_error_method_call("{\"method\":\"hello\"}XXX",
+ FL_JSON_MESSAGE_CODEC_ERROR,
+ FL_JSON_MESSAGE_CODEC_ERROR_INVALID_JSON);
+}
+
+TEST(FlJsonMethodCodecTest, EncodeSuccessEnvelopeNullptr) {
+ g_autofree gchar* text = encode_success_envelope(nullptr);
+ EXPECT_STREQ(text, "[null]");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeSuccessEnvelopeNull) {
+ g_autoptr(FlValue) result = fl_value_new_null();
+ g_autofree gchar* text = encode_success_envelope(result);
+ EXPECT_STREQ(text, "[null]");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeSuccessEnvelopeString) {
+ g_autoptr(FlValue) result = fl_value_new_string("hello");
+ g_autofree gchar* text = encode_success_envelope(result);
+ EXPECT_STREQ(text, "[\"hello\"]");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeSuccessEnvelopeList) {
+ g_autoptr(FlValue) result = fl_value_new_list();
+ fl_value_append_take(result, fl_value_new_string("count"));
+ fl_value_append_take(result, fl_value_new_int(42));
+ g_autofree gchar* text = encode_success_envelope(result);
+ EXPECT_STREQ(text, "[[\"count\",42]]");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeErrorEnvelopeEmptyCode) {
+ g_autofree gchar* text = encode_error_envelope("", nullptr, nullptr);
+ EXPECT_STREQ(text, "[\"\",null,null]");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeErrorEnvelopeNonMessageOrDetails) {
+ g_autofree gchar* text = encode_error_envelope("error", nullptr, nullptr);
+ EXPECT_STREQ(text, "[\"error\",null,null]");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeErrorEnvelopeMessage) {
+ g_autofree gchar* text = encode_error_envelope("error", "message", nullptr);
+ EXPECT_STREQ(text, "[\"error\",\"message\",null]");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeErrorEnvelopeDetails) {
+ g_autoptr(FlValue) details = fl_value_new_list();
+ fl_value_append_take(details, fl_value_new_string("count"));
+ fl_value_append_take(details, fl_value_new_int(42));
+ g_autofree gchar* text = encode_error_envelope("error", nullptr, details);
+ EXPECT_STREQ(text, "[\"error\",null,[\"count\",42]]");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeErrorEnvelopeMessageAndDetails) {
+ g_autoptr(FlValue) details = fl_value_new_list();
+ fl_value_append_take(details, fl_value_new_string("count"));
+ fl_value_append_take(details, fl_value_new_int(42));
+ g_autofree gchar* text = encode_error_envelope("error", "message", details);
+ EXPECT_STREQ(text, "[\"error\",\"message\",[\"count\",42]]");
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseSuccessNull) {
+ g_autoptr(FlValue) result = fl_value_new_null();
+ decode_response_with_success("[null]", result);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseSuccessString) {
+ g_autoptr(FlValue) result = fl_value_new_string("hello");
+ decode_response_with_success("[\"hello\"]", result);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseSuccessList) {
+ g_autoptr(FlValue) result = fl_value_new_list();
+ fl_value_append_take(result, fl_value_new_string("count"));
+ fl_value_append_take(result, fl_value_new_int(42));
+ decode_response_with_success("[[\"count\",42]]", result);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseErrorEmptyCode) {
+ decode_response_with_error("[\"\",null,null]", "", nullptr, nullptr);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseErrorNoMessageOrDetails) {
+ decode_response_with_error("[\"error\",null,null]", "error", nullptr,
+ nullptr);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseErrorMessage) {
+ decode_response_with_error("[\"error\",\"message\",null]", "error", "message",
+ nullptr);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseErrorDetails) {
+ g_autoptr(FlValue) details = fl_value_new_list();
+ fl_value_append_take(details, fl_value_new_string("count"));
+ fl_value_append_take(details, fl_value_new_int(42));
+ decode_response_with_error("[\"error\",null,[\"count\",42]]", "error",
+ nullptr, details);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseErrorMessageAndDetails) {
+ g_autoptr(FlValue) details = fl_value_new_list();
+ fl_value_append_take(details, fl_value_new_string("count"));
+ fl_value_append_take(details, fl_value_new_int(42));
+ decode_response_with_error("[\"error\",\"message\",[\"count\",42]]", "error",
+ "message", details);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseNotImplemented) {
+ g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
+ g_autoptr(GBytes) message = g_bytes_new(nullptr, 0);
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(FlMethodResponse) response =
+ fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+ ASSERT_NE(response, nullptr);
+ EXPECT_EQ(error, nullptr);
+ EXPECT_TRUE(FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response));
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseNoTerminator) {
+ decode_error_response("[42", FL_JSON_MESSAGE_CODEC_ERROR,
+ FL_JSON_MESSAGE_CODEC_ERROR_INVALID_JSON);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseInvalidJson) {
+ decode_error_response("X", FL_JSON_MESSAGE_CODEC_ERROR,
+ FL_JSON_MESSAGE_CODEC_ERROR_INVALID_JSON);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseMissingDetails) {
+ decode_error_response("[\"error\",\"message\"]", FL_MESSAGE_CODEC_ERROR,
+ FL_MESSAGE_CODEC_ERROR_FAILED);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseExtraDetails) {
+ decode_error_response("[\"error\",\"message\",true,42]",
+ FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseSuccessExtraData) {
+ decode_error_response("[null]X", FL_JSON_MESSAGE_CODEC_ERROR,
+ FL_JSON_MESSAGE_CODEC_ERROR_INVALID_JSON);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseErrorExtraData) {
+ decode_error_response("[\"error\",null,null]X", FL_JSON_MESSAGE_CODEC_ERROR,
+ FL_JSON_MESSAGE_CODEC_ERROR_INVALID_JSON);
+}
diff --git a/shell/platform/linux/fl_method_call.cc b/shell/platform/linux/fl_method_call.cc
new file mode 100644
index 0000000..0b65891
--- /dev/null
+++ b/shell/platform/linux/fl_method_call.cc
@@ -0,0 +1,119 @@
+// 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_method_call.h"
+#include "flutter/shell/platform/linux/fl_method_call_private.h"
+#include "flutter/shell/platform/linux/fl_method_channel_private.h"
+
+#include <gmodule.h>
+
+struct _FlMethodCall {
+ GObject parent_instance;
+
+ // Name of method being called
+ gchar* name;
+
+ // Arguments provided to method call
+ FlValue* args;
+
+ // Channel to respond on
+ FlMethodChannel* channel;
+ FlBinaryMessengerResponseHandle* response_handle;
+};
+
+G_DEFINE_TYPE(FlMethodCall, fl_method_call, G_TYPE_OBJECT)
+
+static void fl_method_call_dispose(GObject* object) {
+ FlMethodCall* self = FL_METHOD_CALL(object);
+
+ g_clear_pointer(&self->name, g_free);
+ g_clear_pointer(&self->args, fl_value_unref);
+ g_clear_object(&self->channel);
+ g_clear_object(&self->response_handle);
+
+ G_OBJECT_CLASS(fl_method_call_parent_class)->dispose(object);
+}
+
+static void fl_method_call_class_init(FlMethodCallClass* klass) {
+ G_OBJECT_CLASS(klass)->dispose = fl_method_call_dispose;
+}
+
+static void fl_method_call_init(FlMethodCall* self) {}
+
+FlMethodCall* fl_method_call_new(
+ const gchar* name,
+ FlValue* args,
+ FlMethodChannel* channel,
+ FlBinaryMessengerResponseHandle* response_handle) {
+ g_return_val_if_fail(name != nullptr, nullptr);
+ g_return_val_if_fail(args != nullptr, nullptr);
+ g_return_val_if_fail(FL_IS_METHOD_CHANNEL(channel), nullptr);
+ g_return_val_if_fail(FL_IS_BINARY_MESSENGER_RESPONSE_HANDLE(response_handle),
+ nullptr);
+
+ FlMethodCall* self =
+ FL_METHOD_CALL(g_object_new(fl_method_call_get_type(), nullptr));
+
+ self->name = g_strdup(name);
+ self->args = fl_value_ref(args);
+ self->channel = FL_METHOD_CHANNEL(g_object_ref(channel));
+ self->response_handle =
+ FL_BINARY_MESSENGER_RESPONSE_HANDLE(g_object_ref(response_handle));
+
+ return self;
+}
+
+G_MODULE_EXPORT const gchar* fl_method_call_get_name(FlMethodCall* self) {
+ g_return_val_if_fail(FL_IS_METHOD_CALL(self), nullptr);
+ return self->name;
+}
+
+G_MODULE_EXPORT FlValue* fl_method_call_get_args(FlMethodCall* self) {
+ g_return_val_if_fail(FL_IS_METHOD_CALL(self), nullptr);
+ return self->args;
+}
+
+gboolean fl_method_call_respond(FlMethodCall* self,
+ FlMethodResponse* response,
+ GError** error) {
+ g_return_val_if_fail(FL_IS_METHOD_CALL(self), FALSE);
+ g_return_val_if_fail(FL_IS_METHOD_RESPONSE(response), FALSE);
+ return fl_method_channel_respond(self->channel, self->response_handle,
+ response, error);
+}
+
+G_MODULE_EXPORT gboolean fl_method_call_respond_success(FlMethodCall* self,
+ FlValue* result,
+ GError** error) {
+ g_return_val_if_fail(FL_IS_METHOD_CALL(self), FALSE);
+
+ g_autoptr(FlMethodResponse) response =
+ FL_METHOD_RESPONSE(fl_method_success_response_new(result));
+ return fl_method_channel_respond(self->channel, self->response_handle,
+ response, error);
+}
+
+G_MODULE_EXPORT gboolean fl_method_call_respond_error(FlMethodCall* self,
+ const gchar* code,
+ const gchar* message,
+ FlValue* details,
+ GError** error) {
+ g_return_val_if_fail(FL_IS_METHOD_CALL(self), FALSE);
+ g_return_val_if_fail(code != nullptr, FALSE);
+
+ g_autoptr(FlMethodResponse) response =
+ FL_METHOD_RESPONSE(fl_method_error_response_new(code, message, details));
+ return fl_method_channel_respond(self->channel, self->response_handle,
+ response, error);
+}
+
+G_MODULE_EXPORT gboolean
+fl_method_call_respond_not_implemented(FlMethodCall* self, GError** error) {
+ g_return_val_if_fail(FL_IS_METHOD_CALL(self), FALSE);
+
+ g_autoptr(FlMethodResponse) response =
+ FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
+ return fl_method_channel_respond(self->channel, self->response_handle,
+ response, error);
+}
diff --git a/shell/platform/linux/fl_method_call_private.h b/shell/platform/linux/fl_method_call_private.h
new file mode 100644
index 0000000..3388fa8
--- /dev/null
+++ b/shell/platform/linux/fl_method_call_private.h
@@ -0,0 +1,35 @@
+// 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.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CALL_PRIVATE_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CALL_PRIVATE_H_
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_call.h"
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_value.h"
+
+G_BEGIN_DECLS
+
+/**
+ * fl_method_call_new:
+ * @name: a method name.
+ * @args: arguments provided to a method.
+ * @channel: channel call received on.
+ * @response_handle: handle to respond with.
+ *
+ * Creates a method call.
+ *
+ * Returns: a new #FlMethodCall.
+ */
+FlMethodCall* fl_method_call_new(
+ const gchar* name,
+ FlValue* args,
+ FlMethodChannel* channel,
+ FlBinaryMessengerResponseHandle* response_handle);
+
+G_END_DECLS
+
+#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CALL_PRIVATE_H_
diff --git a/shell/platform/linux/fl_method_channel.cc b/shell/platform/linux/fl_method_channel.cc
new file mode 100644
index 0000000..c8d61fa
--- /dev/null
+++ b/shell/platform/linux/fl_method_channel.cc
@@ -0,0 +1,197 @@
+// 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_method_channel.h"
+
+#include "flutter/shell/platform/linux/fl_method_call_private.h"
+#include "flutter/shell/platform/linux/fl_method_channel_private.h"
+#include "flutter/shell/platform/linux/fl_method_codec_private.h"
+
+#include <gmodule.h>
+
+struct _FlMethodChannel {
+ GObject parent_instance;
+
+ // Messenger to communicate on
+ FlBinaryMessenger* messenger;
+
+ // Channel name
+ gchar* name;
+
+ // Codec to en/decode messages
+ FlMethodCodec* codec;
+
+ // Function called when a method call is received
+ FlMethodChannelMethodCallHandler method_call_handler;
+ gpointer method_call_handler_data;
+};
+
+// Added here to stop the compiler from optimising this function away
+G_MODULE_EXPORT GType fl_method_channel_get_type();
+
+G_DEFINE_TYPE(FlMethodChannel, fl_method_channel, G_TYPE_OBJECT)
+
+// Called when a binary message is received on this channel
+static void message_cb(FlBinaryMessenger* messenger,
+ const gchar* channel,
+ GBytes* message,
+ FlBinaryMessengerResponseHandle* response_handle,
+ gpointer user_data) {
+ FlMethodChannel* self = FL_METHOD_CHANNEL(user_data);
+
+ if (self->method_call_handler == nullptr)
+ return;
+
+ g_autofree gchar* method = nullptr;
+ g_autoptr(FlValue) args = nullptr;
+ g_autoptr(GError) error = nullptr;
+ if (!fl_method_codec_decode_method_call(self->codec, message, &method, &args,
+ &error)) {
+ g_warning("Failed to decode method call: %s", error->message);
+ return;
+ }
+
+ g_autoptr(FlMethodCall) method_call =
+ fl_method_call_new(method, args, self, response_handle);
+ self->method_call_handler(self, method_call, self->method_call_handler_data);
+}
+
+// Called when a response is received to a sent message
+static void message_response_cb(GObject* object,
+ GAsyncResult* result,
+ gpointer user_data) {
+ GTask* task = G_TASK(user_data);
+ g_task_return_pointer(task, result, g_object_unref);
+}
+
+static void fl_method_channel_dispose(GObject* object) {
+ FlMethodChannel* self = FL_METHOD_CHANNEL(object);
+
+ if (self->messenger != nullptr)
+ fl_binary_messenger_set_message_handler_on_channel(
+ self->messenger, self->name, nullptr, nullptr);
+
+ g_clear_object(&self->messenger);
+ g_clear_pointer(&self->name, g_free);
+ g_clear_object(&self->codec);
+
+ G_OBJECT_CLASS(fl_method_channel_parent_class)->dispose(object);
+}
+
+static void fl_method_channel_class_init(FlMethodChannelClass* klass) {
+ G_OBJECT_CLASS(klass)->dispose = fl_method_channel_dispose;
+}
+
+static void fl_method_channel_init(FlMethodChannel* self) {}
+
+G_MODULE_EXPORT FlMethodChannel* fl_method_channel_new(
+ FlBinaryMessenger* messenger,
+ const gchar* name,
+ FlMethodCodec* codec) {
+ g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr);
+ g_return_val_if_fail(name != nullptr, nullptr);
+ g_return_val_if_fail(FL_IS_METHOD_CODEC(codec), nullptr);
+
+ FlMethodChannel* self =
+ FL_METHOD_CHANNEL(g_object_new(fl_method_channel_get_type(), nullptr));
+
+ self->messenger = FL_BINARY_MESSENGER(g_object_ref(messenger));
+ self->name = g_strdup(name);
+ self->codec = FL_METHOD_CODEC(g_object_ref(codec));
+
+ fl_binary_messenger_set_message_handler_on_channel(
+ self->messenger, self->name, message_cb, self);
+
+ return self;
+}
+
+G_MODULE_EXPORT void fl_method_channel_set_method_call_handler(
+ FlMethodChannel* self,
+ FlMethodChannelMethodCallHandler handler,
+ gpointer user_data) {
+ g_return_if_fail(FL_IS_METHOD_CHANNEL(self));
+
+ self->method_call_handler = handler;
+ self->method_call_handler_data = user_data;
+}
+
+G_MODULE_EXPORT void fl_method_channel_invoke_method(
+ FlMethodChannel* self,
+ const gchar* method,
+ FlValue* args,
+ GCancellable* cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data) {
+ g_return_if_fail(FL_IS_METHOD_CHANNEL(self));
+ g_return_if_fail(method != nullptr);
+
+ g_autoptr(GTask) task =
+ callback != nullptr ? g_task_new(self, cancellable, callback, user_data)
+ : nullptr;
+
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(GBytes) message =
+ fl_method_codec_encode_method_call(self->codec, method, args, &error);
+ if (message == nullptr) {
+ if (task != nullptr)
+ g_task_return_error(task, error);
+ return;
+ }
+
+ fl_binary_messenger_send_on_channel(
+ self->messenger, self->name, message, cancellable,
+ callback != nullptr ? message_response_cb : nullptr,
+ g_steal_pointer(&task));
+}
+
+G_MODULE_EXPORT FlMethodResponse* fl_method_channel_invoke_method_finish(
+ FlMethodChannel* self,
+ GAsyncResult* result,
+ GError** error) {
+ g_return_val_if_fail(FL_IS_METHOD_CHANNEL(self), nullptr);
+ g_return_val_if_fail(g_task_is_valid(result, self), nullptr);
+
+ g_autoptr(GTask) task = G_TASK(result);
+ GAsyncResult* r = G_ASYNC_RESULT(g_task_propagate_pointer(task, nullptr));
+
+ g_autoptr(GBytes) response =
+ fl_binary_messenger_send_on_channel_finish(self->messenger, r, error);
+ if (response == nullptr)
+ return nullptr;
+
+ return fl_method_codec_decode_response(self->codec, response, error);
+}
+
+gboolean fl_method_channel_respond(
+ FlMethodChannel* self,
+ FlBinaryMessengerResponseHandle* response_handle,
+ FlMethodResponse* response,
+ GError** error) {
+ g_return_val_if_fail(FL_IS_METHOD_CHANNEL(self), FALSE);
+ g_return_val_if_fail(FL_IS_BINARY_MESSENGER_RESPONSE_HANDLE(response_handle),
+ FALSE);
+ g_return_val_if_fail(FL_IS_METHOD_SUCCESS_RESPONSE(response) ||
+ FL_IS_METHOD_ERROR_RESPONSE(response) ||
+ FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response),
+ FALSE);
+
+ g_autoptr(GBytes) message = nullptr;
+ if (FL_IS_METHOD_SUCCESS_RESPONSE(response)) {
+ FlMethodSuccessResponse* r = FL_METHOD_SUCCESS_RESPONSE(response);
+ message = fl_method_codec_encode_success_envelope(
+ self->codec, fl_method_success_response_get_result(r), error);
+ } else if (FL_IS_METHOD_ERROR_RESPONSE(response)) {
+ FlMethodErrorResponse* r = FL_METHOD_ERROR_RESPONSE(response);
+ message = fl_method_codec_encode_error_envelope(
+ self->codec, fl_method_error_response_get_code(r),
+ fl_method_error_response_get_message(r),
+ fl_method_error_response_get_details(r), error);
+ } else if (FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response))
+ message = nullptr;
+ else
+ g_assert_not_reached();
+
+ return fl_binary_messenger_send_response(self->messenger, response_handle,
+ message, error);
+}
diff --git a/shell/platform/linux/fl_method_channel_private.h b/shell/platform/linux/fl_method_channel_private.h
new file mode 100644
index 0000000..f31b91f
--- /dev/null
+++ b/shell/platform/linux/fl_method_channel_private.h
@@ -0,0 +1,35 @@
+// 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.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CHANNEL_PRIVATE_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CHANNEL_PRIVATE_H_
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_response.h"
+
+G_BEGIN_DECLS
+
+/**
+ * fl_method_channel_respond:
+ * @channel: an #FlMethodChannel.
+ * @response_handle: an #FlBinaryMessengerResponseHandle.
+ * @response: an #FlMethodResponse.
+ * @error: (allow-none): #GError location to store the error occurring, or %NULL
+ * to ignore.
+ *
+ * Responds to a method call.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean fl_method_channel_respond(
+ FlMethodChannel* channel,
+ FlBinaryMessengerResponseHandle* response_handle,
+ FlMethodResponse* response,
+ GError** error);
+
+G_END_DECLS
+
+#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CHANNEL_PRIVATE_H_
diff --git a/shell/platform/linux/fl_method_codec.cc b/shell/platform/linux/fl_method_codec.cc
new file mode 100644
index 0000000..11bed27
--- /dev/null
+++ b/shell/platform/linux/fl_method_codec.cc
@@ -0,0 +1,75 @@
+// 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_method_codec.h"
+#include "flutter/shell/platform/linux/fl_method_codec_private.h"
+
+#include <gmodule.h>
+
+// Added here to stop the compiler from optimising this function away
+G_MODULE_EXPORT GType fl_method_codec_get_type();
+
+G_DEFINE_TYPE(FlMethodCodec, fl_method_codec, G_TYPE_OBJECT)
+
+static void fl_method_codec_class_init(FlMethodCodecClass* klass) {}
+
+static void fl_method_codec_init(FlMethodCodec* self) {}
+
+GBytes* fl_method_codec_encode_method_call(FlMethodCodec* self,
+ const gchar* name,
+ FlValue* args,
+ GError** error) {
+ g_return_val_if_fail(FL_IS_METHOD_CODEC(self), nullptr);
+ g_return_val_if_fail(name != nullptr, nullptr);
+
+ return FL_METHOD_CODEC_GET_CLASS(self)->encode_method_call(self, name, args,
+ error);
+}
+
+gboolean fl_method_codec_decode_method_call(FlMethodCodec* self,
+ GBytes* message,
+ gchar** name,
+ FlValue** args,
+ GError** error) {
+ g_return_val_if_fail(FL_IS_METHOD_CODEC(self), FALSE);
+ g_return_val_if_fail(message != nullptr, FALSE);
+ g_return_val_if_fail(name != nullptr, FALSE);
+ g_return_val_if_fail(args != nullptr, FALSE);
+
+ return FL_METHOD_CODEC_GET_CLASS(self)->decode_method_call(self, message,
+ name, args, error);
+}
+
+GBytes* fl_method_codec_encode_success_envelope(FlMethodCodec* self,
+ FlValue* result,
+ GError** error) {
+ g_return_val_if_fail(FL_IS_METHOD_CODEC(self), nullptr);
+
+ return FL_METHOD_CODEC_GET_CLASS(self)->encode_success_envelope(self, result,
+ error);
+}
+
+GBytes* fl_method_codec_encode_error_envelope(FlMethodCodec* self,
+ const gchar* code,
+ const gchar* message,
+ FlValue* details,
+ GError** error) {
+ g_return_val_if_fail(FL_IS_METHOD_CODEC(self), nullptr);
+ g_return_val_if_fail(code != nullptr, nullptr);
+
+ return FL_METHOD_CODEC_GET_CLASS(self)->encode_error_envelope(
+ self, code, message, details, error);
+}
+
+FlMethodResponse* fl_method_codec_decode_response(FlMethodCodec* self,
+ GBytes* message,
+ GError** error) {
+ g_return_val_if_fail(FL_IS_METHOD_CODEC(self), nullptr);
+ g_return_val_if_fail(message != nullptr, nullptr);
+
+ if (g_bytes_get_size(message) == 0)
+ return FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
+
+ return FL_METHOD_CODEC_GET_CLASS(self)->decode_response(self, message, error);
+}
diff --git a/shell/platform/linux/fl_method_codec_private.h b/shell/platform/linux/fl_method_codec_private.h
new file mode 100644
index 0000000..8912093
--- /dev/null
+++ b/shell/platform/linux/fl_method_codec_private.h
@@ -0,0 +1,106 @@
+// 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.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CODEC_PRIVATE_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CODEC_PRIVATE_H_
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_codec.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_response.h"
+
+G_BEGIN_DECLS
+
+/**
+ * fl_method_codec_encode_method_call:
+ * @codec: an #FlMethodCodec.
+ * @name: method name.
+ * @args: (allow-none): method arguments, or %NULL.
+ * @error: (allow-none): #GError location to store the error occurring, or
+ * %NULL.
+ *
+ * Encodes a method call.
+ *
+ * Returns: (transfer full): a binary encoding of this method call or %NULL if
+ * not able to encode.
+ */
+GBytes* fl_method_codec_encode_method_call(FlMethodCodec* codec,
+ const gchar* name,
+ FlValue* args,
+ GError** error);
+
+/**
+ * fl_method_codec_decode_method_call:
+ * @codec: an #FlMethodCodec.
+ * @message: message to decode.
+ * @name: (transfer full): location to write method name or %NULL if not
+ * required.
+ * @args: (transfer full): location to write method arguments, or %NULL if not
+ * required.
+ * @error: (allow-none): #GError location to store the error occurring, or
+ * %NULL.
+ *
+ * Decodes a method call.
+ *
+ * Returns: %TRUE if successfully decoded.
+ */
+gboolean fl_method_codec_decode_method_call(FlMethodCodec* codec,
+ GBytes* message,
+ gchar** name,
+ FlValue** args,
+ GError** error);
+
+/**
+ * fl_method_codec_encode_success_envelope:
+ * @codec: an #FlMethodCodec.
+ * @result: (allow-none): method result, or %NULL.
+ * @error: (allow-none): #GError location to store the error occurring, or
+ * %NULL.
+ *
+ * Encodes a successful response to a method call.
+ *
+ * Returns: (transfer full): a binary encoding of this response or %NULL if not
+ * able to encode.
+ */
+GBytes* fl_method_codec_encode_success_envelope(FlMethodCodec* codec,
+ FlValue* result,
+ GError** error);
+
+/**
+ * fl_method_codec_encode_error_envelope:
+ * @codec: an #FlMethodCodec.
+ * @code: an error code.
+ * @message: (allow-none): an error message or %NULL.
+ * @details: (allow-none): error details, or %NULL.
+ * @error: (allow-none): #GError location to store the error occurring, or
+ * %NULL.
+ *
+ * Encodes an error response to a method call.
+ *
+ * Returns: (transfer full): a binary encoding of this response or %NULL if not
+ * able to encode.
+ */
+GBytes* fl_method_codec_encode_error_envelope(FlMethodCodec* codec,
+ const gchar* code,
+ const gchar* message,
+ FlValue* details,
+ GError** error);
+
+/**
+ * fl_method_codec_decode_response:
+ * @codec: an #FlMethodCodec.
+ * @message: message to decode.
+ * @error: (allow-none): #GError location to store the error occurring, or
+ * %NULL.
+ *
+ * Decodes a response to a method call. If the call resulted in an error then
+ * @error_code is set, otherwise it is %NULL.
+ *
+ * Returns: a new #FlMethodResponse or %NULL on error.
+ */
+FlMethodResponse* fl_method_codec_decode_response(FlMethodCodec* codec,
+ GBytes* message,
+ GError** error);
+
+G_END_DECLS
+
+#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CODEC_PRIVATE_H_
diff --git a/shell/platform/linux/fl_method_codec_test.cc b/shell/platform/linux/fl_method_codec_test.cc
new file mode 100644
index 0000000..cb216cb
--- /dev/null
+++ b/shell/platform/linux/fl_method_codec_test.cc
@@ -0,0 +1,402 @@
+// 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_method_codec.h"
+#include "flutter/shell/platform/linux/fl_method_codec_private.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_message_codec.h"
+#include "gtest/gtest.h"
+
+G_DECLARE_FINAL_TYPE(FlTestMethodCodec,
+ fl_test_method_codec,
+ FL,
+ TEST_METHOD_CODEC,
+ FlMethodCodec)
+
+// Implement the FlMethodCodec API for the following tests to check it works as
+// expected
+struct _FlTestMethodCodec {
+ FlMethodCodec parent_instance;
+};
+
+G_DEFINE_TYPE(FlTestMethodCodec,
+ fl_test_method_codec,
+ fl_method_codec_get_type())
+
+// Helper function to convert binary data to text
+static gchar* message_to_text(GBytes* message) {
+ size_t data_length;
+ const gchar* data =
+ static_cast<const gchar*>(g_bytes_get_data(message, &data_length));
+ return g_strndup(data, data_length);
+}
+
+// Helper function to convert text to binary data
+static GBytes* text_to_message(const gchar* text) {
+ return g_bytes_new(text, strlen(text));
+}
+
+// Implements FlMethodCodec::encode_method_call
+static GBytes* fl_test_codec_encode_method_call(FlMethodCodec* codec,
+ const gchar* name,
+ FlValue* args,
+ GError** error) {
+ EXPECT_TRUE(FL_IS_TEST_METHOD_CODEC(codec));
+
+ g_autofree gchar* text = nullptr;
+ if (args == nullptr || fl_value_get_type(args) == FL_VALUE_TYPE_NULL)
+ text = g_strdup_printf("%s()", name);
+ else if (fl_value_get_type(args) == FL_VALUE_TYPE_INT)
+ text = g_strdup_printf("%s(%" G_GINT64_FORMAT ")", name,
+ fl_value_get_int(args));
+ else {
+ g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+ "ERROR");
+ return nullptr;
+ }
+
+ return text_to_message(text);
+}
+
+// Implements FlMethodCodec::decode_method_call
+static gboolean fl_test_codec_decode_method_call(FlMethodCodec* codec,
+ GBytes* message,
+ gchar** name,
+ FlValue** args,
+ GError** error) {
+ EXPECT_TRUE(FL_IS_TEST_METHOD_CODEC(codec));
+
+ g_autofree gchar* m = message_to_text(message);
+
+ if (strcmp(m, "error") == 0) {
+ g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+ "ERROR");
+ return FALSE;
+ } else {
+ *name = g_strdup(m);
+ *args = fl_value_new_null();
+ return TRUE;
+ }
+}
+
+// Implements FlMethodCodec::encode_success_envelope
+static GBytes* fl_test_codec_encode_success_envelope(FlMethodCodec* codec,
+ FlValue* result,
+ GError** error) {
+ EXPECT_TRUE(FL_IS_TEST_METHOD_CODEC(codec));
+
+ g_autofree gchar* text = nullptr;
+ if (result == nullptr || fl_value_get_type(result) == FL_VALUE_TYPE_NULL)
+ text = g_strdup("(null)");
+ else if (fl_value_get_type(result) == FL_VALUE_TYPE_INT)
+ text = g_strdup_printf("%" G_GINT64_FORMAT, fl_value_get_int(result));
+ else {
+ g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+ "ERROR");
+ return nullptr;
+ }
+
+ return text_to_message(text);
+}
+
+// Implements FlMethodCodec::encode_error_envelope
+static GBytes* fl_test_codec_encode_error_envelope(FlMethodCodec* codec,
+ const gchar* code,
+ const gchar* message,
+ FlValue* details,
+ GError** error) {
+ EXPECT_TRUE(FL_IS_TEST_METHOD_CODEC(codec));
+
+ if (details != nullptr && fl_value_get_type(details) != FL_VALUE_TYPE_INT) {
+ g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+ "ERROR");
+ return nullptr;
+ }
+
+ g_autofree gchar* text = nullptr;
+ if (message == nullptr) {
+ if (details == nullptr || fl_value_get_type(details) == FL_VALUE_TYPE_NULL)
+ text = g_strdup_printf("Error_%s()", code);
+ else
+ text = g_strdup_printf("Error_%s(%" G_GINT64_FORMAT ")", code,
+ fl_value_get_int(details));
+ } else {
+ if (details == nullptr || fl_value_get_type(details) == FL_VALUE_TYPE_NULL)
+ text = g_strdup_printf("Error_%s(%s)", code, message);
+ else
+ text = g_strdup_printf("Error_%s(%s,%" G_GINT64_FORMAT ")", code, message,
+ fl_value_get_int(details));
+ }
+
+ return text_to_message(text);
+}
+
+// Implements FlMethodCodec::decode_response
+static FlMethodResponse* fl_test_codec_decode_response(FlMethodCodec* codec,
+ GBytes* message,
+ GError** error) {
+ EXPECT_TRUE(FL_IS_TEST_METHOD_CODEC(codec));
+
+ g_autofree gchar* m = message_to_text(message);
+ if (strcmp(m, "codec-error") == 0) {
+ g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+ "ERROR");
+ return nullptr;
+ } else if (strcmp(m, "error") == 0) {
+ g_autoptr(FlValue) details = fl_value_new_int(42);
+ return FL_METHOD_RESPONSE(
+ fl_method_error_response_new("code", "message", details));
+ } else {
+ g_autoptr(FlValue) result = fl_value_new_string(m);
+ return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
+ }
+}
+
+static void fl_test_method_codec_class_init(FlTestMethodCodecClass* klass) {
+ FL_METHOD_CODEC_CLASS(klass)->encode_method_call =
+ fl_test_codec_encode_method_call;
+ FL_METHOD_CODEC_CLASS(klass)->decode_method_call =
+ fl_test_codec_decode_method_call;
+ FL_METHOD_CODEC_CLASS(klass)->encode_success_envelope =
+ fl_test_codec_encode_success_envelope;
+ FL_METHOD_CODEC_CLASS(klass)->encode_error_envelope =
+ fl_test_codec_encode_error_envelope;
+ FL_METHOD_CODEC_CLASS(klass)->decode_response = fl_test_codec_decode_response;
+}
+
+static void fl_test_method_codec_init(FlTestMethodCodec* self) {}
+
+static FlTestMethodCodec* fl_test_method_codec_new() {
+ return FL_TEST_METHOD_CODEC(
+ g_object_new(fl_test_method_codec_get_type(), nullptr));
+}
+
+TEST(FlMethodCodecTest, EncodeMethodCall) {
+ g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
+ FL_METHOD_CODEC(codec), "foo", nullptr, &error);
+ EXPECT_EQ(error, nullptr);
+ EXPECT_NE(message, nullptr);
+
+ g_autofree gchar* message_text = message_to_text(message);
+ EXPECT_STREQ(message_text, "foo()");
+}
+
+TEST(FlMethodCodecTest, EncodeMethodCallEmptyName) {
+ g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
+ FL_METHOD_CODEC(codec), "", nullptr, &error);
+ EXPECT_EQ(error, nullptr);
+ EXPECT_NE(message, nullptr);
+
+ g_autofree gchar* message_text = message_to_text(message);
+ EXPECT_STREQ(message_text, "()");
+}
+
+TEST(FlMethodCodecTest, EncodeMethodCallArgs) {
+ g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+ g_autoptr(FlValue) args = fl_value_new_int(42);
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
+ FL_METHOD_CODEC(codec), "foo", args, &error);
+ EXPECT_EQ(error, nullptr);
+ EXPECT_NE(message, nullptr);
+
+ g_autofree gchar* message_text = message_to_text(message);
+ EXPECT_STREQ(message_text, "foo(42)");
+}
+
+TEST(FlMethodCodecTest, EncodeMethodCallError) {
+ g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+ g_autoptr(FlValue) args = fl_value_new_bool(FALSE);
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
+ FL_METHOD_CODEC(codec), "foo", args, &error);
+ EXPECT_EQ(message, nullptr);
+ EXPECT_TRUE(g_error_matches(error, FL_MESSAGE_CODEC_ERROR,
+ FL_MESSAGE_CODEC_ERROR_FAILED));
+}
+
+TEST(FlMethodCodecTest, DecodeMethodCall) {
+ g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+ g_autoptr(GBytes) message = text_to_message("foo");
+
+ g_autofree gchar* name = nullptr;
+ g_autoptr(FlValue) args = nullptr;
+ g_autoptr(GError) error = nullptr;
+ gboolean result = fl_method_codec_decode_method_call(
+ FL_METHOD_CODEC(codec), message, &name, &args, &error);
+ EXPECT_EQ(error, nullptr);
+ EXPECT_TRUE(result);
+
+ EXPECT_STREQ(name, "foo");
+ ASSERT_EQ(fl_value_get_type(args), FL_VALUE_TYPE_NULL);
+}
+
+TEST(FlMethodCodecTest, EncodeSuccessEnvelope) {
+ g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+ g_autoptr(FlValue) result = fl_value_new_int(42);
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(GBytes) message = fl_method_codec_encode_success_envelope(
+ FL_METHOD_CODEC(codec), result, &error);
+ EXPECT_EQ(error, nullptr);
+ EXPECT_NE(message, nullptr);
+
+ g_autofree gchar* message_text = message_to_text(message);
+ EXPECT_STREQ(message_text, "42");
+}
+
+TEST(FlMethodCodecTest, EncodeSuccessEnvelopeEmpty) {
+ g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(GBytes) message = fl_method_codec_encode_success_envelope(
+ FL_METHOD_CODEC(codec), nullptr, &error);
+ EXPECT_EQ(error, nullptr);
+ EXPECT_NE(message, nullptr);
+
+ g_autofree gchar* message_text = message_to_text(message);
+ EXPECT_STREQ(message_text, "(null)");
+}
+
+TEST(FlMethodCodecTest, EncodeSuccessEnvelopeError) {
+ g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+ g_autoptr(FlValue) result = fl_value_new_string("X");
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(GBytes) message = fl_method_codec_encode_success_envelope(
+ FL_METHOD_CODEC(codec), result, &error);
+ EXPECT_EQ(message, nullptr);
+ EXPECT_TRUE(g_error_matches(error, FL_MESSAGE_CODEC_ERROR,
+ FL_MESSAGE_CODEC_ERROR_FAILED));
+}
+
+TEST(FlMethodCodecTest, EncodeErrorEnvelopeNoMessageOrDetails) {
+ g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(GBytes) message = fl_method_codec_encode_error_envelope(
+ FL_METHOD_CODEC(codec), "code", nullptr, nullptr, &error);
+ EXPECT_EQ(error, nullptr);
+ EXPECT_NE(message, nullptr);
+
+ g_autofree gchar* message_text = message_to_text(message);
+ EXPECT_STREQ(message_text, "Error_code()");
+}
+
+TEST(FlMethodCodecTest, EncodeErrorEnvelopeMessage) {
+ g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(GBytes) message = fl_method_codec_encode_error_envelope(
+ FL_METHOD_CODEC(codec), "code", "message", nullptr, &error);
+ EXPECT_EQ(error, nullptr);
+ EXPECT_NE(message, nullptr);
+
+ g_autofree gchar* message_text = message_to_text(message);
+ EXPECT_STREQ(message_text, "Error_code(message)");
+}
+
+TEST(FlMethodCodecTest, EncodeErrorEnvelopeDetails) {
+ g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+ g_autoptr(FlValue) details = fl_value_new_int(42);
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(GBytes) message = fl_method_codec_encode_error_envelope(
+ FL_METHOD_CODEC(codec), "code", nullptr, details, &error);
+ EXPECT_EQ(error, nullptr);
+ EXPECT_NE(message, nullptr);
+
+ g_autofree gchar* message_text = message_to_text(message);
+ EXPECT_STREQ(message_text, "Error_code(42)");
+}
+
+TEST(FlMethodCodecTest, EncodeErrorEnvelopeMessageAndDetails) {
+ g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+ g_autoptr(FlValue) details = fl_value_new_int(42);
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(GBytes) message = fl_method_codec_encode_error_envelope(
+ FL_METHOD_CODEC(codec), "code", "message", details, &error);
+ EXPECT_EQ(error, nullptr);
+ EXPECT_NE(message, nullptr);
+
+ g_autofree gchar* message_text = message_to_text(message);
+ EXPECT_STREQ(message_text, "Error_code(message,42)");
+}
+
+TEST(FlMethodCodecTest, DecodeResponseSuccess) {
+ g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+ g_autoptr(GBytes) message = text_to_message("echo");
+
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(FlMethodResponse) response =
+ fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+ EXPECT_EQ(error, nullptr);
+ EXPECT_NE(response, nullptr);
+ ASSERT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
+ FlValue* result = fl_method_success_response_get_result(
+ FL_METHOD_SUCCESS_RESPONSE(response));
+ ASSERT_NE(result, nullptr);
+ ASSERT_EQ(fl_value_get_type(result), FL_VALUE_TYPE_STRING);
+ EXPECT_STREQ(fl_value_get_string(result), "echo");
+}
+
+TEST(FlMethodCodecTest, DecodeResponseNotImplemented) {
+ g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+ g_autoptr(GBytes) message = g_bytes_new(nullptr, 0);
+
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(FlMethodResponse) response =
+ fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+ EXPECT_EQ(error, nullptr);
+ EXPECT_NE(response, nullptr);
+ ASSERT_TRUE(FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response));
+}
+
+TEST(FlMethodCodecTest, DecodeResponseCodecError) {
+ g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+ g_autoptr(GBytes) message = text_to_message("codec-error");
+
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(FlMethodResponse) response =
+ fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+ EXPECT_TRUE(g_error_matches(error, FL_MESSAGE_CODEC_ERROR,
+ FL_MESSAGE_CODEC_ERROR_FAILED));
+ EXPECT_EQ(response, nullptr);
+}
+
+TEST(FlMethodCodecTest, DecodeResponseError) {
+ g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+ g_autoptr(GBytes) message = text_to_message("error");
+
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(FlMethodResponse) response =
+ fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+ EXPECT_EQ(error, nullptr);
+ EXPECT_NE(response, nullptr);
+ ASSERT_TRUE(FL_METHOD_ERROR_RESPONSE(response));
+ EXPECT_STREQ(
+ fl_method_error_response_get_code(FL_METHOD_ERROR_RESPONSE(response)),
+ "code");
+ EXPECT_STREQ(
+ fl_method_error_response_get_message(FL_METHOD_ERROR_RESPONSE(response)),
+ "message");
+ FlValue* details =
+ fl_method_error_response_get_details(FL_METHOD_ERROR_RESPONSE(response));
+ ASSERT_NE(details, nullptr);
+ ASSERT_EQ(fl_value_get_type(details), FL_VALUE_TYPE_INT);
+ EXPECT_EQ(fl_value_get_int(details), 42);
+}
diff --git a/shell/platform/linux/fl_method_response.cc b/shell/platform/linux/fl_method_response.cc
new file mode 100644
index 0000000..9082b9d
--- /dev/null
+++ b/shell/platform/linux/fl_method_response.cc
@@ -0,0 +1,178 @@
+// 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_method_response.h"
+
+#include <gmodule.h>
+
+G_DEFINE_QUARK(fl_method_response_error_quark, fl_method_response_error)
+
+struct _FlMethodSuccessResponse {
+ FlMethodResponse parent_instance;
+
+ FlValue* result;
+};
+
+struct _FlMethodErrorResponse {
+ FlMethodResponse parent_instance;
+
+ gchar* code;
+ gchar* message;
+ FlValue* details;
+};
+
+struct _FlMethodNotImplementedResponse {
+ FlMethodResponse parent_instance;
+};
+
+G_DEFINE_TYPE(FlMethodResponse, fl_method_response, G_TYPE_OBJECT)
+G_DEFINE_TYPE(FlMethodSuccessResponse,
+ fl_method_success_response,
+ fl_method_response_get_type())
+G_DEFINE_TYPE(FlMethodErrorResponse,
+ fl_method_error_response,
+ fl_method_response_get_type())
+G_DEFINE_TYPE(FlMethodNotImplementedResponse,
+ fl_method_not_implemented_response,
+ fl_method_response_get_type())
+
+static void fl_method_response_class_init(FlMethodResponseClass* klass) {}
+
+static void fl_method_response_init(FlMethodResponse* self) {}
+
+static void fl_method_success_response_dispose(GObject* object) {
+ FlMethodSuccessResponse* self = FL_METHOD_SUCCESS_RESPONSE(object);
+
+ g_clear_pointer(&self->result, fl_value_unref);
+
+ G_OBJECT_CLASS(fl_method_success_response_parent_class)->dispose(object);
+}
+
+static void fl_method_success_response_class_init(
+ FlMethodSuccessResponseClass* klass) {
+ G_OBJECT_CLASS(klass)->dispose = fl_method_success_response_dispose;
+}
+
+static void fl_method_success_response_init(FlMethodSuccessResponse* self) {}
+
+static void fl_method_error_response_dispose(GObject* object) {
+ FlMethodErrorResponse* self = FL_METHOD_ERROR_RESPONSE(object);
+
+ g_clear_pointer(&self->code, g_free);
+ g_clear_pointer(&self->message, g_free);
+ g_clear_pointer(&self->details, fl_value_unref);
+
+ G_OBJECT_CLASS(fl_method_error_response_parent_class)->dispose(object);
+}
+
+static void fl_method_error_response_class_init(
+ FlMethodErrorResponseClass* klass) {
+ G_OBJECT_CLASS(klass)->dispose = fl_method_error_response_dispose;
+}
+
+static void fl_method_error_response_init(FlMethodErrorResponse* self) {}
+
+static void fl_method_not_implemented_response_class_init(
+ FlMethodNotImplementedResponseClass* klass) {}
+
+static void fl_method_not_implemented_response_init(
+ FlMethodNotImplementedResponse* self) {}
+
+G_MODULE_EXPORT FlValue* fl_method_response_get_result(FlMethodResponse* self,
+ GError** error) {
+ if (FL_IS_METHOD_SUCCESS_RESPONSE(self))
+ return fl_method_success_response_get_result(
+ FL_METHOD_SUCCESS_RESPONSE(self));
+
+ if (FL_IS_METHOD_ERROR_RESPONSE(self)) {
+ const gchar* code =
+ fl_method_error_response_get_code(FL_METHOD_ERROR_RESPONSE(self));
+ const gchar* message =
+ fl_method_error_response_get_message(FL_METHOD_ERROR_RESPONSE(self));
+ FlValue* details =
+ fl_method_error_response_get_details(FL_METHOD_ERROR_RESPONSE(self));
+ g_autofree gchar* details_text = nullptr;
+ if (details != nullptr) {
+ // TODO(robert-ancell): Update this when we have fl_value_to_string()
+ }
+
+ g_autoptr(GString) error_message = g_string_new("");
+ g_string_append_printf(error_message, "Remote code returned error %s",
+ code);
+ if (message != nullptr)
+ g_string_append_printf(error_message, ": %s", message);
+ if (details_text != nullptr)
+ g_string_append_printf(error_message, " %s", details_text);
+ g_set_error_literal(error, FL_METHOD_RESPONSE_ERROR,
+ FL_METHOD_RESPONSE_ERROR_REMOTE_ERROR,
+ error_message->str);
+ return nullptr;
+ } else if (FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(self)) {
+ g_set_error(error, FL_METHOD_RESPONSE_ERROR,
+ FL_METHOD_RESPONSE_ERROR_NOT_IMPLEMENTED,
+ "Requested method is not implemented");
+ return nullptr;
+ } else {
+ g_set_error(error, FL_METHOD_RESPONSE_ERROR,
+ FL_METHOD_RESPONSE_ERROR_FAILED, "Unknown response type");
+ return nullptr;
+ }
+}
+
+G_MODULE_EXPORT FlMethodSuccessResponse* fl_method_success_response_new(
+ FlValue* result) {
+ FlMethodSuccessResponse* self = FL_METHOD_SUCCESS_RESPONSE(
+ g_object_new(fl_method_success_response_get_type(), nullptr));
+
+ if (result != nullptr)
+ self->result = fl_value_ref(result);
+
+ return self;
+}
+
+G_MODULE_EXPORT FlValue* fl_method_success_response_get_result(
+ FlMethodSuccessResponse* self) {
+ g_return_val_if_fail(FL_IS_METHOD_SUCCESS_RESPONSE(self), nullptr);
+ return self->result;
+}
+
+G_MODULE_EXPORT FlMethodErrorResponse* fl_method_error_response_new(
+ const gchar* code,
+ const gchar* message,
+ FlValue* details) {
+ g_return_val_if_fail(code != nullptr, nullptr);
+
+ FlMethodErrorResponse* self = FL_METHOD_ERROR_RESPONSE(
+ g_object_new(fl_method_error_response_get_type(), nullptr));
+
+ self->code = g_strdup(code);
+ self->message = g_strdup(message);
+ self->details = details != nullptr ? fl_value_ref(details) : nullptr;
+
+ return self;
+}
+
+G_MODULE_EXPORT const gchar* fl_method_error_response_get_code(
+ FlMethodErrorResponse* self) {
+ g_return_val_if_fail(FL_IS_METHOD_ERROR_RESPONSE(self), nullptr);
+ return self->code;
+}
+
+G_MODULE_EXPORT const gchar* fl_method_error_response_get_message(
+ FlMethodErrorResponse* self) {
+ g_return_val_if_fail(FL_IS_METHOD_ERROR_RESPONSE(self), nullptr);
+ return self->message;
+}
+
+G_MODULE_EXPORT FlValue* fl_method_error_response_get_details(
+ FlMethodErrorResponse* self) {
+ g_return_val_if_fail(FL_IS_METHOD_ERROR_RESPONSE(self), nullptr);
+ return self->details;
+}
+
+G_MODULE_EXPORT FlMethodNotImplementedResponse*
+fl_method_not_implemented_response_new() {
+ return FL_METHOD_NOT_IMPLEMENTED_RESPONSE(
+ g_object_new(fl_method_not_implemented_response_get_type(), nullptr));
+}
diff --git a/shell/platform/linux/fl_method_response_test.cc b/shell/platform/linux/fl_method_response_test.cc
new file mode 100644
index 0000000..0309a30
--- /dev/null
+++ b/shell/platform/linux/fl_method_response_test.cc
@@ -0,0 +1,96 @@
+// 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_method_response.h"
+#include "gtest/gtest.h"
+
+TEST(FlMethodResponseTest, Success) {
+ g_autoptr(FlValue) result = fl_value_new_int(42);
+ g_autoptr(FlMethodSuccessResponse) response =
+ fl_method_success_response_new(result);
+ g_autoptr(FlValue) expected = fl_value_new_int(42);
+ ASSERT_TRUE(fl_value_equal(fl_method_success_response_get_result(response),
+ expected));
+}
+
+TEST(FlMethodResponseTest, Error) {
+ g_autoptr(FlMethodErrorResponse) response =
+ fl_method_error_response_new("code", nullptr, nullptr);
+ EXPECT_STREQ(fl_method_error_response_get_code(response), "code");
+ EXPECT_EQ(fl_method_error_response_get_message(response), nullptr);
+ EXPECT_EQ(fl_method_error_response_get_details(response), nullptr);
+}
+
+TEST(FlMethodResponseTest, ErrorMessage) {
+ g_autoptr(FlMethodErrorResponse) response =
+ fl_method_error_response_new("code", "message", nullptr);
+ EXPECT_STREQ(fl_method_error_response_get_code(response), "code");
+ EXPECT_STREQ(fl_method_error_response_get_message(response), "message");
+ EXPECT_EQ(fl_method_error_response_get_details(response), nullptr);
+}
+
+TEST(FlMethodResponseTest, ErrorDetails) {
+ g_autoptr(FlValue) details = fl_value_new_int(42);
+ g_autoptr(FlMethodErrorResponse) response =
+ fl_method_error_response_new("code", nullptr, details);
+ EXPECT_STREQ(fl_method_error_response_get_code(response), "code");
+ EXPECT_EQ(fl_method_error_response_get_message(response), nullptr);
+ g_autoptr(FlValue) expected_details = fl_value_new_int(42);
+ EXPECT_TRUE(fl_value_equal(fl_method_error_response_get_details(response),
+ expected_details));
+}
+
+TEST(FlMethodResponseTest, ErrorMessageAndDetails) {
+ g_autoptr(FlValue) details = fl_value_new_int(42);
+ g_autoptr(FlMethodErrorResponse) response =
+ fl_method_error_response_new("code", "message", details);
+ EXPECT_STREQ(fl_method_error_response_get_code(response), "code");
+ EXPECT_STREQ(fl_method_error_response_get_message(response), "message");
+ g_autoptr(FlValue) expected_details = fl_value_new_int(42);
+ EXPECT_TRUE(fl_value_equal(fl_method_error_response_get_details(response),
+ expected_details));
+}
+
+TEST(FlMethodResponseTest, NotImplemented) {
+ g_autoptr(FlMethodNotImplementedResponse) response =
+ fl_method_not_implemented_response_new();
+ // Trivial check to stop the compiler deciding that 'response' is an unused
+ // variable
+ EXPECT_TRUE(FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response));
+}
+
+TEST(FlMethodResponseTest, SuccessGetResult) {
+ g_autoptr(FlValue) r = fl_value_new_int(42);
+ g_autoptr(FlMethodSuccessResponse) response =
+ fl_method_success_response_new(r);
+ g_autoptr(GError) error = nullptr;
+ FlValue* result =
+ fl_method_response_get_result(FL_METHOD_RESPONSE(response), &error);
+ ASSERT_NE(result, nullptr);
+ EXPECT_EQ(error, nullptr);
+ g_autoptr(FlValue) expected = fl_value_new_int(42);
+ ASSERT_TRUE(fl_value_equal(result, expected));
+}
+
+TEST(FlMethodResponseTest, ErrorGetResult) {
+ g_autoptr(FlMethodErrorResponse) response =
+ fl_method_error_response_new("code", nullptr, nullptr);
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(FlValue) result =
+ fl_method_response_get_result(FL_METHOD_RESPONSE(response), &error);
+ EXPECT_EQ(result, nullptr);
+ EXPECT_TRUE(g_error_matches(error, FL_METHOD_RESPONSE_ERROR,
+ FL_METHOD_RESPONSE_ERROR_REMOTE_ERROR));
+}
+
+TEST(FlMethodResponseTest, NotImplementedGetResult) {
+ g_autoptr(FlMethodNotImplementedResponse) response =
+ fl_method_not_implemented_response_new();
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(FlValue) result =
+ fl_method_response_get_result(FL_METHOD_RESPONSE(response), &error);
+ EXPECT_EQ(result, nullptr);
+ EXPECT_TRUE(g_error_matches(error, FL_METHOD_RESPONSE_ERROR,
+ FL_METHOD_RESPONSE_ERROR_NOT_IMPLEMENTED));
+}
diff --git a/shell/platform/linux/fl_standard_method_codec.cc b/shell/platform/linux/fl_standard_method_codec.cc
new file mode 100644
index 0000000..429b35a
--- /dev/null
+++ b/shell/platform/linux/fl_standard_method_codec.cc
@@ -0,0 +1,238 @@
+// 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 "flutter/shell/platform/linux/fl_standard_message_codec_private.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h"
+
+#include <gmodule.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_code
+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_code
+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_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<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));
+}
diff --git a/shell/platform/linux/fl_standard_method_codec_test.cc b/shell/platform/linux/fl_standard_method_codec_test.cc
new file mode 100644
index 0000000..ef0b554
--- /dev/null
+++ b/shell/platform/linux/fl_standard_method_codec_test.cc
@@ -0,0 +1,375 @@
+// 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 "flutter/shell/platform/linux/fl_method_codec_private.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_message_codec.h"
+#include "flutter/shell/platform/linux/testing/fl_test.h"
+#include "gtest/gtest.h"
+
+// NOTE(robert-ancell) These test cases assumes a little-endian architecture.
+// These tests will need to be updated if tested on a big endian architecture.
+
+// Encodes a method call using StandardMethodCodec to a hex string.
+static gchar* encode_method_call(const gchar* name, FlValue* args) {
+ g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
+ FL_METHOD_CODEC(codec), name, args, &error);
+ EXPECT_NE(message, nullptr);
+ EXPECT_EQ(error, nullptr);
+
+ return bytes_to_hex_string(message);
+}
+
+// Encodes a success envelope response using StandardMethodCodec to a hex
+// string.
+static gchar* encode_success_envelope(FlValue* result) {
+ g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(GBytes) message = fl_method_codec_encode_success_envelope(
+ FL_METHOD_CODEC(codec), result, &error);
+ EXPECT_NE(message, nullptr);
+ EXPECT_EQ(error, nullptr);
+
+ return bytes_to_hex_string(message);
+}
+
+// Encodes a error envelope response using StandardMethodCodec to a hex string.
+static gchar* encode_error_envelope(const gchar* error_code,
+ const gchar* error_message,
+ FlValue* details) {
+ g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(GBytes) message = fl_method_codec_encode_error_envelope(
+ FL_METHOD_CODEC(codec), error_code, error_message, details, &error);
+ EXPECT_NE(message, nullptr);
+ EXPECT_EQ(error, nullptr);
+
+ return bytes_to_hex_string(message);
+}
+
+// Decodes a method call using StandardMethodCodec with a hex string.
+static void decode_method_call(const char* hex_string,
+ gchar** name,
+ FlValue** args) {
+ g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
+ g_autoptr(GBytes) message = hex_string_to_bytes(hex_string);
+ g_autoptr(GError) error = nullptr;
+ gboolean result = fl_method_codec_decode_method_call(
+ FL_METHOD_CODEC(codec), message, name, args, &error);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(error, nullptr);
+}
+
+// Decodes a method call using StandardMethodCodec. Expect the given error.
+static void decode_error_method_call(const char* hex_string,
+ GQuark domain,
+ gint code) {
+ g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
+ g_autoptr(GBytes) message = hex_string_to_bytes(hex_string);
+ g_autoptr(GError) error = nullptr;
+ g_autofree gchar* name = nullptr;
+ g_autoptr(FlValue) args = nullptr;
+ gboolean result = fl_method_codec_decode_method_call(
+ FL_METHOD_CODEC(codec), message, &name, &args, &error);
+ EXPECT_FALSE(result);
+ EXPECT_EQ(name, nullptr);
+ EXPECT_EQ(args, nullptr);
+ EXPECT_TRUE(g_error_matches(error, domain, code));
+}
+
+// Decodes a response using StandardMethodCodec. Expect the response is a
+// result.
+static void decode_response_with_success(const char* hex_string,
+ FlValue* result) {
+ g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
+ g_autoptr(GBytes) message = hex_string_to_bytes(hex_string);
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(FlMethodResponse) response =
+ fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+ ASSERT_NE(response, nullptr);
+ EXPECT_EQ(error, nullptr);
+ EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
+ EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result(
+ FL_METHOD_SUCCESS_RESPONSE(response)),
+ result));
+}
+
+// Decodes a response using StandardMethodCodec. Expect the response contains
+// the given error.
+static void decode_response_with_error(const char* hex_string,
+ const gchar* code,
+ const gchar* error_message,
+ FlValue* details) {
+ g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
+ g_autoptr(GBytes) message = hex_string_to_bytes(hex_string);
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(FlMethodResponse) response =
+ fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+ ASSERT_NE(response, nullptr);
+ EXPECT_EQ(error, nullptr);
+ EXPECT_TRUE(FL_IS_METHOD_ERROR_RESPONSE(response));
+ EXPECT_STREQ(
+ fl_method_error_response_get_code(FL_METHOD_ERROR_RESPONSE(response)),
+ code);
+ if (error_message == nullptr)
+ EXPECT_EQ(fl_method_error_response_get_message(
+ FL_METHOD_ERROR_RESPONSE(response)),
+ nullptr);
+ else
+ EXPECT_STREQ(fl_method_error_response_get_message(
+ FL_METHOD_ERROR_RESPONSE(response)),
+ error_message);
+ if (details == nullptr)
+ EXPECT_EQ(fl_method_error_response_get_details(
+ FL_METHOD_ERROR_RESPONSE(response)),
+ nullptr);
+ else
+ EXPECT_TRUE(fl_value_equal(fl_method_error_response_get_details(
+ FL_METHOD_ERROR_RESPONSE(response)),
+ details));
+}
+
+static void decode_error_response(const char* hex_string,
+ GQuark domain,
+ gint code) {
+ g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
+ g_autoptr(GBytes) message = hex_string_to_bytes(hex_string);
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(FlMethodResponse) response =
+ fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+ EXPECT_EQ(response, nullptr);
+ EXPECT_TRUE(g_error_matches(error, domain, code));
+}
+
+TEST(FlStandardMethodCodecTest, EncodeMethodCallNullptrArgs) {
+ g_autofree gchar* hex_string = encode_method_call("hello", nullptr);
+ EXPECT_STREQ(hex_string, "070568656c6c6f00");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeMethodCallNullArgs) {
+ g_autoptr(FlValue) value = fl_value_new_null();
+ g_autofree gchar* hex_string = encode_method_call("hello", value);
+ EXPECT_STREQ(hex_string, "070568656c6c6f00");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeMethodCallStringArgs) {
+ g_autoptr(FlValue) args = fl_value_new_string("world");
+ g_autofree gchar* hex_string = encode_method_call("hello", args);
+ EXPECT_STREQ(hex_string, "070568656c6c6f0705776f726c64");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeMethodCallListArgs) {
+ g_autoptr(FlValue) args = fl_value_new_list();
+ fl_value_append_take(args, fl_value_new_string("count"));
+ fl_value_append_take(args, fl_value_new_int(42));
+ g_autofree gchar* hex_string = encode_method_call("hello", args);
+ EXPECT_STREQ(hex_string, "070568656c6c6f0c020705636f756e74032a000000");
+}
+
+TEST(FlStandardMethodCodecTest, DecodeMethodCallNullArgs) {
+ g_autofree gchar* name = nullptr;
+ g_autoptr(FlValue) args = nullptr;
+ decode_method_call("070568656c6c6f00", &name, &args);
+ EXPECT_STREQ(name, "hello");
+ ASSERT_EQ(fl_value_get_type(args), FL_VALUE_TYPE_NULL);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeMethodCallStringArgs) {
+ g_autofree gchar* name = nullptr;
+ g_autoptr(FlValue) args = nullptr;
+ decode_method_call("070568656c6c6f0705776f726c64", &name, &args);
+ EXPECT_STREQ(name, "hello");
+ ASSERT_EQ(fl_value_get_type(args), FL_VALUE_TYPE_STRING);
+ EXPECT_STREQ(fl_value_get_string(args), "world");
+}
+
+TEST(FlStandardMethodCodecTest, DecodeMethodCallListArgs) {
+ g_autofree gchar* name = nullptr;
+ g_autoptr(FlValue) args = nullptr;
+ decode_method_call("070568656c6c6f0c020705636f756e74032a000000", &name,
+ &args);
+ EXPECT_STREQ(name, "hello");
+ ASSERT_EQ(fl_value_get_type(args), FL_VALUE_TYPE_LIST);
+ EXPECT_EQ(fl_value_get_length(args), static_cast<size_t>(2));
+
+ FlValue* arg0 = fl_value_get_list_value(args, 0);
+ ASSERT_EQ(fl_value_get_type(arg0), FL_VALUE_TYPE_STRING);
+ EXPECT_STREQ(fl_value_get_string(arg0), "count");
+
+ FlValue* arg1 = fl_value_get_list_value(args, 1);
+ ASSERT_EQ(fl_value_get_type(arg1), FL_VALUE_TYPE_INT);
+ EXPECT_EQ(fl_value_get_int(arg1), 42);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeMethodCallNoData) {
+ decode_error_method_call("", FL_MESSAGE_CODEC_ERROR,
+ FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeMethodCallNullMethodName) {
+ decode_error_method_call("000000", FL_MESSAGE_CODEC_ERROR,
+ FL_MESSAGE_CODEC_ERROR_FAILED);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeMethodCallMissingArgs) {
+ decode_error_method_call("070568656c6c6f", FL_MESSAGE_CODEC_ERROR,
+ FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA);
+}
+
+TEST(FlStandardMethodCodecTest, EncodeSuccessEnvelopeNullptr) {
+ g_autofree gchar* hex_string = encode_success_envelope(nullptr);
+ EXPECT_STREQ(hex_string, "0000");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeSuccessEnvelopeNull) {
+ g_autoptr(FlValue) result = fl_value_new_null();
+ g_autofree gchar* hex_string = encode_success_envelope(result);
+ EXPECT_STREQ(hex_string, "0000");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeSuccessEnvelopeString) {
+ g_autoptr(FlValue) result = fl_value_new_string("hello");
+ g_autofree gchar* hex_string = encode_success_envelope(result);
+ EXPECT_STREQ(hex_string, "00070568656c6c6f");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeSuccessEnvelopeList) {
+ g_autoptr(FlValue) result = fl_value_new_list();
+ fl_value_append_take(result, fl_value_new_string("count"));
+ fl_value_append_take(result, fl_value_new_int(42));
+ g_autofree gchar* hex_string = encode_success_envelope(result);
+ EXPECT_STREQ(hex_string, "000c020705636f756e74032a000000");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeErrorEnvelopeEmptyCode) {
+ g_autofree gchar* hex_string = encode_error_envelope("", nullptr, nullptr);
+ EXPECT_STREQ(hex_string, "0107000000");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeErrorEnvelopeNonMessageOrDetails) {
+ g_autofree gchar* hex_string =
+ encode_error_envelope("error", nullptr, nullptr);
+ EXPECT_STREQ(hex_string, "0107056572726f720000");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeErrorEnvelopeMessage) {
+ g_autofree gchar* hex_string =
+ encode_error_envelope("error", "message", nullptr);
+ EXPECT_STREQ(hex_string, "0107056572726f7207076d65737361676500");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeErrorEnvelopeDetails) {
+ g_autoptr(FlValue) details = fl_value_new_list();
+ fl_value_append_take(details, fl_value_new_string("count"));
+ fl_value_append_take(details, fl_value_new_int(42));
+ g_autofree gchar* hex_string =
+ encode_error_envelope("error", nullptr, details);
+ EXPECT_STREQ(hex_string, "0107056572726f72000c020705636f756e74032a000000");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeErrorEnvelopeMessageAndDetails) {
+ g_autoptr(FlValue) details = fl_value_new_list();
+ fl_value_append_take(details, fl_value_new_string("count"));
+ fl_value_append_take(details, fl_value_new_int(42));
+ g_autofree gchar* hex_string =
+ encode_error_envelope("error", "message", details);
+ EXPECT_STREQ(
+ hex_string,
+ "0107056572726f7207076d6573736167650c020705636f756e74032a000000");
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseSuccessNull) {
+ g_autoptr(FlValue) result = fl_value_new_null();
+ decode_response_with_success("0000", result);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseSuccessString) {
+ g_autoptr(FlValue) result = fl_value_new_string("hello");
+ decode_response_with_success("00070568656c6c6f", result);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseSuccessList) {
+ g_autoptr(FlValue) result = fl_value_new_list();
+ fl_value_append_take(result, fl_value_new_string("count"));
+ fl_value_append_take(result, fl_value_new_int(42));
+ decode_response_with_success("000c020705636f756e74032a000000", result);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseErrorEmptyCode) {
+ decode_response_with_error("0107000000", "", nullptr, nullptr);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseErrorNoMessageOrDetails) {
+ decode_response_with_error("0107056572726f720000", "error", nullptr, nullptr);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseErrorMessage) {
+ decode_response_with_error("0107056572726f7207076d65737361676500", "error",
+ "message", nullptr);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseErrorDetails) {
+ g_autoptr(FlValue) details = fl_value_new_list();
+ fl_value_append_take(details, fl_value_new_string("count"));
+ fl_value_append_take(details, fl_value_new_int(42));
+ decode_response_with_error("0107056572726f72000c020705636f756e74032a000000",
+ "error", nullptr, details);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseErrorMessageAndDetails) {
+ g_autoptr(FlValue) details = fl_value_new_list();
+ fl_value_append_take(details, fl_value_new_string("count"));
+ fl_value_append_take(details, fl_value_new_int(42));
+ decode_response_with_error(
+ "0107056572726f7207076d6573736167650c020705636f756e74032a000000", "error",
+ "message", details);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseSuccessNoData) {
+ decode_error_response("00", FL_MESSAGE_CODEC_ERROR,
+ FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseSuccessExtraData) {
+ decode_error_response("000000", FL_MESSAGE_CODEC_ERROR,
+ FL_MESSAGE_CODEC_ERROR_FAILED);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseErrorNoData) {
+ decode_error_response("01", FL_MESSAGE_CODEC_ERROR,
+ FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseErrorMissingMessageAndDetails) {
+ decode_error_response("0107056572726f72", FL_MESSAGE_CODEC_ERROR,
+ FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseErrorMissingDetails) {
+ decode_error_response("0107056572726f7200", FL_MESSAGE_CODEC_ERROR,
+ FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseErrorExtraData) {
+ decode_error_response("0107056572726f72000000", FL_MESSAGE_CODEC_ERROR,
+ FL_MESSAGE_CODEC_ERROR_FAILED);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseNotImplemented) {
+ g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
+ g_autoptr(GBytes) message = g_bytes_new(nullptr, 0);
+ g_autoptr(GError) error = nullptr;
+ g_autoptr(FlMethodResponse) response =
+ fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+ ASSERT_NE(response, nullptr);
+ EXPECT_EQ(error, nullptr);
+ EXPECT_TRUE(FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response));
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseUnknownEnvelope) {
+ decode_error_response("02", FL_MESSAGE_CODEC_ERROR,
+ FL_MESSAGE_CODEC_ERROR_FAILED);
+}
diff --git a/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h b/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h
new file mode 100644
index 0000000..70ddf79
--- /dev/null
+++ b/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h
@@ -0,0 +1,44 @@
+// 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.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_JSON_METHOD_CODEC_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_JSON_METHOD_CODEC_H_
+
+#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION)
+#error "Only <flutter_linux/flutter_linux.h> can be included directly."
+#endif
+
+#include "fl_method_codec.h"
+
+G_BEGIN_DECLS
+
+G_DECLARE_FINAL_TYPE(FlJsonMethodCodec,
+ fl_json_method_codec,
+ FL,
+ JSON_METHOD_CODEC,
+ FlMethodCodec)
+
+/**
+ * FlJsonMethodCodec:
+ *
+ * #FlJsonMessageCodec is an #FlMethodCodec that implements method calls using
+ * the Flutter JSON message encoding. It should be used with an
+ * #FlMethodChannel.
+ *
+ * #FlJsonMethodCodec matches the JSONMethodCodec class in the Flutter services
+ * library.
+ */
+
+/**
+ * fl_json_method_codec_new:
+ *
+ * Creates an #FlJsonMethodCodec.
+ *
+ * Returns: a new #FlJsonMethodCodec.
+ */
+FlJsonMethodCodec* fl_json_method_codec_new();
+
+G_END_DECLS
+
+#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_JSON_METHOD_CODEC_H_
diff --git a/shell/platform/linux/public/flutter_linux/fl_method_call.h b/shell/platform/linux/public/flutter_linux/fl_method_call.h
new file mode 100644
index 0000000..5a5ebc7
--- /dev/null
+++ b/shell/platform/linux/public/flutter_linux/fl_method_call.h
@@ -0,0 +1,115 @@
+// 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.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CALL_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CALL_H_
+
+#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION)
+#error "Only <flutter_linux/flutter_linux.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+
+#include "fl_method_response.h"
+#include "fl_value.h"
+
+G_BEGIN_DECLS
+
+G_DECLARE_FINAL_TYPE(FlMethodCall, fl_method_call, FL, METHOD_CALL, GObject)
+
+/**
+ * FlMethodCall:
+ *
+ * #FlMethodCall represents and incoming method call as returned by an
+ * #FlMethodChannel.
+ */
+
+/**
+ * fl_method_call_get_name:
+ * @method_call: an #FlMethodCall.
+ *
+ * Gets the name of the method call.
+ *
+ * Returns: a method name.
+ */
+const gchar* fl_method_call_get_name(FlMethodCall* method_call);
+
+/**
+ * fl_method_call_get_args:
+ * @method_call: an #FlMethodCall.
+ *
+ * Gets the arguments passed to the method.
+ *
+ * Returns: an #FlValue.
+ */
+FlValue* fl_method_call_get_args(FlMethodCall* method_call);
+
+/**
+ * fl_method_call_respond:
+ * @method_call: an #FlMethodCall.
+ * @response: an #FlMethodResponse.
+ * @error: (allow-none): #GError location to store the error occurring, or %NULL
+ * to ignore.
+ *
+ * Responds to a method call.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean fl_method_call_respond(FlMethodCall* method_call,
+ FlMethodResponse* response,
+ GError** error);
+
+/**
+ * fl_method_call_respond_success:
+ * @method_call: an #FlMethodCall.
+ * @result: (allow-none): value to respond with, must match what the
+ * #FlMethodCodec supports.
+ * @error: (allow-none): #GError location to store the error occurring, or %NULL
+ * to ignore.
+ *
+ * Convenience method that responds to method call with
+ * #FlMethodSuccessResponse.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean fl_method_call_respond_success(FlMethodCall* method_call,
+ FlValue* result,
+ GError** error);
+
+/**
+ * fl_method_call_respond_error:
+ * @method_call: an #FlMethodCall.
+ * @code: error code.
+ * @message: (allow-none): error message.
+ * @details: (allow-none): details for the error.
+ * @error: (allow-none): #GError location to store the error occurring, or %NULL
+ * to ignore.
+ *
+ * Convenience method that responds to method call with #FlMethodErrorResponse.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean fl_method_call_respond_error(FlMethodCall* method_call,
+ const gchar* code,
+ const gchar* message,
+ FlValue* details,
+ GError** error);
+
+/**
+ * fl_method_call_respond_not_implemented:
+ * @method_call: an #FlMethodCall.
+ * @error: (allow-none): #GError location to store the error occurring, or %NULL
+ * to ignore.
+ *
+ * Convenience method that responds to method call with
+ * #FlMethodNotImplementedResponse.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean fl_method_call_respond_not_implemented(FlMethodCall* method_call,
+ GError** error);
+
+G_END_DECLS
+
+#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CALL_H_
diff --git a/shell/platform/linux/public/flutter_linux/fl_method_channel.h b/shell/platform/linux/public/flutter_linux/fl_method_channel.h
new file mode 100644
index 0000000..0ef04ff
--- /dev/null
+++ b/shell/platform/linux/public/flutter_linux/fl_method_channel.h
@@ -0,0 +1,182 @@
+// 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.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CHANNEL_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CHANNEL_H_
+
+#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION)
+#error "Only <flutter_linux/flutter_linux.h> can be included directly."
+#endif
+
+#include <gio/gio.h>
+#include <glib-object.h>
+
+#include "fl_binary_messenger.h"
+#include "fl_method_call.h"
+#include "fl_method_codec.h"
+#include "fl_method_response.h"
+
+G_BEGIN_DECLS
+
+G_DECLARE_FINAL_TYPE(FlMethodChannel,
+ fl_method_channel,
+ FL,
+ METHOD_CHANNEL,
+ GObject)
+
+/**
+ * FlMethodChannel:
+ *
+ * #FlMethodChannel is an object that allows method calls to and from Dart code.
+ *
+ * The following example shows how to call and handle methods on a channel.
+ * See #FlMethodResponse for how to handle errors in more detail.
+ *
+ * |[<!-- language="C" -->
+ * static FlMethodChannel *channel = NULL;
+ *
+ * static void method_call_cb (FlMethodChannel* channel,
+ * FlMethodCall* method_call,
+ * gpointer user_data) {
+ * g_autoptr(FlMethodResponse) response = NULL;
+ * if (strcmp (fl_method_call_get_name (method_call), "Foo.bar") == 0) {
+ * g_autoptr(GError) bar_error = NULL;
+ * g_autoptr(FlValue) result =
+ * do_bar (fl_method_call_get_args (method_call), &bar_error);
+ * if (result == NULL)
+ * response =
+ * FL_METHOD_RESPONSE (fl_method_error_response_new ("bar error",
+ * bar_error->message));
+ * else
+ * response = FL_METHOD_RESPONSE (fl_method_success_response_new
+ * (result)); } else response = FL_METHOD_RESPONSE
+ * (fl_method_not_implemented_response_new ());
+ *
+ * g_autoptr(GError) error = NULL;
+ * if (!fl_method_call_respond(method_call, response))
+ * g_warning ("Failed to send response: %s", error->message);
+ * }
+ *
+ * static void method_response_cb(GObject *object,
+ * GAsyncResult *result,
+ * gpointer user_data) {
+ * g_autoptr(GError) error = NULL;
+ * g_autoptr(FlMethodResponse) response =
+ * fl_method_channel_invoke_method_finish (FL_METHOD_CODEC (object), result,
+ * &error);
+ * if (response == NULL) {
+ * g_warning ("Failed to call method: %s", error->message);
+ * return;
+ * }
+ *
+ * g_autoptr(FlValue) value =
+ * fl_method_response_get_result (response, &error);
+ * if (response == NULL) {
+ * g_warning ("Method returned error: %s", error->message);
+ * return;
+ * }
+ *
+ * use_result (value);
+ * }
+ *
+ * static void call_method () {
+ * g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new ();
+ * channel =
+ * fl_method_channel_new(messenger, "flutter/foo", FL_METHOD_CODEC (codec));
+ * fl_method_channel_set_method_call_handler (channel, method_call_cb, NULL);
+ *
+ * g_autoptr(FlValue) args = fl_value_new_string ("Hello World");
+ * fl_method_channel_invoke_method (channel, "Foo.foo", args,
+ * cancellable, method_response_cb, NULL);
+ * }
+ * ]|
+ *
+ * #FlMethodChannel matches the MethodChannel class in the Flutter services
+ * library.
+ */
+
+/**
+ * FlMethodChannelMethodCallHandler:
+ * @channel: an #FlMethodChannel.
+ * @method_call: an #FlMethodCall.
+ * @user_data: (closure): data provided when registering this handler.
+ *
+ * Function called when a method call is received. Respond to the method call
+ * with fl_method_call_respond(). If the response is not occurring in this
+ * callback take a reference to @method_call and release that once it has been
+ * responded to.Failing to respond before the last reference to @method_call is
+ * dropped is a programming error.
+ */
+typedef void (*FlMethodChannelMethodCallHandler)(FlMethodChannel* channel,
+ FlMethodCall* method_call,
+ gpointer user_data);
+
+/**
+ * fl_method_channel_new:
+ * @messenger: an #FlBinaryMessenger.
+ * @name: a channel name.
+ * @codec: the method codec.
+ *
+ * Creates a new method channel. @codec must match the codec used on the Dart
+ * end of the channel.
+ *
+ * Returns: a new #FlMethodChannel.
+ */
+FlMethodChannel* fl_method_channel_new(FlBinaryMessenger* messenger,
+ const gchar* name,
+ FlMethodCodec* codec);
+
+/**
+ * fl_method_channel_set_method_call_handler:
+ * @channel: an #FlMethodChannel.
+ * @handler: function to call when a method call is received on this channel.
+ * @user_data: (closure): user data to pass to @handler.
+ *
+ * Sets the function called when a method is called. Call
+ * fl_method_call_respond() when the method completes.
+ */
+void fl_method_channel_set_method_call_handler(
+ FlMethodChannel* channel,
+ FlMethodChannelMethodCallHandler handler,
+ gpointer user_data);
+
+/**
+ * fl_method_channel_invoke_method:
+ * @channel: an #FlMethodChannel.
+ * @method: the method to call.
+ * @args: (allow-none): arguments to the method, must match what the
+ * #FlMethodCodec supports.
+ * @cancellable: (allow-none): a #GCancellable or %NULL.
+ * @callback: (scope async): (allow-none): a #GAsyncReadyCallback to call when
+ * the request is satisfied or %NULL to ignore the response.
+ * @user_data: (closure): user data to pass to @callback.
+ *
+ * Calls a method on this channel.
+ */
+void fl_method_channel_invoke_method(FlMethodChannel* channel,
+ const gchar* method,
+ FlValue* args,
+ GCancellable* cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+/**
+ * fl_method_channel_invoke_method_finish:
+ * @channel: an #FlMethodChannel.
+ * @result: #GAsyncResult.
+ * @error: (allow-none): #GError location to store the error occurring, or %NULL
+ * to ignore.
+ *
+ * Completes request started with fl_method_channel_invoke_method().
+ *
+ * Returns: (transfer full): an #FlMethodResponse or %NULL on error.
+ */
+FlMethodResponse* fl_method_channel_invoke_method_finish(
+ FlMethodChannel* channel,
+ GAsyncResult* result,
+ GError** error);
+
+G_END_DECLS
+
+#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CHANNEL_H_
diff --git a/shell/platform/linux/public/flutter_linux/fl_method_codec.h b/shell/platform/linux/public/flutter_linux/fl_method_codec.h
new file mode 100644
index 0000000..97bf8e9
--- /dev/null
+++ b/shell/platform/linux/public/flutter_linux/fl_method_codec.h
@@ -0,0 +1,131 @@
+// 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.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CODEC_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CODEC_H_
+
+#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION)
+#error "Only <flutter_linux/flutter_linux.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+
+#include "fl_method_response.h"
+#include "fl_value.h"
+
+G_BEGIN_DECLS
+
+G_DECLARE_DERIVABLE_TYPE(FlMethodCodec,
+ fl_method_codec,
+ FL,
+ METHOD_CODEC,
+ GObject)
+
+/**
+ * FlMethodCodec:
+ *
+ * #FlMethodCodec is an abstract class that encodes and decodes method calls on
+ * a platform channel. Override this class to implement an encoding.
+ *
+ * #FlMethodCodec matches the MethodCodec class in the Flutter services
+ * library.
+ */
+
+struct _FlMethodCodecClass {
+ GObjectClass parent_class;
+
+ /**
+ * FlMethodCodec::encode_method_call:
+ * @codec: an #FlMethodCodec.
+ * @name: method name.
+ * @args: (allow-none): method arguments, or %NULL.
+ * @error: (allow-none): #GError location to store the error occurring, or
+ * %NULL.
+ *
+ * Encodes a method call.
+ *
+ * Returns: (transfer full): a binary encoding of this method call or %NULL if
+ * not able to encode.
+ */
+ GBytes* (*encode_method_call)(FlMethodCodec* codec,
+ const gchar* name,
+ FlValue* args,
+ GError** error);
+
+ /**
+ * FlMethodCodec::decode_method_call:
+ * @codec: an #FlMethodCodec
+ * @message: message to decode.
+ * @name: (transfer full): location to write method name or %NULL if not
+ * required
+ * @args: (transfer full): location to write method arguments, or %NULL if not
+ * required
+ * @error: (allow-none): #GError location to store the error occurring, or
+ * %NULL
+ *
+ * Decodes a method call.
+ *
+ * Returns: %TRUE if successfully decoded.
+ */
+ gboolean (*decode_method_call)(FlMethodCodec* codec,
+ GBytes* message,
+ gchar** name,
+ FlValue** args,
+ GError** error);
+
+ /**
+ * FlMethodCodec::encode_success_envelope:
+ * @codec: an #FlMethodCodec.
+ * @result: (allow-none): method result, or %NULL.
+ * @error: (allow-none): #GError location to store the error occurring, or
+ * %NULL.
+ *
+ * Encodes a successful response to a method call.
+ *
+ * Returns: (transfer full): a binary encoding of this response or %NULL if
+ * not able to encode.
+ */
+ GBytes* (*encode_success_envelope)(FlMethodCodec* codec,
+ FlValue* result,
+ GError** error);
+
+ /**
+ * FlMethodCodec::encode_error_envelope:
+ * @codec: an #FlMethodCodec.
+ * @code: an error code.
+ * @message: (allow-none): an error message, or %NULL.
+ * @details: (allow-none): error details, or %NULL.
+ * @error: (allow-none): #GError location to store the error occurring, or
+ * %NULL.
+ *
+ * Encodes an error response to a method call.
+ *
+ * Returns: (transfer full): a binary encoding of this response or %NULL if
+ * not able to encode.
+ */
+ GBytes* (*encode_error_envelope)(FlMethodCodec* codec,
+ const gchar* code,
+ const gchar* message,
+ FlValue* details,
+ GError** error);
+
+ /**
+ * FlMethodCodec::decode_response:
+ * @codec: an #FlMethodCodec.
+ * @message: message to decode.
+ * @error: (allow-none): #GError location to store the error occurring, or
+ * %NULL.
+ *
+ * Decodes a response to a method call.
+ *
+ * Returns: a new #FlMethodResponse or %NULL on error.
+ */
+ FlMethodResponse* (*decode_response)(FlMethodCodec* codec,
+ GBytes* message,
+ GError** error);
+};
+
+G_END_DECLS
+
+#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CODEC_H_
diff --git a/shell/platform/linux/public/flutter_linux/fl_method_response.h b/shell/platform/linux/public/flutter_linux/fl_method_response.h
new file mode 100644
index 0000000..949cddc
--- /dev/null
+++ b/shell/platform/linux/public/flutter_linux/fl_method_response.h
@@ -0,0 +1,212 @@
+// 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.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_RESPONSE_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_RESPONSE_H_
+
+#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION)
+#error "Only <flutter_linux/flutter_linux.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+
+#include "fl_value.h"
+
+G_BEGIN_DECLS
+
+/**
+ * FlMethodResponseError:
+ * @FL_METHOD_RESPONSE_ERROR_FAILED: Call failed due to an unspecified error.
+ * @FL_METHOD_RESPONSE_ERROR_REMOTE_ERROR: An error was returned by the other
+ * side of the channel.
+ * @FL_METHOD_RESPONSE_ERROR_NOT_IMPLEMENTED: The requested method is not
+ * implemented.
+ *
+ * Errors set by `fl_method_response_get_result` when the method call response
+ * is not #FlMethodSuccessResponse.
+ */
+#define FL_METHOD_RESPONSE_ERROR fl_method_response_error_quark()
+
+typedef enum {
+ FL_METHOD_RESPONSE_ERROR_FAILED,
+ FL_METHOD_RESPONSE_ERROR_REMOTE_ERROR,
+ FL_METHOD_RESPONSE_ERROR_NOT_IMPLEMENTED,
+} FlMethodResponseError;
+
+GQuark fl_method_response_error_quark(void) G_GNUC_CONST;
+
+G_DECLARE_DERIVABLE_TYPE(FlMethodResponse,
+ fl_method_response,
+ FL,
+ METHOD_RESPONSE,
+ GObject)
+
+struct _FlMethodResponseClass {
+ GObjectClass parent_class;
+};
+
+G_DECLARE_FINAL_TYPE(FlMethodSuccessResponse,
+ fl_method_success_response,
+ FL,
+ METHOD_SUCCESS_RESPONSE,
+ FlMethodResponse)
+
+G_DECLARE_FINAL_TYPE(FlMethodErrorResponse,
+ fl_method_error_response,
+ FL,
+ METHOD_ERROR_RESPONSE,
+ FlMethodResponse)
+
+G_DECLARE_FINAL_TYPE(FlMethodNotImplementedResponse,
+ fl_method_not_implemented_response,
+ FL,
+ METHOD_NOT_IMPLEMENTED_RESPONSE,
+ FlMethodResponse)
+
+/**
+ * FlMethodResponse:
+ *
+ * #FlMethodResponse contains the information returned when an #FlMethodChannel
+ * method call returns. If you expect the method call to be successful use
+ * fl_method_response_get_result(). If you want to handle error cases then you
+ * should use code like:
+ *
+ * |[<!-- language="C" -->
+ * if (FL_IS_METHOD_SUCCESS_RESPONSE (response)) {
+ * FlValue *result =
+ * fl_method_success_response_get_result(
+ * FL_METHOD_SUCCESS_RESPONSE (response));
+ * handle_result (result);
+ * } else if (FL_IS_METHOD_ERROR_RESPONSE (response)) {
+ * FlMethodErrorResponse *error_response =
+ * FL_METHOD_ERROR_RESPONSE (response);
+ * handle_error (fl_method_error_response_get_code (error_response),
+ * fl_method_error_response_get_message (error_response),
+ * fl_method_error_response_get_details (error_response));
+ * }
+ * else if (FL_IS_METHOD_ERROR_RESPONSE (response)) {
+ * handle_not_implemented ();
+ * }
+ * }
+ * ]|
+ */
+
+/**
+ * FlMethodSuccessResponse:
+ *
+ * #FlMethodSuccessResponse is the #FlMethodResponse returned when a method call
+ * has successfully completed. The result of the method call is obtained using
+ * `fl_method_success_response_get_result`.
+ */
+
+/**
+ * FlMethodErrorResponse:
+ *
+ * #FlMethodErrorResponse is the #FlMethodResponse returned when a method call
+ * results in an error. The error details are obtained using
+ * `fl_method_error_response_get_code`, `fl_method_error_response_get_message`
+ * and `fl_method_error_response_get_details`.
+ */
+
+/**
+ * FlMethodNotImplementedResponse:
+ *
+ * #FlMethodNotImplementedResponse is the #FlMethodResponse returned when a
+ * method call is not implemented.
+ */
+
+/**
+ * fl_method_response_get_result:
+ * @response: an #FlMethodResponse.
+ * @error: (allow-none): #GError location to store the error occurring, or %NULL
+ * to ignore.
+ *
+ * Gets the result of a method call, or an error if the response wasn't
+ * successful.
+ *
+ * Returns: an #FlValue or %NULL on error.
+ */
+FlValue* fl_method_response_get_result(FlMethodResponse* response,
+ GError** error);
+
+/**
+ * fl_method_success_response_new:
+ * @result: (allow-none): the #FlValue returned by the method call or %NULL.
+ *
+ * Creates a response to a method call when that method has successfully
+ * completed.
+ *
+ * Returns: a new #FlMethodResponse.
+ */
+FlMethodSuccessResponse* fl_method_success_response_new(FlValue* result);
+
+/**
+ * fl_method_success_response_get_result:
+ * @response: an #FlMethodSuccessResponse.
+ *
+ * Gets the result of the method call.
+ *
+ * Returns: an #FlValue.
+ */
+FlValue* fl_method_success_response_get_result(
+ FlMethodSuccessResponse* response);
+
+/**
+ * fl_method_error_response_new:
+ * @result: an #FlValue.
+ * @code: an error code.
+ * @message: (allow-none): an error message.
+ * @details: (allow-none): error details.
+ *
+ * Creates a response to a method call when that method has returned an error.
+ *
+ * Returns: a new #FlMethodErrorResponse.
+ */
+FlMethodErrorResponse* fl_method_error_response_new(const gchar* code,
+ const gchar* message,
+ FlValue* details);
+
+/**
+ * fl_method_error_response_get_code:
+ * @response: an #FlMethodErrorResponse.
+ *
+ * Gets the error code reported.
+ *
+ * Returns: an error code.
+ */
+const gchar* fl_method_error_response_get_code(FlMethodErrorResponse* response);
+
+/**
+ * fl_method_error_response_get_message:
+ * @response: an #FlMethodErrorResponse.
+ *
+ * Gets the error message reported.
+ *
+ * Returns: an error message or %NULL if no error message provided.
+ */
+const gchar* fl_method_error_response_get_message(
+ FlMethodErrorResponse* response);
+
+/**
+ * fl_method_error_response_get_details:
+ * @response: an #FlMethodErrorResponse.
+ *
+ * Gets the details provided with this error.
+ *
+ * Returns: an #FlValue or %NULL if no details provided.
+ */
+FlValue* fl_method_error_response_get_details(FlMethodErrorResponse* response);
+
+/**
+ * fl_method_not_implemented_response_new:
+ *
+ * Creates a response to a method call when that method does not exist.
+ *
+ * Returns: a new #FlMethodNotImplementedResponse.
+ */
+FlMethodNotImplementedResponse* fl_method_not_implemented_response_new();
+
+G_END_DECLS
+
+#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_RESPONSE_H_
diff --git a/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h b/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h
new file mode 100644
index 0000000..1685ebc
--- /dev/null
+++ b/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h
@@ -0,0 +1,44 @@
+// 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.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_STANDARD_METHOD_CODEC_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_STANDARD_METHOD_CODEC_H_
+
+#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION)
+#error "Only <flutter_linux/flutter_linux.h> can be included directly."
+#endif
+
+#include "fl_method_codec.h"
+
+G_BEGIN_DECLS
+
+G_DECLARE_FINAL_TYPE(FlStandardMethodCodec,
+ fl_standard_method_codec,
+ FL,
+ STANDARD_METHOD_CODEC,
+ FlMethodCodec)
+
+/**
+ * FlStandardMethodCodec:
+ *
+ * #FlStandardMethodCodec is an #FlMethodCodec that implements method calls
+ * using the Flutter standard message encoding. It should be used with a
+ * #FlMethodChannel.
+ *
+ * #FlStandardMethodCodec matches the StandardMethodCodec class in the Flutter
+ * services library.
+ */
+
+/**
+ * fl_standard_method_codec_new:
+ *
+ * Creates an #FlStandardMethodCodec.
+ *
+ * Returns: a new #FlStandardMethodCodec.
+ */
+FlStandardMethodCodec* fl_standard_method_codec_new();
+
+G_END_DECLS
+
+#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_STANDARD_METHOD_CODEC_H_
diff --git a/shell/platform/linux/public/flutter_linux/flutter_linux.h b/shell/platform/linux/public/flutter_linux/flutter_linux.h
index b26e9ab..03f9dfe 100644
--- a/shell/platform/linux/public/flutter_linux/flutter_linux.h
+++ b/shell/platform/linux/public/flutter_linux/flutter_linux.h
@@ -13,8 +13,14 @@
#include <flutter_linux/fl_dart_project.h>
#include <flutter_linux/fl_engine.h>
#include <flutter_linux/fl_json_message_codec.h>
+#include <flutter_linux/fl_json_method_codec.h>
#include <flutter_linux/fl_message_codec.h>
+#include <flutter_linux/fl_method_call.h>
+#include <flutter_linux/fl_method_channel.h>
+#include <flutter_linux/fl_method_codec.h>
+#include <flutter_linux/fl_method_response.h>
#include <flutter_linux/fl_standard_message_codec.h>
+#include <flutter_linux/fl_standard_method_codec.h>
#include <flutter_linux/fl_string_codec.h>
#include <flutter_linux/fl_value.h>
#include <flutter_linux/fl_view.h>