Files
engine/shell/platform/fuchsia/flutter/engine.cc
T
Filip Filmar 20e3c5bb83 Revert "[fuchsia] Temporarily disable intl provider (#13696)" (#13721)
This reverts commit 6c763bb551.

The reverted code was not the root cause of the issues with rolling
flutter into fuchsia, so adding it back.

In addition, lowering the severity of the connection error at the outset
to WARNING; since it is not a hard failure.
2019-11-08 17:34:04 -08:00

529 lines
19 KiB
C++

// 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 <sstream>
#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 "fuchsia_intl.h"
#include "platform_view.h"
#include "runtime/dart/utils/files.h"
#include "task_runner_adapter.h"
#include "third_party/skia/include/ports/SkFontMgr_fuchsia.h"
#include "thread.h"
namespace flutter_runner {
static 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.GetGPUTaskRunner(), label, ".gpu");
set_thread_name(runners.GetIOTaskRunner(), label, ".io");
}
static fml::RefPtr<flutter::PlatformMessage> MakeLocalizationPlatformMessage(
const fuchsia::intl::Profile& intl_profile) {
return fml::MakeRefCounted<flutter::PlatformMessage>(
"flutter/localization", MakeLocalizationPlatformMessageData(intl_profile),
nullptr);
}
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,
fuchsia::ui::views::ViewRefControl view_ref_control,
fuchsia::ui::views::ViewRef view_ref,
UniqueFDIONS fdio_ns,
fidl::InterfaceRequest<fuchsia::io::Directory> directory_request)
: delegate_(delegate),
thread_label_(std::move(thread_label)),
settings_(std::move(settings)),
weak_factory_(this) {
if (zx::event::create(0, &vsync_event_) != ZX_OK) {
FML_DLOG(ERROR) << "Could not create the vsync event.";
return;
}
// Launch the threads that will be used to run the shell. These threads will
// be joined in the destructor.
for (auto& thread : threads_) {
thread.reset(new Thread());
}
// Set up the session connection.
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();
scenic->CreateSession(session.NewRequest(), session_listener.Bind());
// 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();
// We need to manually schedule a frame when the session metrics change.
OnMetricsUpdate on_session_metrics_change_callback = std::bind(
&Engine::OnSessionMetricsDidChange, this, std::placeholders::_1);
OnSizeChangeHint on_session_size_change_hint_callback =
std::bind(&Engine::OnSessionSizeChangeHint, this, std::placeholders::_1,
std::placeholders::_2);
OnEnableWireframe on_enable_wireframe_callback = std::bind(
&Engine::OnDebugWireframeSettingsChanged, this, std::placeholders::_1);
// 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_control = std::move(view_ref_control),
view_ref = std::move(view_ref), runner_services,
parent_environment_service_provider =
std::move(parent_environment_service_provider),
session_listener_request = std::move(session_listener_request),
on_session_listener_error_callback =
std::move(on_session_listener_error_callback),
on_session_metrics_change_callback =
std::move(on_session_metrics_change_callback),
on_session_size_change_hint_callback =
std::move(on_session_size_change_hint_callback),
on_enable_wireframe_callback =
std::move(on_enable_wireframe_callback),
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_control), // view control ref
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(on_session_listener_error_callback),
std::move(on_session_metrics_change_callback),
std::move(on_session_size_change_hint_callback),
std::move(on_enable_wireframe_callback),
vsync_handle // vsync handle
);
});
// Session can be terminated on the GPU 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 on_session_error_callback =
[dispatcher = async_get_default_dispatcher(),
weak = weak_factory_.GetWeakPtr()]() {
async::PostTask(dispatcher, [weak]() {
if (weak) {
weak->Terminate();
}
});
};
// 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()), // gpu
CreateFMLTaskRunner(threads_[1]->dispatcher()), // ui
CreateFMLTaskRunner(threads_[2]->dispatcher()) // io
);
// Setup the callback that will instantiate the rasterizer.
flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
fml::MakeCopyable([thread_label = thread_label_, //
view_token = std::move(view_token), //
session = std::move(session), //
on_session_error_callback, //
vsync_event = vsync_event_.get() //
](flutter::Shell& shell) mutable {
std::unique_ptr<flutter_runner::CompositorContext> compositor_context;
{
TRACE_DURATION("flutter", "CreateCompositorContext");
compositor_context =
std::make_unique<flutter_runner::CompositorContext>(
thread_label, // debug label
std::move(view_token), // scenic view we attach our tree to
std::move(session), // scenic session
on_session_error_callback, // session did encounter error
vsync_event // vsync event handle
);
}
return std::make_unique<flutter::Rasterizer>(
shell.GetTaskRunners(), // task runners
std::move(compositor_context) // compositor context
);
});
UpdateNativeThreadLabelNames(thread_label_, task_runners);
settings_.verbose_logging = true;
settings_.advisory_script_uri = thread_label_;
settings_.advisory_script_entrypoint = thread_label_;
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(
task_runners, // host task runners
settings_, // shell launch settings
std::move(isolate_snapshot), // isolate snapshot
on_create_platform_view, // platform view create callback
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() //
);
}
// 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(
settings_, task_runners.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 (const auto& thread : threads_) {
thread->Quit();
}
for (const 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::OnSessionMetricsDidChange(
const fuchsia::ui::gfx::Metrics& metrics) {
if (!shell_) {
return;
}
shell_->GetTaskRunners().GetGPUTaskRunner()->PostTask(
[rasterizer = shell_->GetRasterizer(), metrics]() {
if (rasterizer) {
auto compositor_context =
reinterpret_cast<flutter_runner::CompositorContext*>(
rasterizer->compositor_context());
compositor_context->OnSessionMetricsDidChange(metrics);
}
});
}
void Engine::OnDebugWireframeSettingsChanged(bool enabled) {
if (!shell_) {
return;
}
shell_->GetTaskRunners().GetGPUTaskRunner()->PostTask(
[rasterizer = shell_->GetRasterizer(), enabled]() {
if (rasterizer) {
auto compositor_context =
reinterpret_cast<flutter_runner::CompositorContext*>(
rasterizer->compositor_context());
compositor_context->OnWireframeEnabled(enabled);
}
});
}
void Engine::OnSessionSizeChangeHint(float width_change_factor,
float height_change_factor) {
if (!shell_) {
return;
}
shell_->GetTaskRunners().GetGPUTaskRunner()->PostTask(
[rasterizer = shell_->GetRasterizer(), width_change_factor,
height_change_factor]() {
if (rasterizer) {
auto compositor_context = reinterpret_cast<CompositorContext*>(
rasterizer->compositor_context());
compositor_context->OnSessionSizeChangeHint(width_change_factor,
height_change_factor);
}
});
}
#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