blob: 3d8d53cddc6e94fa2033334edb233dc3c35568b6 [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 "engine.h"
#include <lib/async/cpp/task.h>
#include <zircon/status.h>
#include "../runtime/dart/utils/files.h"
#include "flutter/common/task_runners.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/synchronization/waitable_event.h"
#include "flutter/fml/task_runner.h"
#include "flutter/runtime/dart_vm_lifecycle.h"
#include "flutter/shell/common/rasterizer.h"
#include "flutter/shell/common/run_configuration.h"
#include "third_party/skia/include/ports/SkFontMgr_fuchsia.h"
#include "flutter_runner_product_configuration.h"
#include "fuchsia_external_view_embedder.h"
#include "fuchsia_intl.h"
#include "platform_view.h"
#include "surface.h"
#include "task_runner_adapter.h"
#include "thread.h"
#if defined(LEGACY_FUCHSIA_EMBEDDER)
#include "compositor_context.h" // nogncheck
#endif
namespace flutter_runner {
namespace {
void UpdateNativeThreadLabelNames(const std::string& label,
const flutter::TaskRunners& runners) {
auto set_thread_name = [](fml::RefPtr<fml::TaskRunner> runner,
std::string prefix, std::string suffix) {
if (!runner) {
return;
}
fml::TaskRunner::RunNowOrPostTask(runner, [name = prefix + suffix]() {
zx::thread::self()->set_property(ZX_PROP_NAME, name.c_str(), name.size());
});
};
set_thread_name(runners.GetPlatformTaskRunner(), label, ".platform");
set_thread_name(runners.GetUITaskRunner(), label, ".ui");
set_thread_name(runners.GetRasterTaskRunner(), label, ".raster");
set_thread_name(runners.GetIOTaskRunner(), label, ".io");
}
fml::RefPtr<flutter::PlatformMessage> MakeLocalizationPlatformMessage(
const fuchsia::intl::Profile& intl_profile) {
return fml::MakeRefCounted<flutter::PlatformMessage>(
"flutter/localization", MakeLocalizationPlatformMessageData(intl_profile),
nullptr);
}
} // namespace
Engine::Engine(Delegate& delegate,
std::string thread_label,
std::shared_ptr<sys::ServiceDirectory> svc,
std::shared_ptr<sys::ServiceDirectory> runner_services,
flutter::Settings settings,
fml::RefPtr<const flutter::DartSnapshot> isolate_snapshot,
fuchsia::ui::views::ViewToken view_token,
scenic::ViewRefPair view_ref_pair,
UniqueFDIONS fdio_ns,
fidl::InterfaceRequest<fuchsia::io::Directory> directory_request,
FlutterRunnerProductConfiguration product_config)
: delegate_(delegate),
thread_label_(std::move(thread_label)),
#if defined(LEGACY_FUCHSIA_EMBEDDER)
use_legacy_renderer_(product_config.use_legacy_renderer()),
#endif
weak_factory_(this) {
if (zx::event::create(0, &vsync_event_) != ZX_OK) {
FML_DLOG(ERROR) << "Could not create the vsync event.";
return;
}
// Get the task runners from the managed threads. The current thread will be
// used as the "platform" thread.
const flutter::TaskRunners task_runners(
thread_label_, // Dart thread labels
CreateFMLTaskRunner(async_get_default_dispatcher()), // platform
CreateFMLTaskRunner(threads_[0].dispatcher()), // raster
CreateFMLTaskRunner(threads_[1].dispatcher()), // ui
CreateFMLTaskRunner(threads_[2].dispatcher()) // io
);
UpdateNativeThreadLabelNames(thread_label_, task_runners);
// Connect to Scenic.
auto scenic = svc->Connect<fuchsia::ui::scenic::Scenic>();
fidl::InterfaceHandle<fuchsia::ui::scenic::Session> session;
fidl::InterfaceHandle<fuchsia::ui::scenic::SessionListener> session_listener;
auto session_listener_request = session_listener.NewRequest();
fidl::InterfaceHandle<fuchsia::ui::views::Focuser> focuser;
scenic->CreateSession2(session.NewRequest(), session_listener.Bind(),
focuser.NewRequest());
// Make clones of the `ViewRef` before sending it down to Scenic.
fuchsia::ui::views::ViewRef platform_view_ref, isolate_view_ref;
view_ref_pair.view_ref.Clone(&platform_view_ref);
view_ref_pair.view_ref.Clone(&isolate_view_ref);
// Session is terminated on the raster thread, but we must terminate ourselves
// on the platform thread.
//
// This handles the fidl error callback when the Session connection is
// broken. The SessionListener interface also has an OnError method, which is
// invoked on the platform thread (in PlatformView).
fml::closure session_error_callback = [dispatcher =
async_get_default_dispatcher(),
weak = weak_factory_.GetWeakPtr()]() {
async::PostTask(dispatcher, [weak]() {
if (weak) {
weak->Terminate();
}
});
};
// Set up the session connection and other Scenic helpers on the raster
// thread.
task_runners.GetRasterTaskRunner()->PostTask(fml::MakeCopyable(
[this, session = std::move(session),
session_error_callback = std::move(session_error_callback),
view_token = std::move(view_token),
view_ref_pair = std::move(view_ref_pair),
vsync_handle = vsync_event_.get()]() mutable {
session_connection_.emplace(
thread_label_, std::move(session),
std::move(session_error_callback), [](auto) {}, vsync_handle);
surface_producer_.emplace(session_connection_->get());
#if defined(LEGACY_FUCHSIA_EMBEDDER)
if (use_legacy_renderer_) {
legacy_external_view_embedder_.emplace(
thread_label_, std::move(view_token), std::move(view_ref_pair),
session_connection_.value());
} else
#endif
{
external_view_embedder_.emplace(
thread_label_, std::move(view_token), std::move(view_ref_pair),
session_connection_.value(), surface_producer_.value());
}
}));
// Grab the parent environment services. The platform view may want to
// access some of these services.
fuchsia::sys::EnvironmentPtr environment;
svc->Connect(environment.NewRequest());
fidl::InterfaceHandle<fuchsia::sys::ServiceProvider>
parent_environment_service_provider;
environment->GetServices(parent_environment_service_provider.NewRequest());
environment.Unbind();
OnEnableWireframe on_enable_wireframe_callback = std::bind(
&Engine::DebugWireframeSettingsChanged, this, std::placeholders::_1);
OnCreateView on_create_view_callback =
std::bind(&Engine::CreateView, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3);
OnUpdateView on_update_view_callback =
std::bind(&Engine::UpdateView, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3);
OnDestroyView on_destroy_view_callback =
std::bind(&Engine::DestroyView, this, std::placeholders::_1);
OnCreateSurface on_create_surface_callback =
std::bind(&Engine::CreateSurface, this);
// SessionListener has a OnScenicError method; invoke this callback on the
// platform thread when that happens. The Session itself should also be
// disconnected when this happens, and it will also attempt to terminate.
fit::closure on_session_listener_error_callback =
[dispatcher = async_get_default_dispatcher(),
weak = weak_factory_.GetWeakPtr()]() {
async::PostTask(dispatcher, [weak]() {
if (weak) {
weak->Terminate();
}
});
};
// Setup the callback that will instantiate the platform view.
flutter::Shell::CreateCallback<flutter::PlatformView>
on_create_platform_view = fml::MakeCopyable(
[debug_label = thread_label_, view_ref = std::move(platform_view_ref),
runner_services,
parent_environment_service_provider =
std::move(parent_environment_service_provider),
session_listener_request = std::move(session_listener_request),
focuser = std::move(focuser),
on_session_listener_error_callback =
std::move(on_session_listener_error_callback),
on_enable_wireframe_callback =
std::move(on_enable_wireframe_callback),
on_create_view_callback = std::move(on_create_view_callback),
on_update_view_callback = std::move(on_update_view_callback),
on_destroy_view_callback = std::move(on_destroy_view_callback),
on_create_surface_callback = std::move(on_create_surface_callback),
vsync_offset = product_config.get_vsync_offset(),
vsync_handle = vsync_event_.get()](flutter::Shell& shell) mutable {
return std::make_unique<flutter_runner::PlatformView>(
shell, // delegate
debug_label, // debug label
std::move(view_ref), // view ref
shell.GetTaskRunners(), // task runners
std::move(runner_services),
std::move(parent_environment_service_provider), // services
std::move(session_listener_request), // session listener
std::move(focuser),
std::move(on_session_listener_error_callback),
std::move(on_enable_wireframe_callback),
std::move(on_create_view_callback),
std::move(on_update_view_callback),
std::move(on_destroy_view_callback),
std::move(on_create_surface_callback),
std::move(vsync_offset), // vsync offset
vsync_handle);
});
// Setup the callback that will instantiate the rasterizer.
flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer;
#if defined(LEGACY_FUCHSIA_EMBEDDER)
on_create_rasterizer = [this](flutter::Shell& shell) {
if (use_legacy_renderer_) {
FML_DCHECK(session_connection_);
FML_DCHECK(surface_producer_);
FML_DCHECK(legacy_external_view_embedder_);
auto compositor_context =
std::make_unique<flutter_runner::CompositorContext>(
session_connection_.value(), surface_producer_.value(),
legacy_external_view_embedder_.value());
return std::make_unique<flutter::Rasterizer>(
shell, std::move(compositor_context));
} else {
return std::make_unique<flutter::Rasterizer>(shell);
}
};
#else
on_create_rasterizer = [](flutter::Shell& shell) {
return std::make_unique<flutter::Rasterizer>(shell);
};
#endif
settings.root_isolate_create_callback =
std::bind(&Engine::OnMainIsolateStart, this);
settings.root_isolate_shutdown_callback =
std::bind([weak = weak_factory_.GetWeakPtr(),
runner = task_runners.GetPlatformTaskRunner()]() {
runner->PostTask([weak = std::move(weak)] {
if (weak) {
weak->OnMainIsolateShutdown();
}
});
});
auto vm = flutter::DartVMRef::Create(settings);
if (!isolate_snapshot) {
isolate_snapshot = vm->GetVMData()->GetIsolateSnapshot();
}
{
TRACE_EVENT0("flutter", "CreateShell");
shell_ = flutter::Shell::Create(
std::move(task_runners), // host task runners
flutter::PlatformData(), // default window data
std::move(settings), // shell launch settings
std::move(isolate_snapshot), // isolate snapshot
std::move(on_create_platform_view), // platform view create callback
std::move(on_create_rasterizer), // rasterizer create callback
std::move(vm) // vm reference
);
}
if (!shell_) {
FML_LOG(ERROR) << "Could not launch the shell.";
return;
}
// Shell has been created. Before we run the engine, setup the isolate
// configurator.
{
fuchsia::sys::EnvironmentPtr environment;
svc->Connect(environment.NewRequest());
isolate_configurator_ = std::make_unique<IsolateConfigurator>(
std::move(fdio_ns), //
std::move(environment), //
directory_request.TakeChannel(), //
std::move(isolate_view_ref.reference) //
);
}
// This platform does not get a separate surface platform view creation
// notification. Fire one eagerly.
shell_->GetPlatformView()->NotifyCreated();
// Connect to the intl property provider. If the connection fails, the
// initialization of the engine will simply proceed, printing a warning
// message. The engine will be fully functional, except that the user's
// locale preferences would not be communicated to flutter engine.
{
intl_property_provider_.set_error_handler([](zx_status_t status) {
FML_LOG(WARNING) << "Failed to connect to "
<< fuchsia::intl::PropertyProvider::Name_ << ": "
<< zx_status_get_string(status)
<< " This is not a fatal error, but the user locale "
<< " preferences will not be forwarded to flutter apps";
});
// Note that we're using the runner's services, not the component's.
// Flutter locales should be updated regardless of whether the component has
// direct access to the fuchsia.intl.PropertyProvider service.
ZX_ASSERT(runner_services->Connect(intl_property_provider_.NewRequest()) ==
ZX_OK);
auto get_profile_callback = [flutter_runner_engine =
weak_factory_.GetWeakPtr()](
const fuchsia::intl::Profile& profile) {
if (!flutter_runner_engine) {
return;
}
if (!profile.has_locales()) {
FML_LOG(WARNING) << "Got intl Profile without locales";
}
auto message = MakeLocalizationPlatformMessage(profile);
FML_VLOG(-1) << "Sending LocalizationPlatformMessage";
flutter_runner_engine->shell_->GetPlatformView()->DispatchPlatformMessage(
message);
};
FML_VLOG(-1) << "Requesting intl Profile";
// Make the initial request
intl_property_provider_->GetProfile(get_profile_callback);
// And register for changes
intl_property_provider_.events().OnChange = [this, runner_services,
get_profile_callback]() {
FML_VLOG(-1) << fuchsia::intl::PropertyProvider::Name_ << ": OnChange";
runner_services->Connect(intl_property_provider_.NewRequest());
intl_property_provider_->GetProfile(get_profile_callback);
};
}
// Launch the engine in the appropriate configuration.
auto run_configuration = flutter::RunConfiguration::InferFromSettings(
shell_->GetSettings(), shell_->GetTaskRunners().GetIOTaskRunner());
auto on_run_failure = [weak = weak_factory_.GetWeakPtr()]() {
// The engine could have been killed by the caller right after the
// constructor was called but before it could run on the UI thread.
if (weak) {
weak->Terminate();
}
};
// Connect to the system font provider.
fuchsia::fonts::ProviderSyncPtr sync_font_provider;
svc->Connect(sync_font_provider.NewRequest());
shell_->GetTaskRunners().GetUITaskRunner()->PostTask(
fml::MakeCopyable([engine = shell_->GetEngine(), //
run_configuration = std::move(run_configuration), //
sync_font_provider = std::move(sync_font_provider), //
on_run_failure //
]() mutable {
if (!engine) {
return;
}
// Set default font manager.
engine->GetFontCollection().GetFontCollection()->SetDefaultFontManager(
SkFontMgr_New_Fuchsia(std::move(sync_font_provider)));
if (engine->Run(std::move(run_configuration)) ==
flutter::Engine::RunStatus::Failure) {
on_run_failure();
}
}));
}
Engine::~Engine() {
shell_.reset();
for (auto& thread : threads_) {
thread.Quit();
}
for (auto& thread : threads_) {
thread.Join();
}
}
std::pair<bool, uint32_t> Engine::GetEngineReturnCode() const {
std::pair<bool, uint32_t> code(false, 0);
if (!shell_) {
return code;
}
fml::AutoResetWaitableEvent latch;
fml::TaskRunner::RunNowOrPostTask(
shell_->GetTaskRunners().GetUITaskRunner(),
[&latch, &code, engine = shell_->GetEngine()]() {
if (engine) {
code = engine->GetUIIsolateReturnCode();
}
latch.Signal();
});
latch.Wait();
return code;
}
static void CreateCompilationTrace(Dart_Isolate isolate) {
Dart_EnterIsolate(isolate);
{
Dart_EnterScope();
uint8_t* trace = nullptr;
intptr_t trace_length = 0;
Dart_Handle result = Dart_SaveCompilationTrace(&trace, &trace_length);
tonic::LogIfError(result);
for (intptr_t start = 0; start < trace_length;) {
intptr_t end = start;
while ((end < trace_length) && trace[end] != '\n')
end++;
std::string line(reinterpret_cast<char*>(&trace[start]), end - start);
FML_LOG(INFO) << "compilation-trace: " << line;
start = end + 1;
}
Dart_ExitScope();
}
// Re-enter Dart scope to release the compilation trace's memory.
{
Dart_EnterScope();
uint8_t* feedback = nullptr;
intptr_t feedback_length = 0;
Dart_Handle result = Dart_SaveTypeFeedback(&feedback, &feedback_length);
tonic::LogIfError(result);
const std::string kTypeFeedbackFile = "/data/dart_type_feedback.bin";
if (dart_utils::WriteFile(kTypeFeedbackFile,
reinterpret_cast<const char*>(feedback),
feedback_length)) {
FML_LOG(INFO) << "Dart type feedback written to " << kTypeFeedbackFile;
} else {
FML_LOG(ERROR) << "Could not write Dart type feedback to "
<< kTypeFeedbackFile;
}
Dart_ExitScope();
}
Dart_ExitIsolate();
}
void Engine::OnMainIsolateStart() {
if (!isolate_configurator_ ||
!isolate_configurator_->ConfigureCurrentIsolate()) {
FML_LOG(ERROR) << "Could not configure some native embedder bindings for a "
"new root isolate.";
}
FML_DLOG(INFO) << "Main isolate for engine '" << thread_label_
<< "' was started.";
const intptr_t kCompilationTraceDelayInSeconds = 0;
if (kCompilationTraceDelayInSeconds != 0) {
Dart_Isolate isolate = Dart_CurrentIsolate();
FML_CHECK(isolate);
shell_->GetTaskRunners().GetUITaskRunner()->PostDelayedTask(
[engine = shell_->GetEngine(), isolate]() {
if (!engine) {
return;
}
CreateCompilationTrace(isolate);
},
fml::TimeDelta::FromSeconds(kCompilationTraceDelayInSeconds));
}
}
void Engine::OnMainIsolateShutdown() {
FML_DLOG(INFO) << "Main isolate for engine '" << thread_label_
<< "' shutting down.";
Terminate();
}
void Engine::Terminate() {
delegate_.OnEngineTerminate(this);
// Warning. Do not do anything after this point as the delegate may have
// collected this object.
}
void Engine::DebugWireframeSettingsChanged(bool enabled) {
FML_CHECK(shell_);
shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask([this, enabled]() {
#if defined(LEGACY_FUCHSIA_EMBEDDER)
if (use_legacy_renderer_) {
FML_CHECK(legacy_external_view_embedder_);
legacy_external_view_embedder_->EnableWireframe(enabled);
} else
#endif
{
FML_CHECK(external_view_embedder_);
external_view_embedder_->EnableWireframe(enabled);
}
});
}
void Engine::CreateView(int64_t view_id, bool hit_testable, bool focusable) {
FML_CHECK(shell_);
shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(
[this, view_id, hit_testable, focusable]() {
#if defined(LEGACY_FUCHSIA_EMBEDDER)
if (use_legacy_renderer_) {
FML_CHECK(legacy_external_view_embedder_);
legacy_external_view_embedder_->CreateView(view_id, hit_testable,
focusable);
} else
#endif
{
FML_CHECK(external_view_embedder_);
external_view_embedder_->CreateView(view_id);
external_view_embedder_->SetViewProperties(view_id, hit_testable,
focusable);
}
});
}
void Engine::UpdateView(int64_t view_id, bool hit_testable, bool focusable) {
FML_CHECK(shell_);
shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(
[this, view_id, hit_testable, focusable]() {
#if defined(LEGACY_FUCHSIA_EMBEDDER)
if (use_legacy_renderer_) {
FML_CHECK(legacy_external_view_embedder_);
legacy_external_view_embedder_->UpdateView(view_id, hit_testable,
focusable);
} else
#endif
{
FML_CHECK(external_view_embedder_);
external_view_embedder_->SetViewProperties(view_id, hit_testable,
focusable);
}
});
}
void Engine::DestroyView(int64_t view_id) {
FML_CHECK(shell_);
shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask([this, view_id]() {
#if defined(LEGACY_FUCHSIA_EMBEDDER)
if (use_legacy_renderer_) {
FML_CHECK(legacy_external_view_embedder_);
legacy_external_view_embedder_->DestroyView(view_id);
} else
#endif
{
FML_CHECK(external_view_embedder_);
external_view_embedder_->DestroyView(view_id);
}
});
}
std::unique_ptr<flutter::Surface> Engine::CreateSurface() {
flutter::ExternalViewEmbedder* external_view_embedder = nullptr;
#if defined(LEGACY_FUCHSIA_EMBEDDER)
if (use_legacy_renderer_) {
FML_CHECK(legacy_external_view_embedder_);
external_view_embedder = &legacy_external_view_embedder_.value();
} else
#endif
{
FML_CHECK(external_view_embedder_);
external_view_embedder = &external_view_embedder_.value();
}
FML_CHECK(external_view_embedder);
return std::make_unique<Surface>(thread_label_, external_view_embedder,
surface_producer_->gr_context());
}
#if !defined(DART_PRODUCT)
void Engine::WriteProfileToTrace() const {
Dart_Port main_port = shell_->GetEngine()->GetUIIsolateMainPort();
char* error = NULL;
bool success = Dart_WriteProfileToTimeline(main_port, &error);
if (!success) {
FML_LOG(ERROR) << "Failed to write Dart profile to trace: " << error;
free(error);
}
}
#endif // !defined(DART_PRODUCT)
} // namespace flutter_runner