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/fidl/cpp/client.h>
16#include <lib/vfs/cpp/composed_service_dir.h>
17#include <lib/vfs/cpp/remote_dir.h>
18#include <lib/vfs/cpp/service.h>
20#include <zircon/dlfcn.h>
21#include <zircon/status.h>
22#include <zircon/types.h>
47constexpr char kDataKey[] =
"data";
48constexpr char kAssetsKey[] =
"assets";
52constexpr char kOldGenHeapSizeKey[] =
"old_gen_heap_size";
53constexpr char kExposeDirsKey[] =
"expose_dirs";
55constexpr char kTmpPath[] =
"/tmp";
56constexpr char kServiceRootPath[] =
"/svc";
57constexpr char kRunnerConfigPath[] =
"/config/data/flutter_runner_config";
59std::string DebugLabelForUrl(
const std::string& url) {
60 auto found = url.rfind(
"/");
61 if (found == std::string::npos) {
64 return {url, found + 1};
70void ParseArgs(std::vector<std::string>&
args, ProgramMetadata* metadata) {
74 std::vector<std::string> command_line_args = {
""};
75 command_line_args.insert(command_line_args.end(),
args.begin(),
args.end());
77 command_line_args.begin(), command_line_args.end());
79 std::string old_gen_heap_size_option;
81 &old_gen_heap_size_option)) {
82 int64_t specified_old_gen_heap_size = strtol(
83 old_gen_heap_size_option.c_str(),
nullptr , 10 );
84 if (specified_old_gen_heap_size != 0) {
85 metadata->old_gen_heap_size = specified_old_gen_heap_size;
87 FML_LOG(ERROR) <<
"Invalid old_gen_heap_size: "
88 << old_gen_heap_size_option;
92 std::string expose_dirs_option;
93 if (parsed_args.
GetOptionValue(kExposeDirsKey, &expose_dirs_option)) {
95 std::vector<std::string> expose_dirs;
96 std::stringstream s(expose_dirs_option);
100 metadata->expose_dirs.push_back(dir);
108 const fuchsia::data::Dictionary& program_metadata) {
111 for (
const auto& entry : program_metadata.entries()) {
112 if (entry.key.compare(kDataKey) == 0 && entry.value !=
nullptr) {
113 result.
data_path =
"pkg/" + entry.value->str();
114 }
else if (entry.key.compare(kAssetsKey) == 0 && entry.value !=
nullptr) {
116 }
else if (entry.key.compare(
kArgsKey) == 0 && entry.value !=
nullptr) {
117 ParseArgs(entry.value->str_vec(), &result);
131 fuchsia::component::runner::ComponentStartInfo start_info,
132 std::shared_ptr<sys::ServiceDirectory> runner_incoming_services,
133 fidl::InterfaceRequest<fuchsia::component::runner::ComponentController>
135 auto thread = std::make_unique<fml::Thread>();
136 std::unique_ptr<ComponentV2> component;
139 thread->GetTaskRunner()->PostTask([&]()
mutable {
141 new ComponentV2(std::move(termination_callback), std::move(start_info),
142 runner_incoming_services, std::move(controller)));
147 return {.platform_thread = std::move(thread),
148 .component = std::move(component)};
151ComponentV2::ComponentV2(
152 TerminationCallback termination_callback,
153 fuchsia::component::runner::ComponentStartInfo start_info,
154 std::shared_ptr<sys::ServiceDirectory> runner_incoming_services,
155 fidl::InterfaceRequest<fuchsia::component::runner::ComponentController>
156 component_controller_request)
157 : termination_callback_(
std::move(termination_callback)),
158 debug_label_(DebugLabelForUrl(start_info.resolved_url())),
159 component_controller_(this),
160 outgoing_dir_(new vfs::PseudoDir()),
161 runtime_dir_(new vfs::PseudoDir()),
162 runner_incoming_services_(runner_incoming_services),
163 weak_factory_(this) {
164 component_controller_.set_error_handler([
this](zx_status_t status) {
165 FML_LOG(ERROR) <<
"ComponentController binding error for component("
166 << debug_label_ <<
"): " << zx_status_get_string(status);
168 zx_status_t(fuchsia::component::Error::INSTANCE_CANNOT_START));
174 FML_LOG(WARNING) <<
"program() arguments are currently ignored (fxb/88391).";
178 if (metadata.data_path.empty()) {
179 FML_LOG(ERROR) <<
"Could not find a /pkg/data directory for "
180 << start_info.resolved_url();
187 if (start_info.has_ns()) {
188 for (
auto& entry : *start_info.mutable_ns()) {
191 const auto&
path = entry.path();
192 if (path == kTmpPath) {
198 if (!entry.has_directory()) {
199 FML_LOG(ERROR) <<
"Namespace entry at path (" <<
path
200 <<
") has no directory.";
205 if (path == kServiceRootPath) {
206 svc_ = std::make_unique<sys::ServiceDirectory>(
207 std::move(*entry.mutable_directory()));
208 dir = svc_->CloneChannel().TakeChannel();
210 dir = entry.mutable_directory()->TakeChannel();
213 zx_handle_t dir_handle = dir.release();
214 if (fdio_ns_bind(fdio_ns_.
get(),
path.data(), dir_handle) != ZX_OK) {
215 FML_LOG(ERROR) <<
"Could not bind path to namespace: " <<
path;
216 zx_handle_close(dir_handle);
226 constexpr mode_t
mode = O_RDONLY | O_DIRECTORY;
228 component_assets_directory_.
reset(
229 openat(ns_fd.get(), metadata.assets_path.c_str(), mode));
232 component_data_directory_.
reset(
233 openat(ns_fd.get(), metadata.data_path.c_str(), mode));
238 if (start_info.has_runtime_dir()) {
239 fidl::ServerEnd<fuchsia_io::Directory> server_end{
240 start_info.mutable_runtime_dir()->TakeChannel()};
242 fuchsia_io::wire::kPermReadable | fuchsia_io::wire::kPermWritable,
243 std::move(server_end));
247 if (start_info.has_outgoing_dir()) {
248 fidl::ServerEnd<fuchsia_io::Directory> server_end{
249 start_info.mutable_outgoing_dir()->TakeChannel()};
250 outgoing_dir_->Serve(
251 fuchsia_io::wire::kPermReadable | fuchsia_io::wire::kPermWritable,
252 std::move(server_end));
255 directory_request_ = directory_ptr_.NewRequest();
257 fuchsia::io::DirectoryHandle flutter_public_dir;
259 auto request = flutter_public_dir.NewRequest().TakeChannel();
260 const zx_status_t status =
261 fdio_open3_at(directory_ptr_.channel().get(),
"svc",
262 uint64_t{fuchsia::io::PERM_READABLE}, request.release());
263 if (status != ZX_OK) {
264 FML_LOG(ERROR) <<
"Failed to open /svc in outgoing directory: "
265 << zx_status_get_string(status);
270 auto composed_service_dir = std::make_unique<vfs::ComposedServiceDir>();
271 composed_service_dir->SetFallback(
272 fidl::ClientEnd<fuchsia_io::Directory>(flutter_public_dir.TakeChannel()));
275 directory_ptr_->Open(
".",
276 fuchsia::io::Flags::PROTOCOL_NODE |
277 fuchsia::io::Flags::FLAG_SEND_REPRESENTATION,
278 {}, cloned_directory_ptr_.NewRequest().TakeChannel());
282 std::vector<std::string> other_dirs = {
"debug",
"ctrl"};
283 for (
auto dir : metadata.expose_dirs) {
284 other_dirs.push_back(dir);
287 cloned_directory_ptr_.events().OnRepresentation = [
this,
288 other_dirs](
auto unused) {
289 cloned_directory_ptr_.Unbind();
291 for (
auto& dir_str : other_dirs) {
292 fuchsia::io::DirectoryHandle dir;
293 auto request = dir.NewRequest().TakeChannel();
294 const zx_status_t status =
295 fdio_open3_at(directory_ptr_.channel().get(), dir_str.c_str(),
296 uint64_t{fuchsia::io::Flags::PROTOCOL_DIRECTORY |
297 fuchsia::io::PERM_READABLE},
299 if (status == ZX_OK) {
300 outgoing_dir_->AddEntry(
302 std::make_unique<vfs::RemoteDir>(dir.TakeChannel()));
304 FML_LOG(ERROR) <<
"could not add out directory entry(" << dir_str
305 <<
") for flutter component(" << debug_label_
306 <<
"): " << zx_status_get_string(status);
311 cloned_directory_ptr_.set_error_handler(
312 [
this](zx_status_t status) { cloned_directory_ptr_.Unbind(); });
325 composed_service_dir->AddService(
326 fuchsia::ui::app::ViewProvider::Name_,
327 std::make_unique<vfs::Service>(
328 [
this](zx::channel
channel, async_dispatcher_t* dispatcher) {
329 shells_bindings_.AddBinding(
330 this, fidl::InterfaceRequest<fuchsia::ui::app::ViewProvider>(
333 outgoing_dir_->AddEntry(
"svc", std::move(composed_service_dir));
336 if (component_controller_request) {
337 component_controller_.Bind(std::move(component_controller_request));
341 std::string json_string;
343 product_config_ = FlutterRunnerProductConfiguration(json_string);
344 FML_LOG(INFO) <<
"Successfully loaded runner configuration: "
347 FML_LOG(WARNING) <<
"Failed to load runner configuration from "
348 << kRunnerConfigPath <<
"; using default config values.";
355 std::shared_ptr<dart_utils::ElfSnapshot> snapshot =
356 std::make_shared<dart_utils::ElfSnapshot>();
357 if (snapshot->Load(component_data_directory_.
get(),
358 "app_aot_snapshot.so")) {
359 const uint8_t* isolate_data = snapshot->IsolateData();
360 const uint8_t* isolate_instructions = snapshot->IsolateInstrs();
361 const uint8_t* vm_data = snapshot->VmData();
362 const uint8_t* vm_instructions = snapshot->VmInstrs();
363 if (isolate_data ==
nullptr || isolate_instructions ==
nullptr ||
364 vm_data ==
nullptr || vm_instructions ==
nullptr) {
365 FML_LOG(FATAL) <<
"ELF snapshot missing AOT symbols.";
368 auto hold_snapshot = [snapshot](
const uint8_t* _,
size_t __) {};
370 return std::make_unique<fml::NonOwnedMapping>(vm_data, 0, hold_snapshot,
374 return std::make_unique<fml::NonOwnedMapping>(
375 vm_instructions, 0, hold_snapshot,
true );
378 return std::make_unique<fml::NonOwnedMapping>(
379 isolate_data, 0, hold_snapshot,
true );
382 isolate_instructions]() {
383 return std::make_unique<fml::NonOwnedMapping>(
384 isolate_instructions, 0, hold_snapshot,
true );
386 isolate_snapshot_ = fml::MakeRefCounted<flutter::DartSnapshot>(
387 std::make_shared<fml::NonOwnedMapping>(isolate_data, 0, hold_snapshot,
389 std::make_shared<fml::NonOwnedMapping>(isolate_instructions, 0,
393 const int namespace_fd = component_data_directory_.
get();
395 return LoadFile(namespace_fd,
"vm_snapshot_data.bin",
399 return LoadFile(namespace_fd,
"vm_snapshot_instructions.bin",
403 return LoadFile(namespace_fd,
"isolate_snapshot_data.bin",
407 return LoadFile(namespace_fd,
"isolate_snapshot_instructions.bin",
422#if defined(DART_PRODUCT)
447 settings_.
log_tag = debug_label_ + std::string{
"(flutter)"};
449 if (metadata.old_gen_heap_size.has_value()) {
456#if !defined(DART_PRODUCT) && (!defined(FLUTTER_PROFILE) || !defined(NDEBUG))
470 std::placeholders::_1, std::placeholders::_2);
474 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;
630 auto status = zx::eventpair::create(
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();
665 bool method_has_response) {
666 FML_LOG(ERROR) <<
"Unknown method called on ComponentV2. Ordinal: "
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)
void handle_unknown_method(uint64_t ordinal, bool method_has_response) override
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[]
G_BEGIN_DECLS GBytes * message
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
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all 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
fml::TaskQueueId CurrentMessageLoopAddAfterTaskObserver(intptr_t key, fit::closure observer)
void CurrentMessageLoopRemoveAfterTaskObserver(fml::TaskQueueId queue_id, 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