Skip to content

[linux] Add MethodChannel #153

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,26 @@
#ifndef LIBRARY_LINUX_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_BINARY_MESSENGER_H_
#define LIBRARY_LINUX_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_BINARY_MESSENGER_H_

#include <functional>
#include <string>

// TODO: Consider adding absl as a dependency and using absl::Span for all of
// the message/message_size pairs.
namespace flutter_desktop_embedding {

// A message reply callback.
//
// Used for submitting a reply back to a Flutter message sender.
typedef std::function<void(const uint8_t *reply, const size_t reply_size)>
BinaryReply;

// A message handler callback.
//
// Used for receiving messages from Flutter and providing an asynchronous reply.
typedef std::function<void(const uint8_t *message, const size_t message_size,
BinaryReply reply)>
BinaryMessageHandler;

// A protocol for a class that handles communication of binary data on named
// channels to and from the Flutter engine.
class BinaryMessenger {
Expand All @@ -32,7 +48,13 @@ class BinaryMessenger {
// TODO: Add support for a version of Send expecting a reply once
// https://github.com/flutter/flutter/issues/18852 is fixed.

// TODO: Add SetMessageHandler. See Issue #102.
// Registers a message handler for incoming binary messages from the Flutter
// side on the specified channel.
//
// Replaces any existing handler. Provide a null handler to unregister the
// existing handler.
virtual void SetMessageHandler(const std::string &channel,
BinaryMessageHandler handler) = 0;
};

} // namespace flutter_desktop_embedding
Expand Down
16 changes: 14 additions & 2 deletions library/linux/include/flutter_desktop_embedding/json_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@
#ifndef LIBRARY_LINUX_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_JSON_PLUGIN_H_
#define LIBRARY_LINUX_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_JSON_PLUGIN_H_

#include <memory>
#include <string>

#include "json_method_call.h"
#include "method_channel.h"
#include "plugin.h"

namespace flutter_desktop_embedding {
Expand All @@ -38,16 +42,24 @@ class JsonPlugin : public Plugin {
std::unique_ptr<MethodResult> result) override;

protected:
// Plugin implementation:
void RegisterMethodChannels(BinaryMessenger *messenger) override;

// Identical to HandleMethodCall, except that the call has been cast to the
// more specific type. Subclasses must implement this instead of
// HandleMethodCall.
virtual void HandleJsonMethodCall(const JsonMethodCall &method_call,
std::unique_ptr<MethodResult> result) = 0;

// Calls InvokeMethodCall with a JsonMethodCall constructed from the given
// values.
// Calls InvokeMethodCall on |method_channel_| with a JsonMethodCall
// constructed from the given values.
void InvokeMethod(const std::string &method,
const Json::Value &arguments = Json::Value());

private:
// The MethodChannel used by this plugin for communication with the Flutter
// engine.
std::unique_ptr<MethodChannel> method_channel_;
};

} // namespace flutter_desktop_embedding
Expand Down
72 changes: 72 additions & 0 deletions library/linux/include/flutter_desktop_embedding/method_channel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef LIBRARY_LINUX_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_METHOD_CHANNEL_H_
#define LIBRARY_LINUX_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_METHOD_CHANNEL_H_

#include <string>

#include "binary_messenger.h"
#include "method_call.h"
#include "method_codec.h"
#include "method_result.h"

namespace flutter_desktop_embedding {

// A handler for receiving a method call from the Flutter engine.
//
// Implementations must asynchronously call exactly one of the methods on
// |result| to indicate the resust of the method call.
typedef std::function<void(const MethodCall &call,
std::unique_ptr<MethodResult> result)>
MethodCallHandler;

// A channel for communicating with the Flutter engine using invocation of
// asynchronous methods.
class MethodChannel {
public:
// Creates an instance that sends and receives method calls on the channel
// named |name|, encoded with |codec| and dispatched via |messenger|.
//
// TODO: Make codec optional once the standard codec is supported (Issue #67).
MethodChannel(BinaryMessenger *messenger, const std::string &name,
const MethodCodec *codec);
~MethodChannel();

// Prevent copying.
MethodChannel(MethodChannel const &) = delete;
MethodChannel &operator=(MethodChannel const &) = delete;

// Sends |method_call| to the Flutter engine on this channel.
//
// TODO: Implement InovkeMethod and remove this. This is a temporary
// implementation, since supporting InvokeMethod involves significant changes
// to other classes.
void InvokeMethodCall(const MethodCall &method_call) const;

// TODO: Add support for a version expecting a reply once
// https://github.com/flutter/flutter/issues/18852 is fixed.

// Registers a handler that should be called any time a method call is
// received on this channel.
void SetMethodCallHandler(MethodCallHandler handler) const;

private:
BinaryMessenger *messenger_;
std::string name_;
const MethodCodec *codec_;
};

} // namespace flutter_desktop_embedding

#endif // LIBRARY_LINUX_INCLUDE_FLUTTER_DESKTOP_EMBEDDING_METHOD_CHANNEL_H_
15 changes: 10 additions & 5 deletions library/linux/include/flutter_desktop_embedding/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,26 @@ class Plugin {
// while waiting for this plugin to handle its platform message.
virtual bool input_blocking() const { return input_blocking_; }

// Sets the pointer to the caller-owned binary messenger.
// Binds this plugin to the given caller-owned binary messenger. It must
// remain valid for the life of the plugin.
//
// The embedder typically sets this pointer rather than the client.
virtual void set_binary_messenger(BinaryMessenger *messenger) {
messenger_ = messenger;
}
void SetBinaryMessenger(BinaryMessenger *messenger);

protected:
// Implementers should register any MethodChannels that should receive
// messages from Flutter with |messenger| when this is called.
virtual void RegisterMethodChannels(BinaryMessenger *messenger) = 0;

// Calls a method in the Flutter engine on this Plugin's channel.
//
// Deprecated. Use MethodChannel's InvokeMethodCall instead.
void InvokeMethodCall(const MethodCall &method_call);

private:
std::string channel_;
// Caller-owned instance of the binary messenger used to message the engine.
BinaryMessenger *messenger_;
const BinaryMessenger *messenger_;
bool input_blocking_;
};

Expand Down
1 change: 0 additions & 1 deletion library/linux/src/embedder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,6 @@ namespace flutter_desktop_embedding {

bool AddPlugin(GLFWwindow *flutter_window, std::unique_ptr<Plugin> plugin) {
auto state = GetSavedEmbedderState(flutter_window);
plugin->set_binary_messenger(state->plugin_handler.get());
return state->plugin_handler->AddPlugin(std::move(plugin));
}

Expand Down
26 changes: 11 additions & 15 deletions library/linux/src/internal/engine_method_result.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,17 @@

namespace flutter_desktop_embedding {

EngineMethodResult::EngineMethodResult(
FlutterEngine engine,
const FlutterPlatformMessageResponseHandle *response_handle,
const MethodCodec *codec)
: engine_(engine), response_handle_(response_handle), codec_(codec) {
if (!response_handle_) {
std::cerr << "Error: Response handle must be provided for a response."
EngineMethodResult::EngineMethodResult(BinaryReply reply_handler,
const MethodCodec *codec)
: reply_handler_(std::move(reply_handler)), codec_(codec) {
if (!reply_handler_) {
std::cerr << "Error: Reply handler must be provided for a response."
<< std::endl;
}
}

EngineMethodResult::~EngineMethodResult() {
if (response_handle_) {
if (reply_handler_) {
// Warn, rather than send a not-implemented response, since the engine may
// no longer be valid at this point.
std::cerr
Expand All @@ -55,20 +53,18 @@ void EngineMethodResult::ErrorInternal(const std::string &error_code,
void EngineMethodResult::NotImplementedInternal() { SendResponseData(nullptr); }

void EngineMethodResult::SendResponseData(const std::vector<uint8_t> *data) {
if (!response_handle_) {
if (!reply_handler_) {
std::cerr
<< "Error: Response can be set only once. Ignoring duplicate response."
<< "Error: Only one of Success, Error, or NotImplemented can be called,"
<< " and it can be called exactyl once. Ignoring duplicate result."
<< std::endl;
return;
}

const uint8_t *message = data && !data->empty() ? data->data() : nullptr;
size_t message_size = data ? data->size() : 0;
FlutterEngineSendPlatformMessageResponse(engine_, response_handle_, message,
message_size);
// The engine frees the response handle once
// FlutterEngineSendPlatformMessageResponse is called.
response_handle_ = nullptr;
reply_handler_(message, message_size);
reply_handler_ = nullptr;
}

} // namespace flutter_desktop_embedding
17 changes: 6 additions & 11 deletions library/linux/src/internal/engine_method_result.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
#include <string>
#include <vector>

#include <flutter_embedder.h>

#include "library/linux/include/flutter_desktop_embedding/binary_messenger.h"
#include "library/linux/include/flutter_desktop_embedding/method_codec.h"
#include "library/linux/include/flutter_desktop_embedding/method_result.h"

Expand All @@ -27,15 +26,12 @@ namespace flutter_desktop_embedding {
// Implemention of MethodResult that sends responses to the Flutter egnine.
class EngineMethodResult : public MethodResult {
public:
// Creates a result object that will send results to |engine|, tagged as
// associated with |response_handle|, encoded using |codec|. The |engine|
// and |codec| pointers must remain valid for as long as this object exists.
// Creates a result object that will send results to |reply_handler|, encoded
// using |codec|. The |codec| pointer must remain valid for as long as this
// object exists.
//
// If the codec is null, only NotImplemented() may be called.
EngineMethodResult(
FlutterEngine engine,
const FlutterPlatformMessageResponseHandle *response_handle,
const MethodCodec *codec);
EngineMethodResult(BinaryReply reply_handler, const MethodCodec *codec);
~EngineMethodResult();

protected:
Expand All @@ -52,8 +48,7 @@ class EngineMethodResult : public MethodResult {
// the engine.
void SendResponseData(const std::vector<uint8_t> *data);

FlutterEngine engine_;
const FlutterPlatformMessageResponseHandle *response_handle_;
BinaryReply reply_handler_;
const MethodCodec *codec_;
};

Expand Down
51 changes: 32 additions & 19 deletions library/linux/src/internal/plugin_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.
#include "library/linux/src/internal/plugin_handler.h"

#include "library/linux/include/flutter_desktop_embedding/method_channel.h"
#include "library/linux/src/internal/engine_method_result.h"

#include <iostream>
Expand All @@ -27,6 +28,7 @@ bool PluginHandler::AddPlugin(std::unique_ptr<Plugin> plugin) {
if (plugins_.find(plugin->channel()) != plugins_.end()) {
return false;
}
plugin->SetBinaryMessenger(this);
plugins_.insert(std::make_pair(plugin->channel(), std::move(plugin)));
return true;
}
Expand All @@ -36,36 +38,42 @@ void PluginHandler::HandleMethodCallMessage(
std::function<void(void)> input_block_cb,
std::function<void(void)> input_unblock_cb) {
std::string channel(message->channel);
auto *response_handle = message->response_handle;
auto *response_engine = engine_;
BinaryReply reply_handler = [response_engine, response_handle](
const uint8_t *reply,
const size_t reply_size) mutable {
if (!response_handle) {
std::cerr << "Error: Response can be set only once. Ignoring "
"duplicate response."
<< std::endl;
return;
}
FlutterEngineSendPlatformMessageResponse(response_engine, response_handle,
reply, reply_size);
// The engine frees the response handle once
// FlutterEngineSendPlatformMessageResponse is called.
response_handle = nullptr;
};

// Find the plugin for the channel; if there isn't one, report the failure.
if (plugins_.find(channel) == plugins_.end()) {
// Find the handler for the channel; if there isn't one, report the failure.
if (handlers_.find(channel) == handlers_.end()) {
auto result =
std::make_unique<flutter_desktop_embedding::EngineMethodResult>(
engine_, message->response_handle, nullptr);
std::move(reply_handler), nullptr);
result->NotImplemented();
return;
}
const BinaryMessageHandler &message_handler = handlers_[channel];
const std::unique_ptr<Plugin> &plugin = plugins_[channel];

// Use the plugin's codec to decode the call and build a result handler.
const flutter_desktop_embedding::MethodCodec &codec = plugin->GetCodec();
auto result = std::make_unique<flutter_desktop_embedding::EngineMethodResult>(
engine_, message->response_handle, &codec);
std::unique_ptr<flutter_desktop_embedding::MethodCall> method_call =
codec.DecodeMethodCall(message->message, message->message_size);
if (!method_call) {
std::cerr << "Unable to construct method call from message on channel "
<< message->channel << std::endl;
result->NotImplemented();
return;
}

// Process the call, handling input blocking if requested by the plugin.
if (plugin->input_blocking()) {
if (plugin && plugin->input_blocking()) {
input_block_cb();
}
plugin->HandleMethodCall(*method_call, std::move(result));
if (plugin->input_blocking()) {
message_handler(message->message, message->message_size,
std::move(reply_handler));
if (plugin && plugin->input_blocking()) {
input_unblock_cb();
}
}
Expand All @@ -81,4 +89,9 @@ void PluginHandler::Send(const std::string &channel, const uint8_t *message,
FlutterEngineSendPlatformMessage(engine_, &platform_message);
}

void PluginHandler::SetMessageHandler(const std::string &channel,
BinaryMessageHandler handler) {
handlers_[channel] = std::move(handler);
}

} // namespace flutter_desktop_embedding
3 changes: 3 additions & 0 deletions library/linux/src/internal/plugin_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,13 @@ class PluginHandler : public BinaryMessenger {
// BinaryMessenger implementation:
void Send(const std::string &channel, const uint8_t *message,
const size_t message_size) const override;
void SetMessageHandler(const std::string &channel,
BinaryMessageHandler handler) override;

private:
FlutterEngine engine_;
std::map<std::string, std::unique_ptr<Plugin>> plugins_;
std::map<std::string, BinaryMessageHandler> handlers_;
};

} // namespace flutter_desktop_embedding
Expand Down
Loading