From ea7ca9804a99b1269b198338b2fbcdf095bb2460 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Mon, 17 Jun 2019 16:02:26 -0700 Subject: [PATCH] Send the isolate service ID from the engine to the embedder (#9324) Applications can use an embedder API to obtain the isolate ID and then use it in calls to the Dart service protocol. --- runtime/dart_isolate.cc | 7 +++ runtime/dart_isolate.h | 2 + shell/common/engine.cc | 9 ++++ .../embedding/engine/dart/DartExecutor.java | 43 +++++++++++++++++- .../embedder/tests/embedder_config_builder.cc | 10 +++++ .../embedder/tests/embedder_config_builder.h | 3 ++ .../embedder/tests/embedder_context.cc | 12 +++++ .../embedder/tests/embedder_context.h | 6 +++ .../embedder/tests/embedder_unittests.cc | 45 +++++++++++++++++++ 9 files changed, 136 insertions(+), 1 deletion(-) diff --git a/runtime/dart_isolate.cc b/runtime/dart_isolate.cc index 345792cadfc..986fe554a6e 100644 --- a/runtime/dart_isolate.cc +++ b/runtime/dart_isolate.cc @@ -141,6 +141,13 @@ DartIsolate::Phase DartIsolate::GetPhase() const { return phase_; } +std::string DartIsolate::GetServiceId() { + const char* service_id_buf = Dart_IsolateServiceId(isolate()); + std::string service_id(service_id_buf); + free(const_cast(service_id_buf)); + return service_id; +} + bool DartIsolate::Initialize(Dart_Isolate dart_isolate, bool is_root_isolate) { TRACE_EVENT0("flutter", "DartIsolate::Initialize"); if (phase_ != Phase::Uninitialized) { diff --git a/runtime/dart_isolate.h b/runtime/dart_isolate.h index d99de870914..407852dc2a0 100644 --- a/runtime/dart_isolate.h +++ b/runtime/dart_isolate.h @@ -73,6 +73,8 @@ class DartIsolate : public UIDartState { Phase GetPhase() const; + std::string GetServiceId(); + FML_WARN_UNUSED_RESULT bool PrepareForRunningFromPrecompiledCode(); diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 3b38e577ab2..5147789d706 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -33,6 +33,7 @@ static constexpr char kLifecycleChannel[] = "flutter/lifecycle"; static constexpr char kNavigationChannel[] = "flutter/navigation"; static constexpr char kLocalizationChannel[] = "flutter/localization"; static constexpr char kSettingsChannel[] = "flutter/settings"; +static constexpr char kIsolateChannel[] = "flutter/isolate"; Engine::Engine(Delegate& delegate, DartVM& vm, @@ -145,6 +146,14 @@ Engine::RunStatus Engine::Run(RunConfiguration configuration) { isolate->AddIsolateShutdownCallback( settings_.root_isolate_shutdown_callback); } + + std::string service_id = isolate->GetServiceId(); + fml::RefPtr service_id_message = + fml::MakeRefCounted( + kIsolateChannel, + std::vector(service_id.begin(), service_id.end()), + nullptr); + HandlePlatformMessage(service_id_message); } return isolate_running ? Engine::RunStatus::Success diff --git a/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java b/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java index ffbbec06d86..54657dc8563 100644 --- a/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java +++ b/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java @@ -14,6 +14,7 @@ import java.nio.ByteBuffer; import io.flutter.embedding.engine.FlutterJNI; import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.StringCodec; import io.flutter.view.FlutterCallbackInformation; /** @@ -44,10 +45,24 @@ public class DartExecutor implements BinaryMessenger { @NonNull private final DartMessenger messenger; private boolean isApplicationRunning = false; + private String isolateServiceId; + private IsolateServiceIdListener isolateServiceIdListener; + + private final BinaryMessenger.BinaryMessageHandler isolateChannelMessageHandler = + new BinaryMessenger.BinaryMessageHandler() { + @Override + public void onMessage(ByteBuffer message, final BinaryReply callback) { + isolateServiceId = StringCodec.INSTANCE.decodeMessage(message); + if (isolateServiceIdListener != null) { + isolateServiceIdListener.onIsolateServiceIdAvailable(isolateServiceId); + } + } + }; public DartExecutor(@NonNull FlutterJNI flutterJNI) { this.flutterJNI = flutterJNI; this.messenger = new DartMessenger(flutterJNI); + messenger.setMessageHandler("flutter/isolate", isolateChannelMessageHandler); } /** @@ -181,6 +196,32 @@ public class DartExecutor implements BinaryMessenger { } //------ END BinaryMessenger ----- + /** + * Returns an identifier for this executor's primary isolate. This identifier can be used + * in queries to the Dart service protocol. + */ + public String getIsolateServiceId() { + return isolateServiceId; + } + + /** + * Callback interface invoked when the isolate identifier becomes available. + */ + interface IsolateServiceIdListener { + void onIsolateServiceIdAvailable(String isolateServiceId); + } + + /** + * Set a listener that will be notified when an isolate identifier is available for this + * executor's primary isolate. + */ + public void setIsolateServiceIdListener(IsolateServiceIdListener listener) { + isolateServiceIdListener = listener; + if (isolateServiceIdListener != null && isolateServiceId != null) { + isolateServiceIdListener.onIsolateServiceIdAvailable(isolateServiceId); + } + } + /** * Configuration options that specify which Dart entrypoint function is executed and where * to find that entrypoint and other assets required for Dart execution. @@ -286,4 +327,4 @@ public class DartExecutor implements BinaryMessenger { this.callbackHandle = callbackHandle; } } -} \ No newline at end of file +} diff --git a/shell/platform/embedder/tests/embedder_config_builder.cc b/shell/platform/embedder/tests/embedder_config_builder.cc index 9021eaee1f9..fc077dcfa11 100644 --- a/shell/platform/embedder/tests/embedder_config_builder.cc +++ b/shell/platform/embedder/tests/embedder_config_builder.cc @@ -12,6 +12,11 @@ EmbedderConfigBuilder::EmbedderConfigBuilder( InitializationPreference preference) : context_(context) { project_args_.struct_size = sizeof(project_args_); + project_args_.platform_message_callback = + [](const FlutterPlatformMessage* message, void* context) { + reinterpret_cast(context)->PlatformMessageCallback( + message); + }; custom_task_runners_.struct_size = sizeof(FlutterCustomTaskRunners); @@ -126,6 +131,11 @@ void EmbedderConfigBuilder::SetPlatformTaskRunner( project_args_.custom_task_runners = &custom_task_runners_; } +void EmbedderConfigBuilder::SetPlatformMessageCallback( + std::function callback) { + context_.SetPlatformMessageCallback(callback); +} + UniqueEngine EmbedderConfigBuilder::LaunchEngine() { FlutterEngine engine = nullptr; diff --git a/shell/platform/embedder/tests/embedder_config_builder.h b/shell/platform/embedder/tests/embedder_config_builder.h index 1c05f835cc3..44bae1f227d 100644 --- a/shell/platform/embedder/tests/embedder_config_builder.h +++ b/shell/platform/embedder/tests/embedder_config_builder.h @@ -58,6 +58,9 @@ class EmbedderConfigBuilder { void SetPlatformTaskRunner(const FlutterTaskRunnerDescription* runner); + void SetPlatformMessageCallback( + std::function callback); + UniqueEngine LaunchEngine(); private: diff --git a/shell/platform/embedder/tests/embedder_context.cc b/shell/platform/embedder/tests/embedder_context.cc index bf066ff1079..ae759d66731 100644 --- a/shell/platform/embedder/tests/embedder_context.cc +++ b/shell/platform/embedder/tests/embedder_context.cc @@ -91,6 +91,18 @@ void EmbedderContext::SetSemanticsCustomActionCallback( update_semantics_custom_action_callback; } +void EmbedderContext::SetPlatformMessageCallback( + std::function callback) { + platform_message_callback_ = callback; +} + +void EmbedderContext::PlatformMessageCallback( + const FlutterPlatformMessage* message) { + if (platform_message_callback_) { + platform_message_callback_(message); + } +} + FlutterUpdateSemanticsNodeCallback EmbedderContext::GetUpdateSemanticsNodeCallbackHook() { return [](const FlutterSemanticsNode* semantics_node, void* user_data) { diff --git a/shell/platform/embedder/tests/embedder_context.h b/shell/platform/embedder/tests/embedder_context.h index c2d6d3c7528..7e9754e0962 100644 --- a/shell/platform/embedder/tests/embedder_context.h +++ b/shell/platform/embedder/tests/embedder_context.h @@ -49,6 +49,9 @@ class EmbedderContext { void SetSemanticsCustomActionCallback( SemanticsActionCallback semantics_custom_action); + void SetPlatformMessageCallback( + std::function callback); + private: // This allows the builder to access the hooks. friend class EmbedderConfigBuilder; @@ -63,6 +66,7 @@ class EmbedderContext { SemanticsNodeCallback update_semantics_node_callback_; SemanticsActionCallback update_semantics_custom_action_callback_; std::unique_ptr gl_surface_; // lazy + std::function platform_message_callback_; static VoidCallback GetIsolateCreateCallbackHook(); @@ -90,6 +94,8 @@ class EmbedderContext { void* GLGetProcAddress(const char* name); + void PlatformMessageCallback(const FlutterPlatformMessage* message); + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderContext); }; diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 4dcf2d17209..58c75b3292e 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -216,5 +216,50 @@ TEST_F(EmbedderTest, CanCreateOpenGLRenderingEngine) { ASSERT_TRUE(engine.is_valid()); } +TEST_F(EmbedderTest, IsolateServiceIdSent) { + auto& context = GetEmbedderContext(); + fml::AutoResetWaitableEvent latch; + + fml::Thread thread; + UniqueEngine engine; + std::string isolate_message; + + EmbedderTestTaskRunner runner( + [&](FlutterTask task) { FlutterEngineRunTask(engine.get(), &task); }); + + thread.GetTaskRunner()->PostTask([&]() { + EmbedderConfigBuilder builder(context); + const auto task_runner_description = runner.GetEmbedderDescription(); + runner.SetForwardingTaskRunner( + fml::MessageLoop::GetCurrent().GetTaskRunner()); + builder.SetPlatformTaskRunner(&task_runner_description); + builder.SetDartEntrypoint("main"); + builder.SetPlatformMessageCallback( + [&](const FlutterPlatformMessage* message) { + if (strcmp(message->channel, "flutter/isolate") == 0) { + isolate_message = {reinterpret_cast(message->message), + message->message_size}; + latch.Signal(); + } + }); + engine = builder.LaunchEngine(); + ASSERT_TRUE(engine.is_valid()); + }); + + // Wait for the isolate ID message and check its format. + latch.Wait(); + ASSERT_EQ(isolate_message.find("isolates/"), 0ul); + + // Since the engine was started on its own thread, it must be killed there as + // well. + fml::AutoResetWaitableEvent kill_latch; + thread.GetTaskRunner()->PostTask( + fml::MakeCopyable([&engine, &kill_latch]() mutable { + engine.reset(); + kill_latch.Signal(); + })); + kill_latch.Wait(); +} + } // namespace testing } // namespace flutter