mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[fuchsia] Create CF v2 Flutter runner. (flutter/engine#29142)
This commit is contained in:
parent
25ae1667f2
commit
f8d6431176
@ -1413,9 +1413,11 @@ FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/vmservice/meta/vmservi
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/accessibility_bridge.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/accessibility_bridge.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/accessibility_bridge_unittest.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/component.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/component.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/component_unittest.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/component_v1.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/component_v1.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/component_v1_unittest.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/component_v2.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/component_v2.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/engine.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/engine.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/file_in_namespace_buffer.cc
|
||||
|
||||
@ -354,6 +354,13 @@ void DartComponentControllerV2::Run() {
|
||||
loop_->Run();
|
||||
|
||||
if (binding_.is_bound()) {
|
||||
// TODO(fxb/79871): This is likely a bug. We're taking the return_code
|
||||
// of the process (a uint32_t) and implicitly converting it into a
|
||||
// zx_status_t that is used as the epitaph. The uint32_t return code is
|
||||
// unlikely to correspond to the epitaph status that is expected to close
|
||||
// the connection (with the exception of 0 == ZX_OK). For the documentation
|
||||
// of what epitaph status we should choose, see
|
||||
// https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/fidl/fuchsia.component.runner/component_runner.fidl;l=118;drc=e3b39f2b57e720770773b857feca4f770ee0619e
|
||||
binding_.Close(return_code_);
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,8 +57,10 @@ template("runner_sources") {
|
||||
sources = [
|
||||
"accessibility_bridge.cc",
|
||||
"accessibility_bridge.h",
|
||||
"component.cc",
|
||||
"component.h",
|
||||
"component_v1.cc",
|
||||
"component_v1.h",
|
||||
"component_v2.cc",
|
||||
"component_v2.h",
|
||||
"engine.cc",
|
||||
"engine.h",
|
||||
"file_in_namespace_buffer.cc",
|
||||
@ -138,6 +140,7 @@ template("runner_sources") {
|
||||
|
||||
deps = [
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.accessibility.semantics",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.component.runner",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.fonts",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.images",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.intl",
|
||||
@ -465,7 +468,7 @@ executable("flutter_runner_unittests") {
|
||||
|
||||
sources = [
|
||||
"accessibility_bridge_unittest.cc",
|
||||
"component_unittest.cc",
|
||||
"component_v1_unittest.cc",
|
||||
"flutter_runner_fakes.h",
|
||||
"focus_delegate_unittests.cc",
|
||||
"fuchsia_intl_unittest.cc",
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "component.h"
|
||||
#include "component_v1.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <fuchsia/mem/cpp/fidl.h>
|
||||
@ -59,7 +59,7 @@ std::string DebugLabelForUrl(const std::string& url) {
|
||||
|
||||
} // namespace
|
||||
|
||||
void Component::ParseProgramMetadata(
|
||||
void ComponentV1::ParseProgramMetadata(
|
||||
const fidl::VectorPtr<fuchsia::sys::ProgramMetadata>& program_metadata,
|
||||
std::string* data_path,
|
||||
std::string* assets_path) {
|
||||
@ -80,21 +80,21 @@ void Component::ParseProgramMetadata(
|
||||
}
|
||||
}
|
||||
|
||||
ActiveComponent Component::Create(
|
||||
ActiveComponentV1 ComponentV1::Create(
|
||||
TerminationCallback termination_callback,
|
||||
fuchsia::sys::Package package,
|
||||
fuchsia::sys::StartupInfo startup_info,
|
||||
std::shared_ptr<sys::ServiceDirectory> runner_incoming_services,
|
||||
fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller) {
|
||||
auto thread = std::make_unique<fml::Thread>();
|
||||
std::unique_ptr<Component> component;
|
||||
std::unique_ptr<ComponentV1> component;
|
||||
|
||||
fml::AutoResetWaitableEvent latch;
|
||||
thread->GetTaskRunner()->PostTask([&]() mutable {
|
||||
component.reset(new Component(std::move(termination_callback),
|
||||
std::move(package), std::move(startup_info),
|
||||
runner_incoming_services,
|
||||
std::move(controller)));
|
||||
component.reset(new ComponentV1(std::move(termination_callback),
|
||||
std::move(package), std::move(startup_info),
|
||||
runner_incoming_services,
|
||||
std::move(controller)));
|
||||
latch.Signal();
|
||||
});
|
||||
|
||||
@ -103,7 +103,7 @@ ActiveComponent Component::Create(
|
||||
.component = std::move(component)};
|
||||
}
|
||||
|
||||
Component::Component(
|
||||
ComponentV1::ComponentV1(
|
||||
TerminationCallback termination_callback,
|
||||
fuchsia::sys::Package package,
|
||||
fuchsia::sys::StartupInfo startup_info,
|
||||
@ -215,9 +215,9 @@ Component::Component(
|
||||
[this](zx_status_t status, std::unique_ptr<fuchsia::io::NodeInfo> info) {
|
||||
cloned_directory_ptr_.Unbind();
|
||||
if (status != ZX_OK) {
|
||||
FML_LOG(ERROR) << "could not bind out directory for flutter app("
|
||||
<< debug_label_
|
||||
<< "): " << zx_status_get_string(status);
|
||||
FML_LOG(ERROR)
|
||||
<< "could not bind out directory for flutter component("
|
||||
<< debug_label_ << "): " << zx_status_get_string(status);
|
||||
return;
|
||||
}
|
||||
const char* other_dirs[] = {"debug", "ctrl", "diagnostics"};
|
||||
@ -433,7 +433,7 @@ Component::Component(
|
||||
// happening on the UI thread. If the Component dtor and thread
|
||||
// termination happen (on the platform thread) between the previous
|
||||
// line and the next line, a crash will occur since we'll be posting
|
||||
// to a dead thread. See Runner::OnComponentTerminate() in
|
||||
// to a dead thread. See Runner::OnComponentV1Terminate() in
|
||||
// runner.cc.
|
||||
platform_task_runner->PostTask([weak, runner_incoming_services,
|
||||
component_url, error, stack_trace]() {
|
||||
@ -458,13 +458,13 @@ Component::Component(
|
||||
};
|
||||
}
|
||||
|
||||
Component::~Component() = default;
|
||||
ComponentV1::~ComponentV1() = default;
|
||||
|
||||
const std::string& Component::GetDebugLabel() const {
|
||||
const std::string& ComponentV1::GetDebugLabel() const {
|
||||
return debug_label_;
|
||||
}
|
||||
|
||||
void Component::Kill() {
|
||||
void ComponentV1::Kill() {
|
||||
component_controller_.events().OnTerminated(
|
||||
last_return_code_.second, fuchsia::sys::TerminationReason::EXITED);
|
||||
|
||||
@ -473,11 +473,11 @@ void Component::Kill() {
|
||||
// collected.
|
||||
}
|
||||
|
||||
void Component::Detach() {
|
||||
void ComponentV1::Detach() {
|
||||
component_controller_.set_error_handler(nullptr);
|
||||
}
|
||||
|
||||
void Component::OnEngineTerminate(const Engine* shell_holder) {
|
||||
void ComponentV1::OnEngineTerminate(const Engine* shell_holder) {
|
||||
auto found = std::find_if(shell_holders_.begin(), shell_holders_.end(),
|
||||
[shell_holder](const auto& holder) {
|
||||
return holder.get() == shell_holder;
|
||||
@ -504,7 +504,7 @@ void Component::OnEngineTerminate(const Engine* shell_holder) {
|
||||
}
|
||||
}
|
||||
|
||||
void Component::CreateView(
|
||||
void ComponentV1::CreateView(
|
||||
zx::eventpair token,
|
||||
fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> /*incoming_services*/,
|
||||
fidl::InterfaceHandle<
|
||||
@ -514,7 +514,7 @@ void Component::CreateView(
|
||||
std::move(view_ref_pair.view_ref));
|
||||
}
|
||||
|
||||
void Component::CreateViewWithViewRef(
|
||||
void ComponentV1::CreateViewWithViewRef(
|
||||
zx::eventpair view_token,
|
||||
fuchsia::ui::views::ViewRefControl control_ref,
|
||||
fuchsia::ui::views::ViewRef view_ref) {
|
||||
@ -542,7 +542,7 @@ void Component::CreateViewWithViewRef(
|
||||
));
|
||||
}
|
||||
|
||||
void Component::CreateView2(fuchsia::ui::app::CreateView2Args view_args) {
|
||||
void ComponentV1::CreateView2(fuchsia::ui::app::CreateView2Args view_args) {
|
||||
if (!svc_) {
|
||||
FML_DLOG(ERROR)
|
||||
<< "Component incoming services was invalid when attempting to "
|
||||
@ -566,7 +566,7 @@ void Component::CreateView2(fuchsia::ui::app::CreateView2Args view_args) {
|
||||
}
|
||||
|
||||
#if !defined(DART_PRODUCT)
|
||||
void Component::WriteProfileToTrace() const {
|
||||
void ComponentV1::WriteProfileToTrace() const {
|
||||
for (const auto& engine : shell_holders_) {
|
||||
engine->WriteProfileToTrace();
|
||||
}
|
||||
@ -2,8 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_COMPONENT_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_FUCHSIA_COMPONENT_H_
|
||||
#ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_COMPONENT_V1_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_FUCHSIA_COMPONENT_V1_H_
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
@ -30,13 +30,13 @@
|
||||
|
||||
namespace flutter_runner {
|
||||
|
||||
class Component;
|
||||
class ComponentV1;
|
||||
|
||||
struct ActiveComponent {
|
||||
struct ActiveComponentV1 {
|
||||
std::unique_ptr<fml::Thread> platform_thread;
|
||||
std::unique_ptr<Component> component;
|
||||
std::unique_ptr<ComponentV1> component;
|
||||
|
||||
ActiveComponent& operator=(ActiveComponent&& other) noexcept {
|
||||
ActiveComponentV1& operator=(ActiveComponentV1&& other) noexcept {
|
||||
if (this != &other) {
|
||||
this->platform_thread.reset(other.platform_thread.release());
|
||||
this->component.reset(other.component.release());
|
||||
@ -44,21 +44,21 @@ struct ActiveComponent {
|
||||
return *this;
|
||||
}
|
||||
|
||||
~ActiveComponent() = default;
|
||||
~ActiveComponentV1() = default;
|
||||
};
|
||||
|
||||
// Represents an instance of a Flutter component that contains one of more
|
||||
// Represents an instance of a CF v1 Flutter component that contains one or more
|
||||
// Flutter engine instances.
|
||||
class Component final : public Engine::Delegate,
|
||||
public fuchsia::sys::ComponentController,
|
||||
public fuchsia::ui::app::ViewProvider {
|
||||
class ComponentV1 final : public Engine::Delegate,
|
||||
public fuchsia::sys::ComponentController,
|
||||
public fuchsia::ui::app::ViewProvider {
|
||||
public:
|
||||
using TerminationCallback = fit::function<void(const Component*)>;
|
||||
using TerminationCallback = fit::function<void(const ComponentV1*)>;
|
||||
|
||||
// Creates a dedicated thread to run the component and creates the
|
||||
// component on it. The component can be accessed only on this thread.
|
||||
// This is a synchronous operation.
|
||||
static ActiveComponent Create(
|
||||
static ActiveComponentV1 Create(
|
||||
TerminationCallback termination_callback,
|
||||
fuchsia::sys::Package package,
|
||||
fuchsia::sys::StartupInfo startup_info,
|
||||
@ -67,7 +67,7 @@ class Component final : public Engine::Delegate,
|
||||
|
||||
// Must be called on the same thread returned from the create call. The thread
|
||||
// may be collected after.
|
||||
~Component();
|
||||
~ComponentV1();
|
||||
|
||||
static void ParseProgramMetadata(
|
||||
const fidl::VectorPtr<fuchsia::sys::ProgramMetadata>& program_metadata,
|
||||
@ -101,9 +101,9 @@ class Component final : public Engine::Delegate,
|
||||
fml::RefPtr<flutter::DartSnapshot> isolate_snapshot_;
|
||||
std::set<std::unique_ptr<Engine>> shell_holders_;
|
||||
std::pair<bool, uint32_t> last_return_code_;
|
||||
fml::WeakPtrFactory<Component> weak_factory_;
|
||||
fml::WeakPtrFactory<ComponentV1> weak_factory_;
|
||||
|
||||
Component(
|
||||
ComponentV1(
|
||||
TerminationCallback termination_callback,
|
||||
fuchsia::sys::Package package,
|
||||
fuchsia::sys::StartupInfo startup_info,
|
||||
@ -134,9 +134,9 @@ class Component final : public Engine::Delegate,
|
||||
// |flutter::Engine::Delegate|
|
||||
void OnEngineTerminate(const Engine* holder) override;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(Component);
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(ComponentV1);
|
||||
};
|
||||
|
||||
} // namespace flutter_runner
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_FUCHSIA_COMPONENT_H_
|
||||
#endif // FLUTTER_SHELL_PLATFORM_FUCHSIA_COMPONENT_V1_H_
|
||||
@ -2,20 +2,20 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "flutter/shell/platform/fuchsia/flutter/component.h"
|
||||
#include "component_v1.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace flutter_runner {
|
||||
namespace {
|
||||
|
||||
TEST(Component, ParseProgramMetadata) {
|
||||
TEST(ComponentV1, ParseProgramMetadata) {
|
||||
std::string data_path;
|
||||
std::string assets_path;
|
||||
|
||||
// The ProgramMetadata field may be null. We should parse this as if no
|
||||
// fields were specified.
|
||||
Component::ParseProgramMetadata(nullptr, &data_path, &assets_path);
|
||||
ComponentV1::ParseProgramMetadata(nullptr, &data_path, &assets_path);
|
||||
|
||||
EXPECT_EQ(data_path, "");
|
||||
EXPECT_EQ(assets_path, "");
|
||||
@ -23,7 +23,7 @@ TEST(Component, ParseProgramMetadata) {
|
||||
// The ProgramMetadata field may be empty. Treat this the same as null.
|
||||
fidl::VectorPtr<fuchsia::sys::ProgramMetadata> program_metadata(size_t{0});
|
||||
|
||||
Component::ParseProgramMetadata(program_metadata, &data_path, &assets_path);
|
||||
ComponentV1::ParseProgramMetadata(program_metadata, &data_path, &assets_path);
|
||||
|
||||
EXPECT_EQ(data_path, "");
|
||||
EXPECT_EQ(assets_path, "");
|
||||
@ -31,7 +31,7 @@ TEST(Component, ParseProgramMetadata) {
|
||||
// The assets_path defaults to the "data" value if unspecified
|
||||
program_metadata = {{"data", "foobar"}};
|
||||
|
||||
Component::ParseProgramMetadata(program_metadata, &data_path, &assets_path);
|
||||
ComponentV1::ParseProgramMetadata(program_metadata, &data_path, &assets_path);
|
||||
|
||||
EXPECT_EQ(data_path, "pkg/foobar");
|
||||
EXPECT_EQ(assets_path, "pkg/foobar");
|
||||
@ -41,7 +41,7 @@ TEST(Component, ParseProgramMetadata) {
|
||||
|
||||
program_metadata = {{"not_data", "foo"}, {"data", "bar"}, {"assets", "baz"}};
|
||||
|
||||
Component::ParseProgramMetadata(program_metadata, &data_path, &assets_path);
|
||||
ComponentV1::ParseProgramMetadata(program_metadata, &data_path, &assets_path);
|
||||
|
||||
EXPECT_EQ(data_path, "pkg/bar");
|
||||
EXPECT_EQ(assets_path, "pkg/baz");
|
||||
@ -0,0 +1,618 @@
|
||||
// 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 "component_v2.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <fuchsia/mem/cpp/fidl.h>
|
||||
#include <lib/async-loop/cpp/loop.h>
|
||||
#include <lib/async/cpp/task.h>
|
||||
#include <lib/async/default.h>
|
||||
#include <lib/fdio/directory.h>
|
||||
#include <lib/fdio/io.h>
|
||||
#include <lib/fdio/namespace.h>
|
||||
#include <lib/ui/scenic/cpp/view_ref_pair.h>
|
||||
#include <lib/ui/scenic/cpp/view_token_pair.h>
|
||||
#include <lib/vfs/cpp/composed_service_dir.h>
|
||||
#include <lib/vfs/cpp/remote_dir.h>
|
||||
#include <lib/vfs/cpp/service.h>
|
||||
#include <sys/stat.h>
|
||||
#include <zircon/dlfcn.h>
|
||||
#include <zircon/status.h>
|
||||
#include <zircon/types.h>
|
||||
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
|
||||
#include "file_in_namespace_buffer.h"
|
||||
#include "flutter/fml/mapping.h"
|
||||
#include "flutter/fml/platform/fuchsia/task_observers.h"
|
||||
#include "flutter/fml/synchronization/waitable_event.h"
|
||||
#include "flutter/fml/unique_fd.h"
|
||||
#include "flutter/runtime/dart_vm_lifecycle.h"
|
||||
#include "flutter/shell/common/switches.h"
|
||||
#include "runtime/dart/utils/files.h"
|
||||
#include "runtime/dart/utils/handle_exception.h"
|
||||
#include "runtime/dart/utils/mapped_resource.h"
|
||||
#include "runtime/dart/utils/tempfs.h"
|
||||
#include "runtime/dart/utils/vmo.h"
|
||||
|
||||
namespace flutter_runner {
|
||||
namespace {
|
||||
|
||||
constexpr char kDataKey[] = "data";
|
||||
constexpr char kAssetsKey[] = "assets";
|
||||
constexpr char kTmpPath[] = "/tmp";
|
||||
constexpr char kServiceRootPath[] = "/svc";
|
||||
constexpr char kRunnerConfigPath[] = "/config/data/flutter_runner_config";
|
||||
|
||||
std::string DebugLabelForUrl(const std::string& url) {
|
||||
auto found = url.rfind("/");
|
||||
if (found == std::string::npos) {
|
||||
return url;
|
||||
} else {
|
||||
return {url, found + 1};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ComponentV2::ParseProgramMetadata(
|
||||
const fuchsia::data::Dictionary& program_metadata,
|
||||
std::string* data_path,
|
||||
std::string* assets_path) {
|
||||
for (const auto& entry : program_metadata.entries()) {
|
||||
if (entry.key.compare(kDataKey) == 0 && entry.value != nullptr) {
|
||||
*data_path = "pkg/" + entry.value->str();
|
||||
} else if (entry.key.compare(kAssetsKey) == 0 && entry.value != nullptr) {
|
||||
*assets_path = "pkg/" + entry.value->str();
|
||||
}
|
||||
}
|
||||
|
||||
// assets_path defaults to the same as data_path if omitted.
|
||||
if (assets_path->empty()) {
|
||||
*assets_path = *data_path;
|
||||
}
|
||||
}
|
||||
|
||||
ActiveComponentV2 ComponentV2::Create(
|
||||
TerminationCallback termination_callback,
|
||||
fuchsia::component::runner::ComponentStartInfo start_info,
|
||||
std::shared_ptr<sys::ServiceDirectory> runner_incoming_services,
|
||||
fidl::InterfaceRequest<fuchsia::component::runner::ComponentController>
|
||||
controller) {
|
||||
auto thread = std::make_unique<fml::Thread>();
|
||||
std::unique_ptr<ComponentV2> component;
|
||||
|
||||
fml::AutoResetWaitableEvent latch;
|
||||
thread->GetTaskRunner()->PostTask([&]() mutable {
|
||||
component.reset(
|
||||
new ComponentV2(std::move(termination_callback), std::move(start_info),
|
||||
runner_incoming_services, std::move(controller)));
|
||||
latch.Signal();
|
||||
});
|
||||
|
||||
latch.Wait();
|
||||
return {.platform_thread = std::move(thread),
|
||||
.component = std::move(component)};
|
||||
}
|
||||
|
||||
ComponentV2::ComponentV2(
|
||||
TerminationCallback termination_callback,
|
||||
fuchsia::component::runner::ComponentStartInfo start_info,
|
||||
std::shared_ptr<sys::ServiceDirectory> runner_incoming_services,
|
||||
fidl::InterfaceRequest<fuchsia::component::runner::ComponentController>
|
||||
component_controller_request)
|
||||
: termination_callback_(std::move(termination_callback)),
|
||||
debug_label_(DebugLabelForUrl(start_info.resolved_url())),
|
||||
component_controller_(this),
|
||||
outgoing_dir_(new vfs::PseudoDir()),
|
||||
runtime_dir_(new vfs::PseudoDir()),
|
||||
runner_incoming_services_(runner_incoming_services),
|
||||
weak_factory_(this) {
|
||||
component_controller_.set_error_handler([this](zx_status_t status) {
|
||||
FML_LOG(ERROR) << "ComponentController binding error for component("
|
||||
<< debug_label_ << "): " << zx_status_get_string(status);
|
||||
KillWithEpitaph(
|
||||
zx_status_t(fuchsia::component::Error::INSTANCE_CANNOT_START));
|
||||
});
|
||||
|
||||
FML_DCHECK(fdio_ns_.is_valid());
|
||||
|
||||
// TODO(fxb/50694): Dart launch arguments.
|
||||
FML_LOG(WARNING) << "program() arguments are currently ignored (fxb/50694).";
|
||||
|
||||
// Determine where data and assets are stored within /pkg.
|
||||
std::string data_path;
|
||||
std::string assets_path;
|
||||
ParseProgramMetadata(start_info.program(), &data_path, &assets_path);
|
||||
|
||||
if (data_path.empty()) {
|
||||
FML_DLOG(ERROR) << "Could not find a /pkg/data directory for "
|
||||
<< start_info.resolved_url();
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup /tmp to be mapped to the process-local memfs.
|
||||
dart_utils::RunnerTemp::SetupComponent(fdio_ns_.get());
|
||||
|
||||
// ComponentStartInfo::ns (optional)
|
||||
if (start_info.has_ns()) {
|
||||
for (auto& entry : *start_info.mutable_ns()) {
|
||||
// /tmp/ is mapped separately to the process-level memfs, so we ignore it
|
||||
// here.
|
||||
const auto& path = entry.path();
|
||||
if (path == kTmpPath) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We should never receive namespace entries without a directory, but we
|
||||
// check it anyways to avoid crashing if we do.
|
||||
if (!entry.has_directory()) {
|
||||
FML_DLOG(ERROR) << "Namespace entry at path (" << path
|
||||
<< ") has no directory.";
|
||||
continue;
|
||||
}
|
||||
|
||||
zx::channel dir;
|
||||
if (path == kServiceRootPath) {
|
||||
svc_ = std::make_unique<sys::ServiceDirectory>(
|
||||
std::move(*entry.mutable_directory()));
|
||||
dir = svc_->CloneChannel().TakeChannel();
|
||||
} else {
|
||||
dir = entry.mutable_directory()->TakeChannel();
|
||||
}
|
||||
|
||||
zx_handle_t dir_handle = dir.release();
|
||||
if (fdio_ns_bind(fdio_ns_.get(), path.data(), dir_handle) != ZX_OK) {
|
||||
FML_DLOG(ERROR) << "Could not bind path to namespace: " << path;
|
||||
zx_handle_close(dir_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open the data and assets directories inside our namespace.
|
||||
{
|
||||
fml::UniqueFD ns_fd(fdio_ns_opendir(fdio_ns_.get()));
|
||||
FML_DCHECK(ns_fd.is_valid());
|
||||
|
||||
constexpr mode_t mode = O_RDONLY | O_DIRECTORY;
|
||||
|
||||
component_assets_directory_.reset(
|
||||
openat(ns_fd.get(), assets_path.c_str(), mode));
|
||||
FML_DCHECK(component_assets_directory_.is_valid());
|
||||
|
||||
component_data_directory_.reset(
|
||||
openat(ns_fd.get(), data_path.c_str(), mode));
|
||||
FML_DCHECK(component_data_directory_.is_valid());
|
||||
}
|
||||
|
||||
// ComponentStartInfo::runtime_dir (optional).
|
||||
if (start_info.has_runtime_dir()) {
|
||||
runtime_dir_->Serve(fuchsia::io::OPEN_RIGHT_READABLE |
|
||||
fuchsia::io::OPEN_RIGHT_WRITABLE |
|
||||
fuchsia::io::OPEN_FLAG_DIRECTORY,
|
||||
start_info.mutable_runtime_dir()->TakeChannel());
|
||||
}
|
||||
|
||||
// ComponentStartInfo::outgoing_dir (optional).
|
||||
if (start_info.has_outgoing_dir()) {
|
||||
outgoing_dir_->Serve(fuchsia::io::OPEN_RIGHT_READABLE |
|
||||
fuchsia::io::OPEN_RIGHT_WRITABLE |
|
||||
fuchsia::io::OPEN_FLAG_DIRECTORY,
|
||||
start_info.mutable_outgoing_dir()->TakeChannel());
|
||||
}
|
||||
|
||||
directory_request_ = directory_ptr_.NewRequest();
|
||||
|
||||
fidl::InterfaceHandle<fuchsia::io::Directory> flutter_public_dir;
|
||||
// TODO(anmittal): when fixing enumeration using new c++ vfs, make sure that
|
||||
// flutter_public_dir is only accessed once we receive OnOpen Event.
|
||||
// That will prevent FL-175 for public directory
|
||||
auto request = flutter_public_dir.NewRequest().TakeChannel();
|
||||
fdio_service_connect_at(directory_ptr_.channel().get(), "svc",
|
||||
request.release());
|
||||
|
||||
auto composed_service_dir = std::make_unique<vfs::ComposedServiceDir>();
|
||||
composed_service_dir->set_fallback(std::move(flutter_public_dir));
|
||||
|
||||
// Clone and check if client is servicing the directory.
|
||||
directory_ptr_->Clone(fuchsia::io::OPEN_FLAG_DESCRIBE |
|
||||
fuchsia::io::OPEN_RIGHT_READABLE |
|
||||
fuchsia::io::OPEN_RIGHT_WRITABLE,
|
||||
cloned_directory_ptr_.NewRequest());
|
||||
|
||||
cloned_directory_ptr_.events().OnOpen =
|
||||
[this](zx_status_t status, std::unique_ptr<fuchsia::io::NodeInfo> info) {
|
||||
cloned_directory_ptr_.Unbind();
|
||||
if (status != ZX_OK) {
|
||||
FML_LOG(ERROR)
|
||||
<< "could not bind out directory for flutter component("
|
||||
<< debug_label_ << "): " << zx_status_get_string(status);
|
||||
return;
|
||||
}
|
||||
const char* other_dirs[] = {"debug", "ctrl", "diagnostics"};
|
||||
// add other directories as RemoteDirs.
|
||||
for (auto& dir_str : other_dirs) {
|
||||
fidl::InterfaceHandle<fuchsia::io::Directory> dir;
|
||||
auto request = dir.NewRequest().TakeChannel();
|
||||
auto status = fdio_service_connect_at(directory_ptr_.channel().get(),
|
||||
dir_str, request.release());
|
||||
if (status == ZX_OK) {
|
||||
outgoing_dir_->AddEntry(
|
||||
dir_str, std::make_unique<vfs::RemoteDir>(dir.TakeChannel()));
|
||||
} else {
|
||||
FML_LOG(ERROR) << "could not add out directory entry(" << dir_str
|
||||
<< ") for flutter component(" << debug_label_
|
||||
<< "): " << zx_status_get_string(status);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cloned_directory_ptr_.set_error_handler(
|
||||
[this](zx_status_t status) { cloned_directory_ptr_.Unbind(); });
|
||||
|
||||
// TODO(fxb/50694): Close handles from ComponentStartInfo::numbered_handles
|
||||
// since we're not using them. See documentation from ComponentController:
|
||||
// https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/fidl/fuchsia.component.runner/component_runner.fidl;l=97;drc=e3b39f2b57e720770773b857feca4f770ee0619e
|
||||
|
||||
// TODO(fxb/50694): There's an OnPublishDiagnostics event we may want to
|
||||
// fire for diagnostics. See documentation from ComponentController:
|
||||
// https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/fidl/fuchsia.component.runner/component_runner.fidl;l=181;drc=e3b39f2b57e720770773b857feca4f770ee0619e
|
||||
|
||||
// All launch arguments have been read. Perform service binding and
|
||||
// final settings configuration. The next call will be to create a view
|
||||
// for this component.
|
||||
composed_service_dir->AddService(
|
||||
fuchsia::ui::app::ViewProvider::Name_,
|
||||
std::make_unique<vfs::Service>(
|
||||
[this](zx::channel channel, async_dispatcher_t* dispatcher) {
|
||||
shells_bindings_.AddBinding(
|
||||
this, fidl::InterfaceRequest<fuchsia::ui::app::ViewProvider>(
|
||||
std::move(channel)));
|
||||
}));
|
||||
outgoing_dir_->AddEntry("svc", std::move(composed_service_dir));
|
||||
|
||||
// Setup the component controller binding.
|
||||
if (component_controller_request) {
|
||||
component_controller_.Bind(std::move(component_controller_request));
|
||||
}
|
||||
|
||||
// Load and use runner-specific configuration, if it exists.
|
||||
std::string json_string;
|
||||
if (dart_utils::ReadFileToString(kRunnerConfigPath, &json_string)) {
|
||||
product_config_ = FlutterRunnerProductConfiguration(json_string);
|
||||
FML_LOG(INFO) << "Successfully loaded runner configuration: "
|
||||
<< json_string;
|
||||
} else {
|
||||
FML_LOG(WARNING) << "Failed to load runner configuration from "
|
||||
<< kRunnerConfigPath << "; using default config values.";
|
||||
}
|
||||
|
||||
// Load VM and component bytecode.
|
||||
// For AOT, compare with flutter_aot_app in flutter_app.gni.
|
||||
// For JIT, compare flutter_jit_runner in BUILD.gn.
|
||||
if (flutter::DartVM::IsRunningPrecompiledCode()) {
|
||||
std::shared_ptr<dart_utils::ElfSnapshot> snapshot =
|
||||
std::make_shared<dart_utils::ElfSnapshot>();
|
||||
if (snapshot->Load(component_data_directory_.get(),
|
||||
"app_aot_snapshot.so")) {
|
||||
const uint8_t* isolate_data = snapshot->IsolateData();
|
||||
const uint8_t* isolate_instructions = snapshot->IsolateInstrs();
|
||||
const uint8_t* vm_data = snapshot->VmData();
|
||||
const uint8_t* vm_instructions = snapshot->VmInstrs();
|
||||
if (isolate_data == nullptr || isolate_instructions == nullptr ||
|
||||
vm_data == nullptr || vm_instructions == nullptr) {
|
||||
FML_LOG(FATAL) << "ELF snapshot missing AOT symbols.";
|
||||
return;
|
||||
}
|
||||
auto hold_snapshot = [snapshot](const uint8_t* _, size_t __) {};
|
||||
settings_.vm_snapshot_data = [hold_snapshot, vm_data]() {
|
||||
return std::make_unique<fml::NonOwnedMapping>(vm_data, 0,
|
||||
hold_snapshot);
|
||||
};
|
||||
settings_.vm_snapshot_instr = [hold_snapshot, vm_instructions]() {
|
||||
return std::make_unique<fml::NonOwnedMapping>(vm_instructions, 0,
|
||||
hold_snapshot);
|
||||
};
|
||||
settings_.isolate_snapshot_data = [hold_snapshot, isolate_data]() {
|
||||
return std::make_unique<fml::NonOwnedMapping>(isolate_data, 0,
|
||||
hold_snapshot);
|
||||
};
|
||||
settings_.isolate_snapshot_instr = [hold_snapshot,
|
||||
isolate_instructions]() {
|
||||
return std::make_unique<fml::NonOwnedMapping>(isolate_instructions, 0,
|
||||
hold_snapshot);
|
||||
};
|
||||
isolate_snapshot_ = fml::MakeRefCounted<flutter::DartSnapshot>(
|
||||
std::make_shared<fml::NonOwnedMapping>(isolate_data, 0,
|
||||
hold_snapshot),
|
||||
std::make_shared<fml::NonOwnedMapping>(isolate_instructions, 0,
|
||||
hold_snapshot));
|
||||
} else {
|
||||
const int namespace_fd = component_data_directory_.get();
|
||||
settings_.vm_snapshot_data = [namespace_fd]() {
|
||||
return LoadFile(namespace_fd, "vm_snapshot_data.bin",
|
||||
false /* executable */);
|
||||
};
|
||||
settings_.vm_snapshot_instr = [namespace_fd]() {
|
||||
return LoadFile(namespace_fd, "vm_snapshot_instructions.bin",
|
||||
true /* executable */);
|
||||
};
|
||||
settings_.isolate_snapshot_data = [namespace_fd]() {
|
||||
return LoadFile(namespace_fd, "isolate_snapshot_data.bin",
|
||||
false /* executable */);
|
||||
};
|
||||
settings_.isolate_snapshot_instr = [namespace_fd]() {
|
||||
return LoadFile(namespace_fd, "isolate_snapshot_instructions.bin",
|
||||
true /* executable */);
|
||||
};
|
||||
}
|
||||
} else {
|
||||
settings_.vm_snapshot_data = []() {
|
||||
return MakeFileMapping("/pkg/data/vm_snapshot_data.bin",
|
||||
false /* executable */);
|
||||
};
|
||||
settings_.vm_snapshot_instr = []() {
|
||||
return MakeFileMapping("/pkg/data/vm_snapshot_instructions.bin",
|
||||
true /* executable */);
|
||||
};
|
||||
|
||||
settings_.isolate_snapshot_data = []() {
|
||||
return MakeFileMapping("/pkg/data/isolate_core_snapshot_data.bin",
|
||||
false /* executable */);
|
||||
};
|
||||
settings_.isolate_snapshot_instr = [] {
|
||||
return MakeFileMapping("/pkg/data/isolate_core_snapshot_instructions.bin",
|
||||
true /* executable */);
|
||||
};
|
||||
}
|
||||
|
||||
#if defined(DART_PRODUCT)
|
||||
settings_.enable_observatory = false;
|
||||
#else
|
||||
settings_.enable_observatory = true;
|
||||
|
||||
// TODO(cbracken): pass this in as a param to allow 0.0.0.0, ::1, etc.
|
||||
settings_.observatory_host = "127.0.0.1";
|
||||
#endif
|
||||
|
||||
// Controls whether category "skia" trace events are enabled.
|
||||
settings_.trace_skia = true;
|
||||
|
||||
settings_.verbose_logging = true;
|
||||
|
||||
settings_.advisory_script_uri = debug_label_;
|
||||
|
||||
settings_.advisory_script_entrypoint = debug_label_;
|
||||
|
||||
settings_.icu_data_path = "";
|
||||
|
||||
settings_.assets_dir = component_assets_directory_.get();
|
||||
|
||||
// Compare flutter_jit_app in flutter_app.gni.
|
||||
settings_.application_kernel_list_asset = "app.dilplist";
|
||||
|
||||
settings_.log_tag = debug_label_ + std::string{"(flutter)"};
|
||||
|
||||
// No asserts in debug or release product.
|
||||
// No asserts in release with flutter_profile=true (non-product)
|
||||
// Yes asserts in non-product debug.
|
||||
#if !defined(DART_PRODUCT) && (!defined(FLUTTER_PROFILE) || !defined(NDEBUG))
|
||||
// Debug mode
|
||||
settings_.disable_dart_asserts = false;
|
||||
#else
|
||||
// Release mode
|
||||
settings_.disable_dart_asserts = true;
|
||||
#endif
|
||||
|
||||
// Do not leak the VM; allow it to shut down normally when the last shell
|
||||
// terminates.
|
||||
settings_.leak_vm = false;
|
||||
|
||||
settings_.task_observer_add =
|
||||
std::bind(&fml::CurrentMessageLoopAddAfterTaskObserver,
|
||||
std::placeholders::_1, std::placeholders::_2);
|
||||
|
||||
settings_.task_observer_remove = std::bind(
|
||||
&fml::CurrentMessageLoopRemoveAfterTaskObserver, std::placeholders::_1);
|
||||
|
||||
settings_.log_message_callback = [](const std::string& tag,
|
||||
const std::string& message) {
|
||||
if (tag.size() > 0) {
|
||||
std::cout << tag << ": ";
|
||||
}
|
||||
std::cout << message << std::endl;
|
||||
};
|
||||
|
||||
settings_.dart_flags = {"--lazy_async_stacks"};
|
||||
|
||||
// Don't collect CPU samples from Dart VM C++ code.
|
||||
settings_.dart_flags.push_back("--no_profile_vm");
|
||||
|
||||
// Scale back CPU profiler sampling period on ARM64 to avoid overloading
|
||||
// the tracing engine.
|
||||
#if defined(__aarch64__)
|
||||
settings_.dart_flags.push_back("--profile_period=10000");
|
||||
#endif // defined(__aarch64__)
|
||||
|
||||
auto platform_task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
|
||||
const std::string component_url = start_info.resolved_url();
|
||||
settings_.unhandled_exception_callback = [weak = weak_factory_.GetWeakPtr(),
|
||||
platform_task_runner,
|
||||
runner_incoming_services,
|
||||
component_url](
|
||||
const std::string& error,
|
||||
const std::string& stack_trace) {
|
||||
if (weak) {
|
||||
// TODO(cbracken): unsafe. The above check and the PostTask below are
|
||||
// happening on the UI thread. If the Component dtor and thread
|
||||
// termination happen (on the platform thread) between the previous
|
||||
// line and the next line, a crash will occur since we'll be posting
|
||||
// to a dead thread. See Runner::OnComponentV2Terminate() in
|
||||
// runner.cc.
|
||||
platform_task_runner->PostTask([weak, runner_incoming_services,
|
||||
component_url, error, stack_trace]() {
|
||||
if (weak) {
|
||||
dart_utils::HandleException(runner_incoming_services, component_url,
|
||||
error, stack_trace);
|
||||
} else {
|
||||
FML_LOG(WARNING)
|
||||
<< "Exception was thrown which was not caught in Flutter app: "
|
||||
<< error;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
FML_LOG(WARNING)
|
||||
<< "Exception was thrown which was not caught in Flutter app: "
|
||||
<< error;
|
||||
}
|
||||
// Ideally we would return whether HandleException returned ZX_OK, but
|
||||
// short of knowing if the exception was correctly handled, we return
|
||||
// false to have the error and stack trace printed in the logs.
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
ComponentV2::~ComponentV2() = default;
|
||||
|
||||
const std::string& ComponentV2::GetDebugLabel() const {
|
||||
return debug_label_;
|
||||
}
|
||||
|
||||
void ComponentV2::Kill() {
|
||||
FML_VLOG(-1) << "ComponentController: received Kill";
|
||||
|
||||
// From the documentation for ComponentController, ZX_OK should be sent when
|
||||
// the ComponentController receives a termination request.
|
||||
//
|
||||
// TODO(fxb/50694): How should we communicate the return code of the process
|
||||
// with the epitaph? Should we avoid sending ZX_OK if the return code is not
|
||||
// 0?
|
||||
//
|
||||
// CF v1 logic for reference (the OnTerminated event no longer exists):
|
||||
// component_controller_.events().OnTerminated(
|
||||
// last_return_code_.second, fuchsia::sys::TerminationReason::EXITED);
|
||||
|
||||
KillWithEpitaph(ZX_OK);
|
||||
|
||||
// WARNING: Don't do anything past this point as this instance may have been
|
||||
// collected.
|
||||
}
|
||||
|
||||
void ComponentV2::KillWithEpitaph(zx_status_t epitaph_status) {
|
||||
component_controller_.set_error_handler(nullptr);
|
||||
component_controller_.Close(epitaph_status);
|
||||
|
||||
termination_callback_(this);
|
||||
// WARNING: Don't do anything past this point as this instance may have been
|
||||
// collected.
|
||||
}
|
||||
|
||||
void ComponentV2::Stop() {
|
||||
FML_VLOG(-1) << "ComponentController v2: received Stop";
|
||||
|
||||
// TODO(fxb/50694): Any other cleanup logic we should do that's appropriate
|
||||
// for Stop but not for Kill?
|
||||
KillWithEpitaph(ZX_OK);
|
||||
}
|
||||
|
||||
void ComponentV2::OnEngineTerminate(const Engine* shell_holder) {
|
||||
auto found = std::find_if(shell_holders_.begin(), shell_holders_.end(),
|
||||
[shell_holder](const auto& holder) {
|
||||
return holder.get() == shell_holder;
|
||||
});
|
||||
|
||||
if (found == shell_holders_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We may launch multiple shell in this component. However, we will
|
||||
// terminate when the last shell goes away. The error code returned to the
|
||||
// component controller will be the last isolate that had an error.
|
||||
auto return_code = shell_holder->GetEngineReturnCode();
|
||||
if (return_code.has_value()) {
|
||||
last_return_code_ = {true, return_code.value()};
|
||||
}
|
||||
|
||||
shell_holders_.erase(found);
|
||||
|
||||
if (shell_holders_.size() == 0) {
|
||||
Kill();
|
||||
// WARNING: Don't do anything past this point because the delegate may have
|
||||
// collected this instance via the termination callback.
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentV2::CreateView(
|
||||
zx::eventpair token,
|
||||
fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> /*incoming_services*/,
|
||||
fidl::InterfaceHandle<
|
||||
fuchsia::sys::ServiceProvider> /*outgoing_services*/) {
|
||||
auto view_ref_pair = scenic::ViewRefPair::New();
|
||||
CreateViewWithViewRef(std::move(token), std::move(view_ref_pair.control_ref),
|
||||
std::move(view_ref_pair.view_ref));
|
||||
}
|
||||
|
||||
void ComponentV2::CreateViewWithViewRef(
|
||||
zx::eventpair view_token,
|
||||
fuchsia::ui::views::ViewRefControl control_ref,
|
||||
fuchsia::ui::views::ViewRef view_ref) {
|
||||
if (!svc_) {
|
||||
FML_DLOG(ERROR)
|
||||
<< "Component incoming services was invalid when attempting to "
|
||||
"create a shell for a view provider request.";
|
||||
return;
|
||||
}
|
||||
|
||||
shell_holders_.emplace(std::make_unique<Engine>(
|
||||
*this, // delegate
|
||||
debug_label_, // thread label
|
||||
svc_, // Component incoming services
|
||||
runner_incoming_services_, // Runner incoming services
|
||||
settings_, // settings
|
||||
scenic::ToViewToken(std::move(view_token)), // view token
|
||||
scenic::ViewRefPair{
|
||||
.control_ref = std::move(control_ref),
|
||||
.view_ref = std::move(view_ref),
|
||||
},
|
||||
std::move(fdio_ns_), // FDIO namespace
|
||||
std::move(directory_request_), // outgoing request
|
||||
product_config_ // product configuration
|
||||
));
|
||||
}
|
||||
|
||||
void ComponentV2::CreateView2(fuchsia::ui::app::CreateView2Args view_args) {
|
||||
if (!svc_) {
|
||||
FML_DLOG(ERROR)
|
||||
<< "Component incoming services was invalid when attempting to "
|
||||
"create a shell for a view provider request.";
|
||||
return;
|
||||
}
|
||||
|
||||
shell_holders_.emplace(std::make_unique<Engine>(
|
||||
*this, // delegate
|
||||
debug_label_, // thread label
|
||||
svc_, // Component incoming services
|
||||
runner_incoming_services_, // Runner incoming services
|
||||
settings_, // settings
|
||||
std::move(
|
||||
*view_args.mutable_view_creation_token()), // view creation token
|
||||
scenic::ViewRefPair::New(), // view ref pair
|
||||
std::move(fdio_ns_), // FDIO namespace
|
||||
std::move(directory_request_), // outgoing request
|
||||
product_config_ // product configuration
|
||||
));
|
||||
}
|
||||
|
||||
#if !defined(DART_PRODUCT)
|
||||
void ComponentV2::WriteProfileToTrace() const {
|
||||
for (const auto& engine : shell_holders_) {
|
||||
engine->WriteProfileToTrace();
|
||||
}
|
||||
}
|
||||
#endif // !defined(DART_PRODUCT)
|
||||
|
||||
} // namespace flutter_runner
|
||||
158
engine/src/flutter/shell/platform/fuchsia/flutter/component_v2.h
Normal file
158
engine/src/flutter/shell/platform/fuchsia/flutter/component_v2.h
Normal file
@ -0,0 +1,158 @@
|
||||
// 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.
|
||||
|
||||
#ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_COMPONENT_V2_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_FUCHSIA_COMPONENT_V2_H_
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
#include <fuchsia/component/runner/cpp/fidl.h>
|
||||
#include <fuchsia/io/cpp/fidl.h>
|
||||
#include <fuchsia/sys/cpp/fidl.h>
|
||||
#include <fuchsia/ui/app/cpp/fidl.h>
|
||||
#include <lib/async-loop/cpp/loop.h>
|
||||
#include <lib/async/default.h>
|
||||
#include <lib/fidl/cpp/binding_set.h>
|
||||
#include <lib/fidl/cpp/interface_request.h>
|
||||
#include <lib/fit/function.h>
|
||||
#include <lib/sys/cpp/service_directory.h>
|
||||
#include <lib/vfs/cpp/pseudo_dir.h>
|
||||
#include <lib/zx/eventpair.h>
|
||||
|
||||
#include "flutter/common/settings.h"
|
||||
#include "flutter/fml/macros.h"
|
||||
|
||||
#include "engine.h"
|
||||
#include "flutter_runner_product_configuration.h"
|
||||
#include "unique_fdio_ns.h"
|
||||
|
||||
namespace flutter_runner {
|
||||
|
||||
class ComponentV2;
|
||||
|
||||
struct ActiveComponentV2 {
|
||||
std::unique_ptr<fml::Thread> platform_thread;
|
||||
std::unique_ptr<ComponentV2> component;
|
||||
|
||||
ActiveComponentV2& operator=(ActiveComponentV2&& other) noexcept {
|
||||
if (this != &other) {
|
||||
this->platform_thread.reset(other.platform_thread.release());
|
||||
this->component.reset(other.component.release());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~ActiveComponentV2() = default;
|
||||
};
|
||||
|
||||
// Represents an instance of a CF v2 Flutter component that contains one or more
|
||||
// Flutter engine instances.
|
||||
//
|
||||
// TODO(fxb/50694): Add unit tests once we've verified that the current behavior
|
||||
// is working correctly.
|
||||
class ComponentV2 final
|
||||
: public Engine::Delegate,
|
||||
public fuchsia::component::runner::ComponentController,
|
||||
public fuchsia::ui::app::ViewProvider {
|
||||
public:
|
||||
using TerminationCallback = fit::function<void(const ComponentV2*)>;
|
||||
|
||||
// Creates a dedicated thread to run the component and creates the
|
||||
// component on it. The component can be accessed only on this thread.
|
||||
// This is a synchronous operation.
|
||||
static ActiveComponentV2 Create(
|
||||
TerminationCallback termination_callback,
|
||||
fuchsia::component::runner::ComponentStartInfo start_info,
|
||||
std::shared_ptr<sys::ServiceDirectory> runner_incoming_services,
|
||||
fidl::InterfaceRequest<fuchsia::component::runner::ComponentController>
|
||||
controller);
|
||||
|
||||
// Must be called on the same thread returned from the create call. The thread
|
||||
// may be collected after.
|
||||
~ComponentV2();
|
||||
|
||||
static void ParseProgramMetadata(
|
||||
const fuchsia::data::Dictionary& program_metadata,
|
||||
std::string* data_path,
|
||||
std::string* assets_path);
|
||||
|
||||
const std::string& GetDebugLabel() const;
|
||||
|
||||
#if !defined(DART_PRODUCT)
|
||||
void WriteProfileToTrace() const;
|
||||
#endif // !defined(DART_PRODUCT)
|
||||
|
||||
private:
|
||||
flutter::Settings settings_;
|
||||
FlutterRunnerProductConfiguration product_config_;
|
||||
TerminationCallback termination_callback_;
|
||||
const std::string debug_label_;
|
||||
UniqueFDIONS fdio_ns_ = UniqueFDIONSCreate();
|
||||
fml::UniqueFD component_data_directory_;
|
||||
fml::UniqueFD component_assets_directory_;
|
||||
|
||||
fidl::Binding<fuchsia::component::runner::ComponentController>
|
||||
component_controller_;
|
||||
fuchsia::io::DirectoryPtr directory_ptr_;
|
||||
fuchsia::io::NodePtr cloned_directory_ptr_;
|
||||
fidl::InterfaceRequest<fuchsia::io::Directory> directory_request_;
|
||||
std::unique_ptr<vfs::PseudoDir> outgoing_dir_;
|
||||
std::unique_ptr<vfs::PseudoDir> runtime_dir_;
|
||||
std::shared_ptr<sys::ServiceDirectory> svc_;
|
||||
std::shared_ptr<sys::ServiceDirectory> runner_incoming_services_;
|
||||
fidl::BindingSet<fuchsia::ui::app::ViewProvider> shells_bindings_;
|
||||
|
||||
fml::RefPtr<flutter::DartSnapshot> isolate_snapshot_;
|
||||
std::set<std::unique_ptr<Engine>> shell_holders_;
|
||||
std::pair<bool, uint32_t> last_return_code_;
|
||||
fml::WeakPtrFactory<ComponentV2> weak_factory_;
|
||||
|
||||
ComponentV2(
|
||||
TerminationCallback termination_callback,
|
||||
fuchsia::component::runner::ComponentStartInfo start_info,
|
||||
std::shared_ptr<sys::ServiceDirectory> runner_incoming_services,
|
||||
fidl::InterfaceRequest<fuchsia::component::runner::ComponentController>
|
||||
controller);
|
||||
|
||||
// |fuchsia::component::runner::ComponentController|
|
||||
void Kill() override;
|
||||
|
||||
/// Helper to actually |Kill| the component, closing the connection via an
|
||||
/// epitaph with the given |epitaph_status|. Call this instead of
|
||||
/// Kill() in the implementation of this class, as |Kill| is only intended for
|
||||
/// clients of the ComponentController protocol to call.
|
||||
///
|
||||
/// To determine what |epitaph_status| is appropriate for your situation,
|
||||
/// see the documentation for |fuchsia.component.runner.ComponentController|.
|
||||
void KillWithEpitaph(zx_status_t epitaph_status);
|
||||
|
||||
// |fuchsia::component::runner::ComponentController|
|
||||
void Stop() override;
|
||||
|
||||
// |fuchsia::ui::app::ViewProvider|
|
||||
void CreateView(
|
||||
zx::eventpair token,
|
||||
fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> incoming_services,
|
||||
fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> outgoing_services)
|
||||
override;
|
||||
|
||||
// |fuchsia::ui::app::ViewProvider|
|
||||
void CreateViewWithViewRef(zx::eventpair view_token,
|
||||
fuchsia::ui::views::ViewRefControl control_ref,
|
||||
fuchsia::ui::views::ViewRef view_ref) override;
|
||||
|
||||
// |fuchsia::ui::app::ViewProvider|
|
||||
void CreateView2(fuchsia::ui::app::CreateView2Args view_args) override;
|
||||
|
||||
// |flutter::Engine::Delegate|
|
||||
void OnEngineTerminate(const Engine* holder) override;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(ComponentV2);
|
||||
};
|
||||
|
||||
} // namespace flutter_runner
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_FUCHSIA_COMPONENT_V2_H_
|
||||
@ -188,7 +188,10 @@ Runner::Runner(fml::RefPtr<fml::TaskRunner> task_runner,
|
||||
SetThreadName("io.flutter.runner.main");
|
||||
|
||||
context_->outgoing()->AddPublicService<fuchsia::sys::Runner>(
|
||||
std::bind(&Runner::RegisterComponent, this, std::placeholders::_1));
|
||||
std::bind(&Runner::RegisterComponentV1, this, std::placeholders::_1));
|
||||
context_->outgoing()
|
||||
->AddPublicService<fuchsia::component::runner::ComponentRunner>(
|
||||
std::bind(&Runner::RegisterComponentV2, this, std::placeholders::_1));
|
||||
|
||||
#if !defined(DART_PRODUCT)
|
||||
if (Dart_IsPrecompiledRuntime()) {
|
||||
@ -203,15 +206,19 @@ Runner::Runner(fml::RefPtr<fml::TaskRunner> task_runner,
|
||||
|
||||
Runner::~Runner() {
|
||||
context_->outgoing()->RemovePublicService<fuchsia::sys::Runner>();
|
||||
context_->outgoing()
|
||||
->RemovePublicService<fuchsia::component::runner::ComponentRunner>();
|
||||
|
||||
#if !defined(DART_PRODUCT)
|
||||
trace_observer_->Stop();
|
||||
#endif // !defined(DART_PRODUCT)
|
||||
}
|
||||
|
||||
void Runner::RegisterComponent(
|
||||
// CF v1 lifecycle methods.
|
||||
|
||||
void Runner::RegisterComponentV1(
|
||||
fidl::InterfaceRequest<fuchsia::sys::Runner> request) {
|
||||
active_components_bindings_.AddBinding(this, std::move(request));
|
||||
active_components_v1_bindings_.AddBinding(this, std::move(request));
|
||||
}
|
||||
|
||||
void Runner::StartComponent(
|
||||
@ -233,15 +240,15 @@ void Runner::StartComponent(
|
||||
// we capture the runner in the termination callback. There is no risk of
|
||||
// there being multiple component runner instances in the process at the same
|
||||
// time. So it is safe to use the raw pointer.
|
||||
Component::TerminationCallback termination_callback =
|
||||
[component_runner = this](const Component* component) {
|
||||
ComponentV1::TerminationCallback termination_callback =
|
||||
[component_runner = this](const ComponentV1* component) {
|
||||
component_runner->task_runner_->PostTask(
|
||||
[component_runner, component]() {
|
||||
component_runner->OnComponentTerminate(component);
|
||||
component_runner->OnComponentV1Terminate(component);
|
||||
});
|
||||
};
|
||||
|
||||
ActiveComponent active_component = Component::Create(
|
||||
ActiveComponentV1 active_component = ComponentV1::Create(
|
||||
std::move(termination_callback), // termination callback
|
||||
std::move(package), // component package
|
||||
std::move(startup_info), // startup info
|
||||
@ -250,29 +257,103 @@ void Runner::StartComponent(
|
||||
);
|
||||
|
||||
auto key = active_component.component.get();
|
||||
active_components_[key] = std::move(active_component);
|
||||
active_components_v1_[key] = std::move(active_component);
|
||||
}
|
||||
|
||||
void Runner::OnComponentTerminate(const Component* component) {
|
||||
auto app = active_components_.find(component);
|
||||
if (app == active_components_.end()) {
|
||||
void Runner::OnComponentV1Terminate(const ComponentV1* component) {
|
||||
auto app = active_components_v1_.find(component);
|
||||
if (app == active_components_v1_.end()) {
|
||||
FML_LOG(INFO)
|
||||
<< "The remote end of the component runner tried to terminate an "
|
||||
"component that has already been terminated, possibly because we "
|
||||
"initiated the termination";
|
||||
return;
|
||||
}
|
||||
ActiveComponent& active_component = app->second;
|
||||
ActiveComponentV1& active_component = app->second;
|
||||
|
||||
// Grab the items out of the entry because we will have to rethread the
|
||||
// destruction.
|
||||
std::unique_ptr<Component> component_to_destroy =
|
||||
std::unique_ptr<ComponentV1> component_to_destroy =
|
||||
std::move(active_component.component);
|
||||
std::unique_ptr<fml::Thread> component_thread =
|
||||
std::move(active_component.platform_thread);
|
||||
|
||||
// Delete the entry.
|
||||
active_components_.erase(component);
|
||||
active_components_v1_.erase(component);
|
||||
|
||||
// Post the task to destroy the component and quit its message loop.
|
||||
component_thread->GetTaskRunner()->PostTask(fml::MakeCopyable(
|
||||
[instance = std::move(component_to_destroy),
|
||||
thread = component_thread.get()]() mutable { instance.reset(); }));
|
||||
|
||||
// Terminate and join the thread's message loop.
|
||||
component_thread->Join();
|
||||
}
|
||||
|
||||
// CF v2 lifecycle methods.
|
||||
|
||||
void Runner::RegisterComponentV2(
|
||||
fidl::InterfaceRequest<fuchsia::component::runner::ComponentRunner>
|
||||
request) {
|
||||
active_components_v2_bindings_.AddBinding(this, std::move(request));
|
||||
}
|
||||
|
||||
void Runner::Start(
|
||||
fuchsia::component::runner::ComponentStartInfo start_info,
|
||||
fidl::InterfaceRequest<fuchsia::component::runner::ComponentController>
|
||||
controller) {
|
||||
// TRACE_DURATION currently requires that the string data does not change
|
||||
// in the traced scope. Since |package| gets moved in the ComponentV2::Create
|
||||
// call below, we cannot ensure that |package.resolved_url| does not move or
|
||||
// change, so we make a copy to pass to TRACE_DURATION.
|
||||
// TODO(PT-169): Remove this copy when TRACE_DURATION reads string arguments
|
||||
// eagerly.
|
||||
const std::string url_copy = start_info.resolved_url();
|
||||
TRACE_EVENT1("flutter", "Start", "url", url_copy.c_str());
|
||||
|
||||
// Notes on component termination: Components typically terminate on the
|
||||
// thread on which they were created. This usually means the thread was
|
||||
// specifically created to host the component. But we want to ensure that
|
||||
// access to the active components collection is made on the same thread. So
|
||||
// we capture the runner in the termination callback. There is no risk of
|
||||
// there being multiple component runner instances in the process at the same
|
||||
// time. So it is safe to use the raw pointer.
|
||||
ComponentV2::TerminationCallback termination_callback =
|
||||
[component_runner = this](const ComponentV2* component) {
|
||||
component_runner->task_runner_->PostTask(
|
||||
[component_runner, component]() {
|
||||
component_runner->OnComponentV2Terminate(component);
|
||||
});
|
||||
};
|
||||
|
||||
ActiveComponentV2 active_component = ComponentV2::Create(
|
||||
std::move(termination_callback), std::move(start_info),
|
||||
context_->svc() /* runner_incoming_services */, std::move(controller));
|
||||
|
||||
auto key = active_component.component.get();
|
||||
active_components_v2_[key] = std::move(active_component);
|
||||
}
|
||||
|
||||
void Runner::OnComponentV2Terminate(const ComponentV2* component) {
|
||||
auto active_component_it = active_components_v2_.find(component);
|
||||
if (active_component_it == active_components_v2_.end()) {
|
||||
FML_LOG(INFO)
|
||||
<< "The remote end of the component runner tried to terminate an "
|
||||
"component that has already been terminated, possibly because we "
|
||||
"initiated the termination";
|
||||
return;
|
||||
}
|
||||
ActiveComponentV2& active_component = active_component_it->second;
|
||||
|
||||
// Grab the items out of the entry because we will have to rethread the
|
||||
// destruction.
|
||||
std::unique_ptr<ComponentV2> component_to_destroy =
|
||||
std::move(active_component.component);
|
||||
std::unique_ptr<fml::Thread> component_thread =
|
||||
std::move(active_component.platform_thread);
|
||||
|
||||
// Delete the entry.
|
||||
active_components_v2_.erase(component);
|
||||
|
||||
// Post the task to destroy the component and quit its message loop.
|
||||
component_thread->GetTaskRunner()->PostTask(fml::MakeCopyable(
|
||||
@ -317,7 +398,7 @@ void Runner::SetupTraceObserver() {
|
||||
runner->prolonged_context_ = trace_acquire_prolonged_context();
|
||||
Dart_StartProfiling();
|
||||
} else if (trace_state() == TRACE_STOPPING) {
|
||||
for (auto& it : runner->active_components_) {
|
||||
for (auto& it : runner->active_components_v1_) {
|
||||
fml::AutoResetWaitableEvent latch;
|
||||
fml::TaskRunner::RunNowOrPostTask(
|
||||
it.second.platform_thread->GetTaskRunner(), [&]() {
|
||||
@ -326,6 +407,8 @@ void Runner::SetupTraceObserver() {
|
||||
});
|
||||
latch.Wait();
|
||||
}
|
||||
// TODO(fxb/50694): Write v2 component profiles to trace once we're
|
||||
// convinced they're stable.
|
||||
Dart_StopProfiling();
|
||||
trace_release_prolonged_context(runner->prolonged_context_);
|
||||
}
|
||||
|
||||
@ -8,13 +8,15 @@
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <fuchsia/component/runner/cpp/fidl.h>
|
||||
#include <fuchsia/sys/cpp/fidl.h>
|
||||
#include <lib/async-loop/cpp/loop.h>
|
||||
#include <lib/sys/cpp/component_context.h>
|
||||
#include <lib/trace-engine/instrumentation.h>
|
||||
#include <lib/trace/observer.h>
|
||||
|
||||
#include "component.h"
|
||||
#include "component_v1.h"
|
||||
#include "component_v2.h"
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "fml/memory/ref_ptr.h"
|
||||
#include "fml/task_runner.h"
|
||||
@ -23,9 +25,14 @@
|
||||
|
||||
namespace flutter_runner {
|
||||
|
||||
// Publishes the |fuchsia::sys::Runner| service and runs components on
|
||||
// their own threads.
|
||||
class Runner final : public fuchsia::sys::Runner {
|
||||
/// Publishes the CF v1 and CF v2 runner services.
|
||||
///
|
||||
/// Each component will be run on a separate thread dedicated to that component.
|
||||
///
|
||||
/// TODO(fxb/50694): Add unit tests for CF v2.
|
||||
class Runner final
|
||||
: public fuchsia::sys::Runner /* CF v1 */,
|
||||
public fuchsia::component::runner::ComponentRunner /* CF v2 */ {
|
||||
public:
|
||||
// Does not take ownership of context.
|
||||
Runner(fml::RefPtr<fml::TaskRunner> task_runner,
|
||||
@ -34,15 +41,42 @@ class Runner final : public fuchsia::sys::Runner {
|
||||
~Runner();
|
||||
|
||||
private:
|
||||
// CF v1 lifecycle methods.
|
||||
// TODO(fxb/50694) Deprecate these once all Flutter components have been
|
||||
// ported to CF v2.
|
||||
|
||||
// |fuchsia::sys::Runner|
|
||||
void StartComponent(fuchsia::sys::Package package,
|
||||
fuchsia::sys::StartupInfo startup_info,
|
||||
fidl::InterfaceRequest<fuchsia::sys::ComponentController>
|
||||
controller) override;
|
||||
|
||||
void RegisterComponent(fidl::InterfaceRequest<fuchsia::sys::Runner> request);
|
||||
/// Registers a new CF v1 component with this runner, binding the component
|
||||
/// to this runner.
|
||||
void RegisterComponentV1(
|
||||
fidl::InterfaceRequest<fuchsia::sys::Runner> request);
|
||||
|
||||
void OnComponentTerminate(const Component* component);
|
||||
/// Callback that should be fired when a registered CF v2 component is
|
||||
/// terminated.
|
||||
void OnComponentV1Terminate(const ComponentV1* component);
|
||||
|
||||
// CF v2 lifecycle methods.
|
||||
|
||||
// |fuchsia::component::runner::ComponentRunner|
|
||||
void Start(
|
||||
fuchsia::component::runner::ComponentStartInfo start_info,
|
||||
fidl::InterfaceRequest<fuchsia::component::runner::ComponentController>
|
||||
controller) override;
|
||||
|
||||
/// Registers a new CF v2 component with this runner, binding the component
|
||||
/// to this runner.
|
||||
void RegisterComponentV2(
|
||||
fidl::InterfaceRequest<fuchsia::component::runner::ComponentRunner>
|
||||
request);
|
||||
|
||||
/// Callback that should be fired when a registered CF v2 component is
|
||||
/// terminated.
|
||||
void OnComponentV2Terminate(const ComponentV2* component);
|
||||
|
||||
void SetupICU();
|
||||
|
||||
@ -62,8 +96,21 @@ class Runner final : public fuchsia::sys::Runner {
|
||||
fml::RefPtr<fml::TaskRunner> task_runner_;
|
||||
|
||||
sys::ComponentContext* context_;
|
||||
fidl::BindingSet<fuchsia::sys::Runner> active_components_bindings_;
|
||||
std::unordered_map<const Component*, ActiveComponent> active_components_;
|
||||
|
||||
// CF v1 component state.
|
||||
fidl::BindingSet<fuchsia::sys::Runner> active_components_v1_bindings_;
|
||||
std::unordered_map<const ComponentV1*, ActiveComponentV1>
|
||||
active_components_v1_;
|
||||
|
||||
// CF v2 component state.
|
||||
|
||||
/// The components that are currently bound to this runner.
|
||||
fidl::BindingSet<fuchsia::component::runner::ComponentRunner>
|
||||
active_components_v2_bindings_;
|
||||
|
||||
/// The components that are currently actively running on threads.
|
||||
std::unordered_map<const ComponentV2*, ActiveComponentV2>
|
||||
active_components_v2_;
|
||||
|
||||
#if !defined(DART_PRODUCT)
|
||||
// The connection between the Dart VM service and The Hub.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user