8#include <fuchsia/mem/cpp/fidl.h>
9#include <lib/async-loop/cpp/loop.h>
10#include <lib/async/cpp/task.h>
11#include <lib/async/default.h>
12#include <lib/fdio/directory.h>
13#include <lib/fdio/io.h>
14#include <lib/fdio/namespace.h>
15#include <lib/vfs/cpp/composed_service_dir.h>
16#include <lib/vfs/cpp/remote_dir.h>
17#include <lib/vfs/cpp/service.h>
19#include <zircon/dlfcn.h>
20#include <zircon/status.h>
21#include <zircon/types.h>
28#include "flutter/fml/command_line.h"
29#include "flutter/fml/mapping.h"
30#include "flutter/fml/platform/fuchsia/task_observers.h"
31#include "flutter/fml/synchronization/waitable_event.h"
32#include "flutter/fml/unique_fd.h"
33#include "flutter/runtime/dart_vm_lifecycle.h"
34#include "flutter/shell/common/switches.h"
46constexpr char kDataKey[] =
"data";
47constexpr char kAssetsKey[] =
"assets";
51constexpr char kOldGenHeapSizeKey[] =
"old_gen_heap_size";
52constexpr char kExposeDirsKey[] =
"expose_dirs";
54constexpr char kTmpPath[] =
"/tmp";
55constexpr char kServiceRootPath[] =
"/svc";
56constexpr char kRunnerConfigPath[] =
"/config/data/flutter_runner_config";
58std::string DebugLabelForUrl(
const std::string& url) {
59 auto found = url.rfind(
"/");
60 if (found == std::string::npos) {
63 return {url, found + 1};
69void ParseArgs(std::vector<std::string>&
args, ProgramMetadata* metadata) {
73 std::vector<std::string> command_line_args = {
""};
74 command_line_args.insert(command_line_args.end(),
args.begin(),
args.end());
76 command_line_args.begin(), command_line_args.end());
78 std::string old_gen_heap_size_option;
80 &old_gen_heap_size_option)) {
81 int64_t specified_old_gen_heap_size = strtol(
82 old_gen_heap_size_option.c_str(),
nullptr , 10 );
83 if (specified_old_gen_heap_size != 0) {
84 metadata->old_gen_heap_size = specified_old_gen_heap_size;
87 << old_gen_heap_size_option;
91 std::string expose_dirs_option;
92 if (parsed_args.
GetOptionValue(kExposeDirsKey, &expose_dirs_option)) {
94 std::vector<std::string> expose_dirs;
95 std::stringstream
s(expose_dirs_option);
99 metadata->expose_dirs.push_back(
dir);
107 const fuchsia::data::Dictionary& program_metadata) {
110 for (
const auto& entry : program_metadata.entries()) {
111 if (entry.key.compare(kDataKey) == 0 && entry.value !=
nullptr) {
112 result.data_path =
"pkg/" + entry.value->str();
113 }
else if (entry.key.compare(kAssetsKey) == 0 && entry.value !=
nullptr) {
114 result.assets_path =
"pkg/" + entry.value->str();
115 }
else if (entry.key.compare(
kArgsKey) == 0 && entry.value !=
nullptr) {
121 if (
result.assets_path.empty()) {
130 fuchsia::component::runner::ComponentStartInfo start_info,
131 std::shared_ptr<sys::ServiceDirectory> runner_incoming_services,
132 fidl::InterfaceRequest<fuchsia::component::runner::ComponentController>
134 auto thread = std::make_unique<fml::Thread>();
135 std::unique_ptr<ComponentV2> component;
138 thread->GetTaskRunner()->PostTask([&]()
mutable {
140 new ComponentV2(std::move(termination_callback), std::move(start_info),
141 runner_incoming_services, std::move(controller)));
146 return {.platform_thread = std::move(thread),
147 .component = std::move(component)};
150ComponentV2::ComponentV2(
151 TerminationCallback termination_callback,
152 fuchsia::component::runner::ComponentStartInfo start_info,
153 std::shared_ptr<sys::ServiceDirectory> runner_incoming_services,
154 fidl::InterfaceRequest<fuchsia::component::runner::ComponentController>
155 component_controller_request)
156 : termination_callback_(
std::move(termination_callback)),
157 debug_label_(DebugLabelForUrl(start_info.resolved_url())),
158 component_controller_(this),
159 outgoing_dir_(new vfs::PseudoDir()),
160 runtime_dir_(new vfs::PseudoDir()),
161 runner_incoming_services_(runner_incoming_services),
162 weak_factory_(this) {
163 component_controller_.set_error_handler([
this](zx_status_t status) {
164 FML_LOG(
ERROR) <<
"ComponentController binding error for component("
165 << debug_label_ <<
"): " << zx_status_get_string(status);
167 zx_status_t(fuchsia::component::Error::INSTANCE_CANNOT_START));
173 FML_LOG(WARNING) <<
"program() arguments are currently ignored (fxb/88391).";
177 if (metadata.data_path.empty()) {
178 FML_LOG(
ERROR) <<
"Could not find a /pkg/data directory for "
179 << start_info.resolved_url();
186 if (start_info.has_ns()) {
187 for (
auto& entry : *start_info.mutable_ns()) {
190 const auto&
path = entry.path();
191 if (
path == kTmpPath) {
197 if (!entry.has_directory()) {
199 <<
") has no directory.";
204 if (
path == kServiceRootPath) {
205 svc_ = std::make_unique<sys::ServiceDirectory>(
206 std::move(*entry.mutable_directory()));
207 dir = svc_->CloneChannel().TakeChannel();
209 dir = entry.mutable_directory()->TakeChannel();
212 zx_handle_t dir_handle =
dir.release();
213 if (fdio_ns_bind(fdio_ns_.
get(),
path.data(), dir_handle) != ZX_OK) {
215 zx_handle_close(dir_handle);
225 constexpr mode_t
mode = O_RDONLY | O_DIRECTORY;
227 component_assets_directory_.
reset(
228 openat(ns_fd.get(), metadata.assets_path.c_str(),
mode));
231 component_data_directory_.
reset(
232 openat(ns_fd.get(), metadata.data_path.c_str(),
mode));
237 if (start_info.has_runtime_dir()) {
238 runtime_dir_->Serve(fuchsia::io::OpenFlags::RIGHT_READABLE |
239 fuchsia::io::OpenFlags::RIGHT_WRITABLE |
240 fuchsia::io::OpenFlags::DIRECTORY,
241 start_info.mutable_runtime_dir()->TakeChannel());
245 if (start_info.has_outgoing_dir()) {
246 outgoing_dir_->Serve(fuchsia::io::OpenFlags::RIGHT_READABLE |
247 fuchsia::io::OpenFlags::RIGHT_WRITABLE |
248 fuchsia::io::OpenFlags::DIRECTORY,
249 start_info.mutable_outgoing_dir()->TakeChannel());
252 directory_request_ = directory_ptr_.NewRequest();
254 fuchsia::io::DirectoryHandle flutter_public_dir;
258 auto request = flutter_public_dir.NewRequest().TakeChannel();
259 fdio_service_connect_at(directory_ptr_.channel().get(),
"svc",
262#pragma clang diagnostic push
263#pragma clang diagnostic ignored "-Wdeprecated-declarations"
265 auto composed_service_dir = std::make_unique<vfs::ComposedServiceDir>();
266 composed_service_dir->set_fallback(std::move(flutter_public_dir));
268#pragma clang diagnostic pop
271 directory_ptr_->Clone(fuchsia::io::OpenFlags::DESCRIBE |
272 fuchsia::io::OpenFlags::CLONE_SAME_RIGHTS,
273 cloned_directory_ptr_.NewRequest());
277 std::vector<std::string> other_dirs = {
"debug",
"ctrl"};
278 for (
auto dir : metadata.expose_dirs) {
279 other_dirs.push_back(
dir);
282 cloned_directory_ptr_.events().OnOpen = [
this, other_dirs](zx_status_t status,
284 cloned_directory_ptr_.Unbind();
285 if (status != ZX_OK) {
286 FML_LOG(
ERROR) <<
"could not bind out directory for flutter component("
287 << debug_label_ <<
"): " << zx_status_get_string(status);
292 for (
auto& dir_str : other_dirs) {
293 fuchsia::io::DirectoryHandle
dir;
294 auto request =
dir.NewRequest().TakeChannel();
295 auto status = fdio_open_at(
296 directory_ptr_.channel().get(), dir_str.c_str(),
297 static_cast<uint32_t
>(fuchsia::io::OpenFlags::DIRECTORY |
298 fuchsia::io::OpenFlags::RIGHT_READABLE),
300 if (status == ZX_OK) {
301 outgoing_dir_->AddEntry(
303 std::make_unique<vfs::RemoteDir>(
dir.TakeChannel()));
305 FML_LOG(
ERROR) <<
"could not add out directory entry(" << dir_str
306 <<
") for flutter component(" << debug_label_
307 <<
"): " << zx_status_get_string(status);
312 cloned_directory_ptr_.set_error_handler(
313 [
this](zx_status_t status) { cloned_directory_ptr_.Unbind(); });
326 composed_service_dir->AddService(
327 fuchsia::ui::app::ViewProvider::Name_,
328 std::make_unique<vfs::Service>(
329 [
this](zx::channel channel, async_dispatcher_t* dispatcher) {
330 shells_bindings_.AddBinding(
331 this, fidl::InterfaceRequest<fuchsia::ui::app::ViewProvider>(
332 std::move(channel)));
334 outgoing_dir_->AddEntry(
"svc", std::move(composed_service_dir));
337 if (component_controller_request) {
338 component_controller_.Bind(std::move(component_controller_request));
342 std::string json_string;
344 product_config_ = FlutterRunnerProductConfiguration(json_string);
345 FML_LOG(INFO) <<
"Successfully loaded runner configuration: "
348 FML_LOG(WARNING) <<
"Failed to load runner configuration from "
349 << kRunnerConfigPath <<
"; using default config values.";
356 std::shared_ptr<dart_utils::ElfSnapshot> snapshot =
357 std::make_shared<dart_utils::ElfSnapshot>();
358 if (snapshot->Load(component_data_directory_.
get(),
359 "app_aot_snapshot.so")) {
360 const uint8_t* isolate_data = snapshot->IsolateData();
361 const uint8_t* isolate_instructions = snapshot->IsolateInstrs();
362 const uint8_t* vm_data = snapshot->VmData();
363 const uint8_t* vm_instructions = snapshot->VmInstrs();
364 if (isolate_data ==
nullptr || isolate_instructions ==
nullptr ||
365 vm_data ==
nullptr || vm_instructions ==
nullptr) {
369 auto hold_snapshot = [snapshot](
const uint8_t* _,
size_t __) {};
371 return std::make_unique<fml::NonOwnedMapping>(vm_data, 0, hold_snapshot,
375 return std::make_unique<fml::NonOwnedMapping>(
376 vm_instructions, 0, hold_snapshot,
true );
379 return std::make_unique<fml::NonOwnedMapping>(
380 isolate_data, 0, hold_snapshot,
true );
383 isolate_instructions]() {
384 return std::make_unique<fml::NonOwnedMapping>(
385 isolate_instructions, 0, hold_snapshot,
true );
387 isolate_snapshot_ = fml::MakeRefCounted<flutter::DartSnapshot>(
388 std::make_shared<fml::NonOwnedMapping>(isolate_data, 0, hold_snapshot,
390 std::make_shared<fml::NonOwnedMapping>(isolate_instructions, 0,
394 const int namespace_fd = component_data_directory_.
get();
396 return LoadFile(namespace_fd,
"vm_snapshot_data.bin",
400 return LoadFile(namespace_fd,
"vm_snapshot_instructions.bin",
404 return LoadFile(namespace_fd,
"isolate_snapshot_data.bin",
408 return LoadFile(namespace_fd,
"isolate_snapshot_instructions.bin",
423#if defined(DART_PRODUCT)
448 settings_.
log_tag = debug_label_ + std::string{
"(flutter)"};
450 if (metadata.old_gen_heap_size.has_value()) {
457#if !defined(DART_PRODUCT) && (!defined(FLUTTER_PROFILE) || !defined(NDEBUG))
471 std::placeholders::_1, std::placeholders::_2);
479 std::cout << tag <<
": ";
481 std::cout <<
message << std::endl;
487 settings_.
dart_flags.push_back(
"--no_profile_vm");
491#if defined(__aarch64__)
492 settings_.
dart_flags.push_back(
"--profile_period=10000");
496 const std::string component_url = start_info.resolved_url();
498 platform_task_runner,
499 runner_incoming_services,
501 const std::string&
error,
502 const std::string& stack_trace) {
510 platform_task_runner->PostTask([weak, runner_incoming_services,
511 component_url,
error, stack_trace]() {
517 <<
"Exception was thrown which was not caught in Flutter app: "
523 <<
"Exception was thrown which was not caught in Flutter app: "
539void ComponentV2::Kill() {
540 FML_VLOG(1) <<
"received Kill event";
549 auto [got_return_code, return_code] = last_return_code_;
550 if (got_return_code && return_code == 0) {
551 KillWithEpitaph(ZX_OK);
553 if (got_return_code) {
554 FML_LOG(
ERROR) <<
"Component exited with non-zero return code: "
557 FML_LOG(
ERROR) <<
"Failed to get return code for component";
560 KillWithEpitaph(zx_status_t(fuchsia::component::Error::INTERNAL));
567void ComponentV2::KillWithEpitaph(zx_status_t epitaph_status) {
568 component_controller_.set_error_handler(
nullptr);
569 component_controller_.Close(epitaph_status);
571 termination_callback_(
this);
576void ComponentV2::Stop() {
577 FML_VLOG(1) <<
"received Stop event";
581 KillWithEpitaph(ZX_OK);
584void ComponentV2::OnEngineTerminate(
const Engine* shell_holder) {
585 auto found = std::find_if(shell_holders_.begin(), shell_holders_.end(),
586 [shell_holder](
const auto& holder) {
587 return holder.get() == shell_holder;
590 if (found == shell_holders_.end()) {
593 FML_LOG(
ERROR) <<
"Tried to terminate an unregistered shell holder.";
602 auto return_code = shell_holder->GetEngineReturnCode();
603 if (return_code.has_value()) {
604 last_return_code_ = {
true, return_code.value()};
606 FML_LOG(
ERROR) <<
"Failed to get return code from terminated shell holder.";
609 shell_holders_.erase(found);
611 if (shell_holders_.empty()) {
612 FML_VLOG(1) <<
"Killing component because all shell holders have been "
620void ComponentV2::CreateView2(fuchsia::ui::app::CreateView2Args view_args) {
623 <<
"Component incoming services was invalid when attempting to "
624 "create a shell for a view provider request.";
628 fuchsia::ui::views::ViewRefControl view_ref_control;
629 fuchsia::ui::views::ViewRef view_ref;
631 0u, &view_ref_control.reference, &view_ref.reference);
632 ZX_ASSERT(status == ZX_OK);
633 view_ref_control.reference.replace(
634 ZX_DEFAULT_EVENTPAIR_RIGHTS & (~ZX_RIGHT_DUPLICATE),
635 &view_ref_control.reference);
636 view_ref.reference.replace(ZX_RIGHTS_BASIC, &view_ref.reference);
638 std::make_pair(std::move(view_ref_control), std::move(view_ref));
640 shell_holders_.emplace(std::make_unique<Engine>(
644 runner_incoming_services_,
647 *view_args.mutable_view_creation_token()),
648 std::move(view_ref_pair),
650 std::move(directory_request_),
652 std::vector<std::string>()
656#if !defined(DART_PRODUCT)
658 for (
const auto&
engine : shell_holders_) {
659 engine->WriteProfileToTrace();
static bool IsRunningPrecompiledCode()
Checks if VM instances in the process can run precompiled code. This call can be made at any time and...
void WriteProfileToTrace() const
static ProgramMetadata ParseProgramMetadata(const fuchsia::data::Dictionary &program_metadata)
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)
const std::string & GetDebugLabel() const
fit::function< void(const ComponentV2 *)> TerminationCallback
bool GetOptionValue(std::string_view name, std::string *value) const
fml::RefPtr< fml::TaskRunner > GetTaskRunner() const
static FML_EMBEDDER_ONLY MessageLoop & GetCurrent()
void reset(const T &value=Traits::InvalidValue())
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
static constexpr char kArgsKey[]
const uint8_t uint32_t uint32_t GError ** error
#define FML_VLOG(verbose_level)
#define FML_LOG(severity)
#define FML_DCHECK(condition)
bool ReadFileToString(const std::string &path, std::string *result)
void HandleException(std::shared_ptr<::sys::ServiceDirectory > services, const std::string &component_url, const std::string &error, const std::string &stack_trace)
void BindTemp(fdio_ns_t *ns)
std::unique_ptr< fml::FileMapping > MakeFileMapping(const char *path, bool executable)
std::unique_ptr< fml::Mapping > LoadFile(int namespace_fd, const char *path, bool executable)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
void CurrentMessageLoopAddAfterTaskObserver(intptr_t key, fit::closure observer)
void CurrentMessageLoopRemoveAfterTaskObserver(intptr_t key)
CommandLine CommandLineFromIterators(InputIterator first, InputIterator last)
std::string advisory_script_entrypoint
fml::UniqueFD::element_type assets_dir
std::string advisory_script_uri
LogMessageCallback log_message_callback
MappingCallback isolate_snapshot_instr
TaskObserverRemove task_observer_remove
MappingCallback isolate_snapshot_data
MappingCallback vm_snapshot_data
std::string vm_service_host
TaskObserverAdd task_observer_add
std::string application_kernel_list_asset
std::vector< std::string > dart_flags
std::string icu_data_path
bool disable_dart_asserts
UnhandledExceptionCallback unhandled_exception_callback
MappingCallback vm_snapshot_instr
int64_t old_gen_heap_size