Add FlBasicMessageChannel (#18189)

diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index 425a315..8ce5b14 100755
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -1185,6 +1185,7 @@
 FILE: ../../../flutter/shell/platform/glfw/public/flutter_glfw.h
 FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.cc
 FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.h
+FILE: ../../../flutter/shell/platform/linux/fl_basic_message_channel.cc
 FILE: ../../../flutter/shell/platform/linux/fl_binary_codec.cc
 FILE: ../../../flutter/shell/platform/linux/fl_binary_codec_test.cc
 FILE: ../../../flutter/shell/platform/linux/fl_binary_messenger.cc
@@ -1207,6 +1208,7 @@
 FILE: ../../../flutter/shell/platform/linux/fl_value.cc
 FILE: ../../../flutter/shell/platform/linux/fl_value_test.cc
 FILE: ../../../flutter/shell/platform/linux/fl_view.cc
+FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h
 FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_binary_codec.h
 FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h
 FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h
diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn
index cae44d2..5b3f865 100644
--- a/shell/platform/linux/BUILD.gn
+++ b/shell/platform/linux/BUILD.gn
@@ -44,6 +44,7 @@
 }
 
 _public_headers = [
+  "public/flutter_linux/fl_basic_message_channel.h",
   "public/flutter_linux/fl_binary_codec.h",
   "public/flutter_linux/fl_binary_messenger.h",
   "public/flutter_linux/fl_dart_project.h",
@@ -64,6 +65,7 @@
   public = _public_headers
 
   sources = [
+    "fl_basic_message_channel.cc",
     "fl_binary_codec.cc",
     "fl_binary_messenger.cc",
     "fl_dart_project.cc",
diff --git a/shell/platform/linux/fl_basic_message_channel.cc b/shell/platform/linux/fl_basic_message_channel.cc
new file mode 100644
index 0000000..5559c4a
--- /dev/null
+++ b/shell/platform/linux/fl_basic_message_channel.cc
@@ -0,0 +1,206 @@
+// 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_basic_message_channel.h"
+
+#include <gmodule.h>
+
+struct _FlBasicMessageChannel {
+  GObject parent_instance;
+
+  // Messenger to communicate on
+  FlBinaryMessenger* messenger;
+
+  // Channel name
+  gchar* name;
+
+  // Codec to en/decode messages
+  FlMessageCodec* codec;
+
+  // Function called when a message is received
+  FlBasicMessageChannelMessageHandler message_handler;
+  gpointer message_handler_data;
+};
+
+// Wrap the binary messenger handle for type safety and to make the API
+// consistent
+struct _FlBasicMessageChannelResponseHandle {
+  FlBinaryMessengerResponseHandle* response_handle;
+};
+
+static FlBasicMessageChannelResponseHandle* response_handle_new(
+    FlBinaryMessengerResponseHandle* response_handle) {
+  FlBasicMessageChannelResponseHandle* handle =
+      static_cast<FlBasicMessageChannelResponseHandle*>(
+          g_malloc0(sizeof(FlBasicMessageChannelResponseHandle)));
+  handle->response_handle = response_handle;
+
+  return handle;
+}
+
+static void response_handle_free(FlBasicMessageChannelResponseHandle* handle) {
+  g_free(handle);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(FlBasicMessageChannelResponseHandle,
+                              response_handle_free);
+
+// Added here to stop the compiler from optimising this function away
+G_MODULE_EXPORT GType fl_basic_message_channel_get_type();
+
+G_DEFINE_TYPE(FlBasicMessageChannel, fl_basic_message_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) {
+  FlBasicMessageChannel* self = FL_BASIC_MESSAGE_CHANNEL(user_data);
+
+  if (self->message_handler == nullptr) {
+    fl_binary_messenger_send_response(messenger, response_handle, nullptr,
+                                      nullptr);
+    return;
+  }
+
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(FlValue) message_value =
+      fl_message_codec_decode_message(self->codec, message, &error);
+  if (message_value == nullptr) {
+    g_warning("Failed to decode message: %s", error->message);
+    fl_binary_messenger_send_response(messenger, response_handle, nullptr,
+                                      nullptr);
+  }
+
+  self->message_handler(self, message_value,
+                        response_handle_new(response_handle),
+                        self->message_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_basic_message_channel_dispose(GObject* object) {
+  FlBasicMessageChannel* self = FL_BASIC_MESSAGE_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_basic_message_channel_parent_class)->dispose(object);
+}
+
+static void fl_basic_message_channel_class_init(
+    FlBasicMessageChannelClass* klass) {
+  G_OBJECT_CLASS(klass)->dispose = fl_basic_message_channel_dispose;
+}
+
+static void fl_basic_message_channel_init(FlBasicMessageChannel* self) {}
+
+G_MODULE_EXPORT FlBasicMessageChannel* fl_basic_message_channel_new(
+    FlBinaryMessenger* messenger,
+    const gchar* name,
+    FlMessageCodec* 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_MESSAGE_CODEC(codec), nullptr);
+
+  FlBasicMessageChannel* self = FL_BASIC_MESSAGE_CHANNEL(
+      g_object_new(fl_basic_message_channel_get_type(), nullptr));
+
+  self->messenger = FL_BINARY_MESSENGER(g_object_ref(messenger));
+  self->name = g_strdup(name);
+  self->codec = FL_MESSAGE_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_basic_message_channel_set_message_handler(
+    FlBasicMessageChannel* self,
+    FlBasicMessageChannelMessageHandler handler,
+    gpointer user_data) {
+  g_return_if_fail(FL_IS_BASIC_MESSAGE_CHANNEL(self));
+
+  self->message_handler = handler;
+  self->message_handler_data = user_data;
+}
+
+G_MODULE_EXPORT gboolean fl_basic_message_channel_respond(
+    FlBasicMessageChannel* self,
+    FlBasicMessageChannelResponseHandle* response_handle,
+    FlValue* message,
+    GError** error) {
+  g_return_val_if_fail(FL_IS_BASIC_MESSAGE_CHANNEL(self), FALSE);
+  g_return_val_if_fail(response_handle != nullptr, FALSE);
+
+  // Take reference to ensure it is freed
+  g_autoptr(FlBasicMessageChannelResponseHandle) owned_response_handle =
+      response_handle;
+
+  g_autoptr(GBytes) data =
+      fl_message_codec_encode_message(self->codec, message, error);
+  if (data == nullptr)
+    return FALSE;
+
+  return fl_binary_messenger_send_response(
+      self->messenger, owned_response_handle->response_handle, data, error);
+}
+
+G_MODULE_EXPORT void fl_basic_message_channel_send(FlBasicMessageChannel* self,
+                                                   FlValue* message,
+                                                   GCancellable* cancellable,
+                                                   GAsyncReadyCallback callback,
+                                                   gpointer user_data) {
+  g_return_if_fail(FL_IS_BASIC_MESSAGE_CHANNEL(self));
+  g_return_if_fail(message != nullptr);
+
+  g_autoptr(GTask) task =
+      callback != nullptr ? g_task_new(self, cancellable, callback, user_data)
+                          : nullptr;
+
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(GBytes) data =
+      fl_message_codec_encode_message(self->codec, message, &error);
+  if (data == nullptr) {
+    if (task != nullptr)
+      g_task_return_error(task, error);
+    return;
+  }
+
+  fl_binary_messenger_send_on_channel(
+      self->messenger, self->name, data, cancellable,
+      callback != nullptr ? message_response_cb : nullptr,
+      g_steal_pointer(&task));
+}
+
+G_MODULE_EXPORT FlValue* fl_basic_message_channel_send_on_channel_finish(
+    FlBasicMessageChannel* self,
+    GAsyncResult* result,
+    GError** error) {
+  g_return_val_if_fail(FL_IS_BASIC_MESSAGE_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) message =
+      fl_binary_messenger_send_on_channel_finish(self->messenger, r, error);
+  if (message == nullptr)
+    return nullptr;
+
+  return fl_message_codec_decode_message(self->codec, message, error);
+}
diff --git a/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h b/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h
new file mode 100644
index 0000000..a26332b
--- /dev/null
+++ b/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h
@@ -0,0 +1,143 @@
+// 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_BASIC_MESSAGE_CHANNEL_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_BASIC_MESSAGE_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_message_codec.h"
+
+G_BEGIN_DECLS
+
+G_DECLARE_FINAL_TYPE(FlBasicMessageChannel,
+                     fl_basic_message_channel,
+                     FL,
+                     BASIC_MESSAGE_CHANNEL,
+                     GObject)
+
+/**
+ * FlBasicMessageChannel:
+ *
+ * #FlBasicMessageChannel is an object that allows sending and receiving
+ * messages to/from Dart code over platform channels.
+ *
+ * #FlBasicMessageChannel matches the BasicMessageChannel class in the Flutter
+ * services library.
+ */
+
+/**
+ * FlBasicMessageChannelResponseHandle:
+ *
+ * A handle used to respond to messages.
+ */
+typedef struct _FlBasicMessageChannelResponseHandle
+    FlBasicMessageChannelResponseHandle;
+
+/**
+ * FlBasicMessageChannelMessageHandler:
+ * @channel: a #FlBasicMessageChannel
+ * @message: message received
+ * @response_handle: (transfer full): a handle to respond to the message with
+ * @user_data: (closure): data provided when registering this handler
+ *
+ * Function called when a message is received.
+ */
+typedef void (*FlBasicMessageChannelMessageHandler)(
+    FlBasicMessageChannel* channel,
+    FlValue* message,
+    FlBasicMessageChannelResponseHandle* response_handle,
+    gpointer user_data);
+
+/**
+ * fl_basic_message_channel_new:
+ * @messenger: a #FlBinaryMessenger
+ * @name: a channel name
+ * @codec: the message codec
+ *
+ * Create a new basic message channel. @codec must match the codec used on the
+ * Dart end of the channel.
+ *
+ * Returns: a new #FlBasicMessageChannel.
+ */
+FlBasicMessageChannel* fl_basic_message_channel_new(
+    FlBinaryMessenger* messenger,
+    const gchar* name,
+    FlMessageCodec* codec);
+
+/**
+ * fl_basic_message_channel_set_message_handler:
+ * @channel: a #FlBasicMessageChannel
+ * @handler: (allow-none): function to call when a message is received on this
+ * channel or %NULL to disable the handler.
+ * @user_data: (closure): user data to pass to @handler
+ *
+ * Set the function called when a message is received.
+ */
+void fl_basic_message_channel_set_message_handler(
+    FlBasicMessageChannel* channel,
+    FlBasicMessageChannelMessageHandler handler,
+    gpointer user_data);
+
+/**
+ * fl_basic_message_channel_send_response:
+ * @channel: a #FlBasicMessageChannel
+ * @response_handle: (transfer full): handle that was provided in a
+ * #FlBasicMessageChannelMessageHandler
+ * @response: (allow-none): response to send or %NULL for an empty response
+ * @error: (allow-none): #GError location to store the error occurring, or %NULL
+ * to ignore
+ *
+ * Respond to a message.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean fl_basic_message_channel_send_response(
+    FlBasicMessageChannel* channel,
+    FlBasicMessageChannelResponseHandle* response_handle,
+    FlValue* response,
+    GError** error);
+
+/**
+ * fl_basic_message_channel_send:
+ * @channel: a #FlBasicMessageChannel
+ * @message: message to send, must match what the #FlMessageCodec 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
+ *
+ * Asynchronously send a message.
+ */
+void fl_basic_message_channel_send(FlBasicMessageChannel* channel,
+                                   FlValue* message,
+                                   GCancellable* cancellable,
+                                   GAsyncReadyCallback callback,
+                                   gpointer user_data);
+
+/**
+ * fl_basic_message_channel_send_finish:
+ * @channel: a #FlBasicMessageChannel
+ * @result: a #GAsyncResult
+ * @error: (allow-none): #GError location to store the error occurring, or %NULL
+ * to ignore.
+ *
+ * Complete request started with fl_basic_message_channel_send().
+ *
+ * Returns: message response on success or %NULL on error.
+ */
+FlValue* fl_basic_message_channel_send_on_channel_finish(
+    FlBasicMessageChannel* channel,
+    GAsyncResult* result,
+    GError** error);
+
+G_END_DECLS
+
+#endif  // FLUTTER_SHELL_PLATFORM_LINUX_FL_BASIC_MESSAGE_CHANNEL_H_
diff --git a/shell/platform/linux/public/flutter_linux/flutter_linux.h b/shell/platform/linux/public/flutter_linux/flutter_linux.h
index 62449e8..cb869b0 100644
--- a/shell/platform/linux/public/flutter_linux/flutter_linux.h
+++ b/shell/platform/linux/public/flutter_linux/flutter_linux.h
@@ -7,6 +7,7 @@
 
 #define __FLUTTER_LINUX_INSIDE__
 
+#include <flutter_linux/fl_basic_message_channel.h>
 #include <flutter_linux/fl_binary_codec.h>
 #include <flutter_linux/fl_binary_messenger.h>
 #include <flutter_linux/fl_dart_project.h>