Skip to content
This repository was archived by the owner on Apr 11, 2025. It is now read-only.
Draft
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
105 changes: 71 additions & 34 deletions lib/flutter_embedder.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
defmodule FlutterEmbedder do
@moduledoc File.read!("README.md")
# @moduledoc File.read!("README.md")
alias FlutterEmbedder.{PlatformChannelMessage, StandardMessageCodec, StandardMethodCall}
import StandardMessageCodec, only: [is_valid_dart_value: 1]
defstruct [:port, :uri, :module]
Expand All @@ -17,19 +17,30 @@ defmodule FlutterEmbedder do
}
end

def start_link(args, opts \\ []) do
def start_link(args, opts \\ [name: __MODULE__]) do
GenServer.start_link(__MODULE__, args, opts)
end

def send_platform_message(embedder \\ __MODULE__, channel, data)
when is_valid_dart_value(data) do
GenServer.cast(embedder, {:send_platform_message, channel, data})
end

def observatory_url(embedder \\ __MODULE__) do
GenServer.call(embedder, :observatory_url)
end

@impl GenServer
def init(args) do
case sanity_check(args) do
{:ok, args} ->
Logger.info("#{port_executable()} #{Enum.join(args, " ")}")
{:ok, flutter_args} ->
Logger.info("flutter args: #{port_executable()} #{Enum.join(flutter_args, " ")}")

# LD_LIBRARY_PATH=/srv/erlang/lib/flutter_embedder-0.1.0/priv/ /srv/erlang/lib/flutter_embedder-0.1.0/priv/flutter_embedder /srv/erlang/lib/firmware-0.1.0/priv/flutter_assets /srv/erlang/lib/flutter_embedder-0.1.0/priv/icudtl.dat --disable-service-auth-codes --observatory-host 0.0.0.0 --observatory-port 43403 --disable-service-auth-codes --enable-service-port-fallback

port =
Port.open({:spawn_executable, port_executable()}, [
{:args, args},
{:args, flutter_args},
:binary,
:exit_status,
{:packet, 4},
Expand All @@ -38,48 +49,56 @@ defmodule FlutterEmbedder do
[{'LD_LIBRARY_PATH', to_charlist(Application.app_dir(:flutter_embedder, ["priv"]))}]}
])

{:ok, %__MODULE__{port: port, module: FlutterEmbedder.StubMethodCallHandler}}
{:ok,
%__MODULE__{
port: port,
module: args[:method_call_handler] || FlutterEmbedder.StubMethodCallHandler
}}
end
end

@impl true
def terminate(_, state) do
remove_mdns_service(state)
end

@impl GenServer
def handle_info({port, {:exit_status, status}}, %{port: port} = state) do
{:stop, {:flutter_embedder_crash, status}, state}
# {:stop, {:flutter_embedder_crash, status}, state}
Logger.error("Flutter embedder crashed: #{status}")
{:noreply, state}
end

def handle_info({port, {:data, <<1, _::32, log::binary>>}}, %{port: port} = state) do
def handle_info({port, {:data, <<1, log::binary>>}}, %{port: port} = state) do
Logger.info(log)

case log do
"flutter: Observatory listening on " <> uri ->
uri = URI.parse(String.trim(uri))
state = %{state | uri: uri}
# add_mdns_service(state)
{:noreply, state}

_ ->
{:noreply, state}
end
end

def handle_info({port, {:data, <<0, data::binary>>}}, %{port: port} = state) do
def handle_info({port, {:data, <<2, log::binary>>}}, %{port: port} = state) do
Logger.error(log)
{:noreply, state}
end

def handle_info({port, {:data, data}}, %{port: port} = state) do
platform_channel_message = PlatformChannelMessage.decode(data)
# Logger.info("#{inspect(platform_channel_message)}")
Logger.info("incomming call #{inspect(platform_channel_message)}")

case StandardMethodCall.decode(platform_channel_message) do
{:ok, call} ->
Logger.info("handling call: #{inspect(call)}")
handle_standard_call(platform_channel_message, call, state)

{:error, reason} ->
Logger.error(
"Could not decode #{platform_channel_message.channel} message as StandardMethodCall: #{
reason
} (this is probably ok)"
"Could not decode #{platform_channel_message.channel} message as StandardMethodCall: #{reason} (this is probably ok)"
)

reply_bin =
Expand All @@ -90,6 +109,26 @@ defmodule FlutterEmbedder do
end
end

@impl GenServer
def handle_cast({:send_platform_message, channel, data}, state) do
message_bin =
%FlutterEmbedder.PlatformChannelMessage{
channel: channel,
type: 0x0,
message: <<0x0::8, FlutterEmbedder.StandardMessageCodec.encode_value(data)::binary>>,
cookie: 255
}
|> FlutterEmbedder.PlatformChannelMessage.encode()

true = Port.command(state.port, message_bin)
{:noreply, state}
end

@impl GenServer
def handle_call(:observatory_url, _from, state) do
{:reply, state.uri, state}
end

def handle_standard_call(
%PlatformChannelMessage{channel: channel} = call,
%StandardMethodCall{method: method, args: args},
Expand All @@ -116,6 +155,11 @@ defmodule FlutterEmbedder do
:not_implemented ->
reply_bin = PlatformChannelMessage.encode_response(call, :not_implemented)
true = Port.command(state.port, reply_bin)

error ->
Logger.error(
"Failed to handle response from message handler: invalid value: #{inspect(error)}"
)
end

{:noreply, state}
Expand All @@ -132,7 +176,18 @@ defmodule FlutterEmbedder do
icudtl_file =
args[:icudtl_file] || Application.app_dir(:flutter_embedder, ["priv", "icudtl.dat"])

{:ok, ["#{flutter_assets}", "#{icudtl_file}"]}
{:ok,
[
"#{flutter_assets}",
"#{icudtl_file}",
"--disable-service-auth-codes",
"--observatory-host",
"0.0.0.0",
"--observatory-port",
"43403",
"--disable-service-auth-codes",
"--enable-service-port-fallback"
]}
end

@doc false
Expand All @@ -150,22 +205,4 @@ defmodule FlutterEmbedder do
Logger.info("Using #{exe} for flutter_embedder")
exe
end

def add_mdns_service(%{uri: uri}) do
services = [
%{
name: "Flutter Observatory",
protocol: "dartobservatory",
transport: "tcp",
port: uri.port,
txt_payload: [URI.encode_query(%{path: uri.path, port: uri.port})]
}
]

MdnsLite.add_mdns_services(services)
end

def remove_mdns_service(_state) do
MdnsLite.remove_mdns_services("Flutter Observatory")
end
end
143 changes: 0 additions & 143 deletions lib/flutter_embedder/mdns_client.ex

This file was deleted.

25 changes: 20 additions & 5 deletions lib/flutter_embedder/platform_channel_message.ex
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
defmodule FlutterEmbedder.PlatformChannelMessage do
defstruct [:cookie, :channel, :message]
defstruct [:type, :cookie, :channel, :message]

@type msg_type :: 0..255
@type cookie :: 0..255
@type channel :: String.t()
@type message :: binary()
@type t() :: %__MODULE__{
type: msg_type(),
cookie: cookie(),
channel: channel(),
message: message()
Expand All @@ -16,27 +18,40 @@ defmodule FlutterEmbedder.PlatformChannelMessage do

@spec decode(binary()) :: t()
def decode(
<<cookie::8, channel_length::little-16, channel::binary-size(channel_length),
<<type::8, cookie::8, channel_length::little-16, channel::binary-size(channel_length),
message_length::little-16, message::binary-size(message_length)>> = data
) do
%__MODULE__{
type: type,
cookie: cookie,
channel: channel,
message: message
}
end

def decode(_) do
{:error, "platform message decode fail"}
end

def encode(%__MODULE__{cookie: cookie, type: type, message: message, channel: channel}) do
channel_length = byte_size(channel)
message_length = byte_size(message)

<<type::8, cookie::8, channel_length::little-16, channel::binary-size(channel_length),
message_length::little-16, message::binary-size(message_length)>>
end

@spec encode_response(t(), ok_response() | error_response() | not_implemented_response()) ::
binary()
def encode_response(%__MODULE__{cookie: cookie}, {:ok, value}) when is_binary(value) do
<<cookie::8, 0, value::binary>>
<<0x1::8, cookie::8, 0, value::binary>>
end

def encode_response(%__MODULE__{cookie: cookie}, {:error, value}) when is_binary(value) do
<<cookie::8, 1, value::binary>>
<<0x1::8, cookie::8, 1, value::binary>>
end

def encode_response(%__MODULE__{cookie: cookie}, :not_implemented) do
<<cookie::8>>
<<0x1::8, cookie::8>>
end
end
Loading