blob: 3a1a998e597218b166a56b4be37a04ed4218998f [file] [log] [blame]
// 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_binary_messenger.h"
#include "flutter/shell/platform/linux/fl_binary_messenger_private.h"
#include "flutter/fml/logging.h"
#include "flutter/shell/platform/linux/fl_engine_private.h"
#include "flutter/shell/platform/linux/fl_method_codec_private.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_codec.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h"
#include <gmodule.h>
static constexpr char kControlChannelName[] = "dev.flutter/channel-buffers";
static constexpr char kResizeMethod[] = "resize";
static constexpr char kOverflowMethod[] = "overflow";
G_DEFINE_QUARK(fl_binary_messenger_codec_error_quark,
fl_binary_messenger_codec_error)
G_DECLARE_FINAL_TYPE(FlBinaryMessengerImpl,
fl_binary_messenger_impl,
FL,
BINARY_MESSENGER_IMPL,
GObject)
G_DECLARE_FINAL_TYPE(FlBinaryMessengerResponseHandleImpl,
fl_binary_messenger_response_handle_impl,
FL,
BINARY_MESSENGER_RESPONSE_HANDLE_IMPL,
FlBinaryMessengerResponseHandle)
G_DEFINE_INTERFACE(FlBinaryMessenger, fl_binary_messenger, G_TYPE_OBJECT)
struct _FlBinaryMessengerImpl {
GObject parent_instance;
GWeakRef engine;
// PlatformMessageHandler keyed by channel name.
GHashTable* platform_message_handlers;
};
static void fl_binary_messenger_impl_iface_init(
FlBinaryMessengerInterface* iface);
G_DEFINE_TYPE_WITH_CODE(
FlBinaryMessengerImpl,
fl_binary_messenger_impl,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE(fl_binary_messenger_get_type(),
fl_binary_messenger_impl_iface_init))
static void fl_binary_messenger_response_handle_class_init(
FlBinaryMessengerResponseHandleClass* klass) {}
G_DEFINE_TYPE(FlBinaryMessengerResponseHandle,
fl_binary_messenger_response_handle,
G_TYPE_OBJECT)
static void fl_binary_messenger_response_handle_init(
FlBinaryMessengerResponseHandle* self) {}
struct _FlBinaryMessengerResponseHandleImpl {
FlBinaryMessengerResponseHandle parent_instance;
// Messenger sending response on.
FlBinaryMessengerImpl* messenger;
// Handle to send the response with. This is cleared to nullptr when it is
// used.
const FlutterPlatformMessageResponseHandle* response_handle;
};
G_DEFINE_TYPE(FlBinaryMessengerResponseHandleImpl,
fl_binary_messenger_response_handle_impl,
fl_binary_messenger_response_handle_get_type())
static void fl_binary_messenger_default_init(
FlBinaryMessengerInterface* iface) {}
static void fl_binary_messenger_response_handle_impl_dispose(GObject* object) {
FlBinaryMessengerResponseHandleImpl* self =
FL_BINARY_MESSENGER_RESPONSE_HANDLE_IMPL(object);
g_autoptr(FlEngine) engine =
FL_ENGINE(g_weak_ref_get(&self->messenger->engine));
if (self->response_handle != nullptr && engine != nullptr) {
g_critical("FlBinaryMessengerResponseHandle was not responded to");
}
g_clear_object(&self->messenger);
self->response_handle = nullptr;
G_OBJECT_CLASS(fl_binary_messenger_response_handle_impl_parent_class)
->dispose(object);
}
static void fl_binary_messenger_response_handle_impl_class_init(
FlBinaryMessengerResponseHandleImplClass* klass) {
G_OBJECT_CLASS(klass)->dispose =
fl_binary_messenger_response_handle_impl_dispose;
}
static void fl_binary_messenger_response_handle_impl_init(
FlBinaryMessengerResponseHandleImpl* self) {}
static FlBinaryMessengerResponseHandleImpl*
fl_binary_messenger_response_handle_impl_new(
FlBinaryMessengerImpl* messenger,
const FlutterPlatformMessageResponseHandle* response_handle) {
FlBinaryMessengerResponseHandleImpl* self =
FL_BINARY_MESSENGER_RESPONSE_HANDLE_IMPL(g_object_new(
fl_binary_messenger_response_handle_impl_get_type(), nullptr));
self->messenger = FL_BINARY_MESSENGER_IMPL(g_object_ref(messenger));
self->response_handle = response_handle;
return self;
}
typedef struct {
FlBinaryMessengerMessageHandler message_handler;
gpointer message_handler_data;
GDestroyNotify message_handler_destroy_notify;
} PlatformMessageHandler;
static PlatformMessageHandler* platform_message_handler_new(
FlBinaryMessengerMessageHandler handler,
gpointer user_data,
GDestroyNotify destroy_notify) {
PlatformMessageHandler* self = static_cast<PlatformMessageHandler*>(
g_malloc0(sizeof(PlatformMessageHandler)));
self->message_handler = handler;
self->message_handler_data = user_data;
self->message_handler_destroy_notify = destroy_notify;
return self;
}
static void platform_message_handler_free(gpointer data) {
PlatformMessageHandler* self = static_cast<PlatformMessageHandler*>(data);
if (self->message_handler_destroy_notify) {
self->message_handler_destroy_notify(self->message_handler_data);
}
g_free(self);
}
static void engine_weak_notify_cb(gpointer user_data,
GObject* where_the_object_was) {
FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(user_data);
// Disconnect any handlers.
// Take the reference in case a handler tries to modify this table.
g_autoptr(GHashTable) handlers = self->platform_message_handlers;
self->platform_message_handlers = g_hash_table_new_full(
g_str_hash, g_str_equal, g_free, platform_message_handler_free);
g_hash_table_remove_all(handlers);
}
static gboolean fl_binary_messenger_platform_message_cb(
FlEngine* engine,
const gchar* channel,
GBytes* message,
const FlutterPlatformMessageResponseHandle* response_handle,
void* user_data) {
FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(user_data);
PlatformMessageHandler* handler = static_cast<PlatformMessageHandler*>(
g_hash_table_lookup(self->platform_message_handlers, channel));
if (handler == nullptr) {
return FALSE;
}
g_autoptr(FlBinaryMessengerResponseHandleImpl) handle =
fl_binary_messenger_response_handle_impl_new(self, response_handle);
handler->message_handler(FL_BINARY_MESSENGER(self), channel, message,
FL_BINARY_MESSENGER_RESPONSE_HANDLE(handle),
handler->message_handler_data);
return TRUE;
}
static void fl_binary_messenger_impl_dispose(GObject* object) {
FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(object);
{
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
if (engine) {
g_object_weak_unref(G_OBJECT(engine), engine_weak_notify_cb, self);
}
}
g_weak_ref_clear(&self->engine);
g_clear_pointer(&self->platform_message_handlers, g_hash_table_unref);
G_OBJECT_CLASS(fl_binary_messenger_impl_parent_class)->dispose(object);
}
static void set_message_handler_on_channel(
FlBinaryMessenger* messenger,
const gchar* channel,
FlBinaryMessengerMessageHandler handler,
gpointer user_data,
GDestroyNotify destroy_notify) {
FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(messenger);
// Don't set handlers if engine already gone.
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
if (engine == nullptr) {
if (handler != nullptr) {
g_warning(
"Attempted to set message handler on an FlBinaryMessenger without an "
"engine");
}
if (destroy_notify != nullptr) {
destroy_notify(user_data);
}
return;
}
if (handler != nullptr) {
g_hash_table_replace(
self->platform_message_handlers, g_strdup(channel),
platform_message_handler_new(handler, user_data, destroy_notify));
} else {
g_hash_table_remove(self->platform_message_handlers, channel);
}
}
static gboolean do_unref(gpointer value) {
g_object_unref(value);
return G_SOURCE_REMOVE;
}
// Note: This function can be called from any thread.
static gboolean send_response(FlBinaryMessenger* messenger,
FlBinaryMessengerResponseHandle* response_handle_,
GBytes* response,
GError** error) {
FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(messenger);
g_return_val_if_fail(
FL_IS_BINARY_MESSENGER_RESPONSE_HANDLE_IMPL(response_handle_), FALSE);
FlBinaryMessengerResponseHandleImpl* response_handle =
FL_BINARY_MESSENGER_RESPONSE_HANDLE_IMPL(response_handle_);
g_return_val_if_fail(response_handle->messenger == self, FALSE);
g_return_val_if_fail(response_handle->response_handle != nullptr, FALSE);
FlEngine* engine = FL_ENGINE(g_weak_ref_get(&self->engine));
if (engine == nullptr) {
return TRUE;
}
gboolean result = false;
if (response_handle->response_handle == nullptr) {
g_set_error(
error, FL_BINARY_MESSENGER_ERROR,
FL_BINARY_MESSENGER_ERROR_ALREADY_RESPONDED,
"Attempted to respond to a message that is already responded to");
result = FALSE;
} else {
result = fl_engine_send_platform_message_response(
engine, response_handle->response_handle, response, error);
response_handle->response_handle = nullptr;
}
// This guarantees that the dispose method for the engine is executed
// on the platform thread in the rare chance this is the last ref.
g_idle_add(do_unref, engine);
return result;
}
static void platform_message_ready_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 send_on_channel(FlBinaryMessenger* messenger,
const gchar* channel,
GBytes* message,
GCancellable* cancellable,
GAsyncReadyCallback callback,
gpointer user_data) {
FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(messenger);
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
if (engine == nullptr) {
return;
}
fl_engine_send_platform_message(
engine, channel, message, cancellable,
callback != nullptr ? platform_message_ready_cb : nullptr,
callback != nullptr ? g_task_new(self, cancellable, callback, user_data)
: nullptr);
}
static GBytes* send_on_channel_finish(FlBinaryMessenger* messenger,
GAsyncResult* result,
GError** error) {
FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(messenger);
g_return_val_if_fail(g_task_is_valid(result, self), FALSE);
g_autoptr(GTask) task = G_TASK(result);
GAsyncResult* r = G_ASYNC_RESULT(g_task_propagate_pointer(task, nullptr));
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
if (engine == nullptr) {
return nullptr;
}
return fl_engine_send_platform_message_finish(engine, r, error);
}
// Completes method call and returns TRUE if the call was successful.
static gboolean finish_method(GObject* object,
GAsyncResult* result,
GError** error) {
g_autoptr(GBytes) response = fl_binary_messenger_send_on_channel_finish(
FL_BINARY_MESSENGER(object), result, error);
if (response == nullptr) {
return FALSE;
}
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
return fl_method_codec_decode_response(FL_METHOD_CODEC(codec), response,
error) != nullptr;
}
// Called when a response is received for the resize channel message.
static void resize_channel_response_cb(GObject* object,
GAsyncResult* result,
gpointer user_data) {
g_autoptr(GError) error = nullptr;
if (!finish_method(object, result, &error)) {
g_warning("Failed to resize channel: %s", error->message);
}
}
static void resize_channel(FlBinaryMessenger* messenger,
const gchar* channel,
int64_t new_size) {
FML_DCHECK(new_size >= 0);
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
g_autoptr(FlValue) args = fl_value_new_list();
fl_value_append_take(args, fl_value_new_string(channel));
fl_value_append_take(args, fl_value_new_int(new_size));
g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
FL_METHOD_CODEC(codec), kResizeMethod, args, nullptr);
fl_binary_messenger_send_on_channel(messenger, kControlChannelName, message,
nullptr, resize_channel_response_cb,
nullptr);
}
// Called when a response is received for the warns on overflow message.
static void set_warns_on_channel_overflow_response_cb(GObject* object,
GAsyncResult* result,
gpointer user_data) {
g_autoptr(GError) error = nullptr;
if (!finish_method(object, result, &error)) {
g_warning("Failed to set warns on channel overflow: %s", error->message);
}
}
static void set_warns_on_channel_overflow(FlBinaryMessenger* messenger,
const gchar* channel,
bool warns) {
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
g_autoptr(FlValue) args = fl_value_new_list();
fl_value_append_take(args, fl_value_new_string(channel));
fl_value_append_take(args, fl_value_new_bool(!warns));
g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
FL_METHOD_CODEC(codec), kOverflowMethod, args, nullptr);
fl_binary_messenger_send_on_channel(
messenger, kControlChannelName, message, nullptr,
set_warns_on_channel_overflow_response_cb, nullptr);
}
static void fl_binary_messenger_impl_class_init(
FlBinaryMessengerImplClass* klass) {
G_OBJECT_CLASS(klass)->dispose = fl_binary_messenger_impl_dispose;
}
static void fl_binary_messenger_impl_iface_init(
FlBinaryMessengerInterface* iface) {
iface->set_message_handler_on_channel = set_message_handler_on_channel;
iface->send_response = send_response;
iface->send_on_channel = send_on_channel;
iface->send_on_channel_finish = send_on_channel_finish;
iface->resize_channel = resize_channel;
iface->set_warns_on_channel_overflow = set_warns_on_channel_overflow;
}
static void fl_binary_messenger_impl_init(FlBinaryMessengerImpl* self) {
self->platform_message_handlers = g_hash_table_new_full(
g_str_hash, g_str_equal, g_free, platform_message_handler_free);
}
FlBinaryMessenger* fl_binary_messenger_new(FlEngine* engine) {
g_return_val_if_fail(FL_IS_ENGINE(engine), nullptr);
FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(
g_object_new(fl_binary_messenger_impl_get_type(), nullptr));
// Added to stop compiler complaining about an unused function.
FL_IS_BINARY_MESSENGER_IMPL(self);
g_weak_ref_init(&self->engine, G_OBJECT(engine));
g_object_weak_ref(G_OBJECT(engine), engine_weak_notify_cb, self);
fl_engine_set_platform_message_handler(
engine, fl_binary_messenger_platform_message_cb, self, NULL);
return FL_BINARY_MESSENGER(self);
}
G_MODULE_EXPORT void fl_binary_messenger_set_message_handler_on_channel(
FlBinaryMessenger* self,
const gchar* channel,
FlBinaryMessengerMessageHandler handler,
gpointer user_data,
GDestroyNotify destroy_notify) {
g_return_if_fail(FL_IS_BINARY_MESSENGER(self));
g_return_if_fail(channel != nullptr);
FL_BINARY_MESSENGER_GET_IFACE(self)->set_message_handler_on_channel(
self, channel, handler, user_data, destroy_notify);
}
// Note: This function can be called from any thread.
G_MODULE_EXPORT gboolean fl_binary_messenger_send_response(
FlBinaryMessenger* self,
FlBinaryMessengerResponseHandle* response_handle,
GBytes* response,
GError** error) {
g_return_val_if_fail(FL_IS_BINARY_MESSENGER(self), FALSE);
g_return_val_if_fail(FL_IS_BINARY_MESSENGER_RESPONSE_HANDLE(response_handle),
FALSE);
return FL_BINARY_MESSENGER_GET_IFACE(self)->send_response(
self, response_handle, response, error);
}
G_MODULE_EXPORT void fl_binary_messenger_send_on_channel(
FlBinaryMessenger* self,
const gchar* channel,
GBytes* message,
GCancellable* cancellable,
GAsyncReadyCallback callback,
gpointer user_data) {
g_return_if_fail(FL_IS_BINARY_MESSENGER(self));
g_return_if_fail(channel != nullptr);
FL_BINARY_MESSENGER_GET_IFACE(self)->send_on_channel(
self, channel, message, cancellable, callback, user_data);
}
G_MODULE_EXPORT GBytes* fl_binary_messenger_send_on_channel_finish(
FlBinaryMessenger* self,
GAsyncResult* result,
GError** error) {
g_return_val_if_fail(FL_IS_BINARY_MESSENGER(self), FALSE);
return FL_BINARY_MESSENGER_GET_IFACE(self)->send_on_channel_finish(
self, result, error);
}
G_MODULE_EXPORT void fl_binary_messenger_resize_channel(FlBinaryMessenger* self,
const gchar* channel,
int64_t new_size) {
g_return_if_fail(FL_IS_BINARY_MESSENGER(self));
return FL_BINARY_MESSENGER_GET_IFACE(self)->resize_channel(self, channel,
new_size);
}
G_MODULE_EXPORT void fl_binary_messenger_set_warns_on_channel_overflow(
FlBinaryMessenger* self,
const gchar* channel,
bool warns) {
g_return_if_fail(FL_IS_BINARY_MESSENGER(self));
return FL_BINARY_MESSENGER_GET_IFACE(self)->set_warns_on_channel_overflow(
self, channel, warns);
}