diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index 071c6dced30dd..8d4a527959c9b 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -275,6 +275,7 @@ ../../../flutter/shell/platform/fuchsia/flutter/accessibility_bridge_unittest.cc ../../../flutter/shell/platform/fuchsia/flutter/build/asset_package.py ../../../flutter/shell/platform/fuchsia/flutter/build/gen_debug_wrapper_main.py +../../../flutter/shell/platform/fuchsia/flutter/component_v1_unittest.cc ../../../flutter/shell/platform/fuchsia/flutter/component_v2_unittest.cc ../../../flutter/shell/platform/fuchsia/flutter/focus_delegate_unittests.cc ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_intl_unittest.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 6e21e8a07522f..6f660b7ead01d 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -2749,6 +2749,8 @@ ORIGIN: ../../../flutter/shell/platform/fuchsia/dart_runner/service_isolate.h + ORIGIN: ../../../flutter/shell/platform/fuchsia/dart_runner/vmservice/empty.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/fuchsia/flutter/accessibility_bridge.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/fuchsia/flutter/accessibility_bridge.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/fuchsia/flutter/component_v1.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/fuchsia/flutter/component_v1.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/fuchsia/flutter/component_v2.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/fuchsia/flutter/component_v2.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/fuchsia/flutter/engine.cc + ../../../flutter/LICENSE @@ -5303,6 +5305,8 @@ FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/service_isolate.h FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/vmservice/empty.dart FILE: ../../../flutter/shell/platform/fuchsia/flutter/accessibility_bridge.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/accessibility_bridge.h +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_v2.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/component_v2.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/engine.cc @@ -5335,11 +5339,19 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/keyboard.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/keyboard.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/logging.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/main.cc +FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/aot_product_runtime +FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/aot_runtime FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/common.shard.cml FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_aot_product_runner.cml +FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_aot_product_runner.cmx FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cml +FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cml +FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cml +FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx +FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/jit_product_runtime +FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/jit_runtime FILE: ../../../flutter/shell/platform/fuchsia/flutter/platform_view.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/platform_view.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/pointer_delegate.cc diff --git a/shell/platform/fuchsia/flutter/BUILD.gn b/shell/platform/fuchsia/flutter/BUILD.gn index cc1407fa98a0f..13e55261d92f3 100644 --- a/shell/platform/fuchsia/flutter/BUILD.gn +++ b/shell/platform/fuchsia/flutter/BUILD.gn @@ -57,6 +57,8 @@ template("runner_sources") { sources = [ "accessibility_bridge.cc", "accessibility_bridge.h", + "component_v1.cc", + "component_v1.h", "component_v2.cc", "component_v2.h", "engine.cc", @@ -320,6 +322,7 @@ template("jit_runner") { binary = "flutter_jit${product_suffix}_runner" + cmx_file = rebase_path("meta/flutter_jit${product_suffix}_runner.cmx") cml_file = rebase_path("meta/flutter_jit${product_suffix}_runner.cml") resources = [ @@ -399,6 +402,7 @@ template("aot_runner") { ] } + cmx_file = rebase_path("meta/flutter_aot${product_suffix}_runner.cmx") cml_file = rebase_path("meta/flutter_aot${product_suffix}_runner.cml") binary = "flutter_aot${product_suffix}_runner" @@ -496,6 +500,7 @@ if (enable_unittests) { sources = [ "accessibility_bridge_unittest.cc", + "component_v1_unittest.cc", "component_v2_unittest.cc", "flutter_runner_fakes.h", "focus_delegate_unittests.cc", diff --git a/shell/platform/fuchsia/flutter/component_v1.cc b/shell/platform/fuchsia/flutter/component_v1.cc new file mode 100644 index 0000000000000..b869edbdd97d2 --- /dev/null +++ b/shell/platform/fuchsia/flutter/component_v1.cc @@ -0,0 +1,609 @@ +// 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_v1.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 kOldGenHeapSizeKey[] = "old_gen_heap_size"; + +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 + +ProgramMetadata ComponentV1::ParseProgramMetadata( + const fidl::VectorPtr& program_metadata) { + ProgramMetadata result; + if (!program_metadata.has_value()) { + return result; + } + + for (const auto& pg : *program_metadata) { + if (pg.key.compare(kDataKey) == 0) { + result.data_path = "pkg/" + pg.value; + } else if (pg.key.compare(kAssetsKey) == 0) { + result.assets_path = "pkg/" + pg.value; + } else if (pg.key.compare(kOldGenHeapSizeKey) == 0) { + int64_t specified_old_gen_heap_size = + strtol(pg.value.c_str(), nullptr /* endptr */, 10 /* base */); + if (specified_old_gen_heap_size != 0) { + result.old_gen_heap_size = specified_old_gen_heap_size; + } else { + FML_LOG(ERROR) << "Invalid old_gen_heap_size: " << pg.value; + } + } + } + + // assets_path defaults to the same as data_path if omitted. + if (result.assets_path.empty()) { + result.assets_path = result.data_path; + } + + return result; +} + +ActiveComponentV1 ComponentV1::Create( + TerminationCallback termination_callback, + fuchsia::sys::Package package, + fuchsia::sys::StartupInfo startup_info, + std::shared_ptr runner_incoming_services, + fidl::InterfaceRequest controller) { + auto thread = std::make_unique(); + std::unique_ptr component; + + fml::AutoResetWaitableEvent latch; + thread->GetTaskRunner()->PostTask([&]() mutable { + component.reset(new ComponentV1(std::move(termination_callback), + std::move(package), std::move(startup_info), + runner_incoming_services, + std::move(controller))); + latch.Signal(); + }); + + latch.Wait(); + return {.platform_thread = std::move(thread), + .component = std::move(component)}; +} + +ComponentV1::ComponentV1( + TerminationCallback termination_callback, + fuchsia::sys::Package package, + fuchsia::sys::StartupInfo startup_info, + std::shared_ptr runner_incoming_services, + fidl::InterfaceRequest + component_controller_request) + : termination_callback_(std::move(termination_callback)), + debug_label_(DebugLabelForUrl(startup_info.launch_info.url)), + component_controller_(this), + outgoing_dir_(new vfs::PseudoDir()), + runner_incoming_services_(runner_incoming_services), + weak_factory_(this) { + component_controller_.set_error_handler( + [this](zx_status_t status) { Kill(); }); + + FML_DCHECK(fdio_ns_.is_valid()); + // LaunchInfo::url non-optional. + auto& launch_info = startup_info.launch_info; + + // LaunchInfo::arguments optional. + if (auto& arguments = launch_info.arguments) { + dart_entrypoint_args_ = arguments.value(); + } + + const ProgramMetadata metadata = + ParseProgramMetadata(startup_info.program_metadata); + + if (metadata.data_path.empty()) { + FML_LOG(ERROR) << "Could not find a /pkg/data directory for " + << package.resolved_url; + return; + } + + // Setup /tmp to be mapped to the process-local memfs. + dart_utils::RunnerTemp::SetupComponent(fdio_ns_.get()); + + // LaunchInfo::flat_namespace optional. + for (size_t i = 0; i < startup_info.flat_namespace.paths.size(); ++i) { + const auto& path = startup_info.flat_namespace.paths.at(i); + if (path == kTmpPath) { + continue; + } + + fidl::InterfaceHandle dir; + if (path == kServiceRootPath) { + svc_ = std::make_unique( + std::move(startup_info.flat_namespace.directories.at(i))); + dir = svc_->CloneChannel(); + } else { + dir = std::move(startup_info.flat_namespace.directories.at(i)); + } + + zx_handle_t dir_handle = dir.TakeChannel().release(); + if (fdio_ns_bind(fdio_ns_.get(), path.data(), dir_handle) != ZX_OK) { + FML_LOG(ERROR) << "Could not bind path to namespace: " << path; + zx_handle_close(dir_handle); + } + } + + { + 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(), metadata.assets_path.c_str(), mode)); + FML_DCHECK(component_assets_directory_.is_valid()); + + component_data_directory_.reset( + openat(ns_fd.get(), metadata.data_path.c_str(), mode)); + FML_DCHECK(component_data_directory_.is_valid()); + } + + // TODO: LaunchInfo::out. + + // TODO: LaunchInfo::err. + + // LaunchInfo::service_request optional. + if (launch_info.directory_request) { + outgoing_dir_->Serve(fuchsia::io::OpenFlags::RIGHT_READABLE | + fuchsia::io::OpenFlags::RIGHT_WRITABLE | + fuchsia::io::OpenFlags::DIRECTORY, + launch_info.directory_request.TakeChannel()); + } + + directory_request_ = directory_ptr_.NewRequest(); + + fuchsia::io::DirectoryHandle 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(); + composed_service_dir->set_fallback(std::move(flutter_public_dir)); + + // Clone and check if client is servicing the directory. + directory_ptr_->Clone(fuchsia::io::OpenFlags::DESCRIBE | + fuchsia::io::OpenFlags::CLONE_SAME_RIGHTS, + cloned_directory_ptr_.NewRequest()); + + cloned_directory_ptr_.events().OnOpen = [this](zx_status_t status, + auto unused) { + 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) { + fuchsia::io::DirectoryHandle dir; + auto request = dir.NewRequest().TakeChannel(); + auto status = fdio_open_at( + directory_ptr_.channel().get(), dir_str, + static_cast(fuchsia::io::OpenFlags::DIRECTORY | + fuchsia::io::OpenFlags::RIGHT_READABLE), + request.release()); + if (status == ZX_OK) { + outgoing_dir_->AddEntry( + dir_str, std::make_unique(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: LaunchInfo::additional_services optional. + + // 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( + [this](zx::channel channel, async_dispatcher_t* dispatcher) { + shells_bindings_.AddBinding( + this, fidl::InterfaceRequest( + 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 snapshot = + std::make_shared(); + 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(vm_data, 0, hold_snapshot, + true /* dontneed_safe */); + }; + settings_.vm_snapshot_instr = [hold_snapshot, vm_instructions]() { + return std::make_unique( + vm_instructions, 0, hold_snapshot, true /* dontneed_safe */); + }; + settings_.isolate_snapshot_data = [hold_snapshot, isolate_data]() { + return std::make_unique( + isolate_data, 0, hold_snapshot, true /* dontneed_safe */); + }; + settings_.isolate_snapshot_instr = [hold_snapshot, + isolate_instructions]() { + return std::make_unique( + isolate_instructions, 0, hold_snapshot, true /* dontneed_safe */); + }; + isolate_snapshot_ = fml::MakeRefCounted( + std::make_shared(isolate_data, 0, hold_snapshot, + true /* dontneed_safe */), + std::make_shared(isolate_instructions, 0, + hold_snapshot, + true /* dontneed_safe */)); + } 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_vm_service = false; +#else + settings_.enable_vm_service = true; + + // TODO(cbracken): pass this in as a param to allow 0.0.0.0, ::1, etc. + settings_.vm_service_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)"}; + + if (metadata.old_gen_heap_size.has_value()) { + settings_.old_gen_heap_size = *metadata.old_gen_heap_size; + } + + // 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.empty()) { + std::cout << tag << ": "; + } + std::cout << message << std::endl; + }; + + settings_.dart_flags = {}; + + // Run in unsound null safety mode as some packages used in Integration + // testing have not been migrated yet. + settings_.dart_flags.push_back("--no-sound-null-safety"); + + // 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 = package.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::OnComponentV1Terminate() 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; + }; +} + +ComponentV1::~ComponentV1() = default; + +const std::string& ComponentV1::GetDebugLabel() const { + return debug_label_; +} + +void ComponentV1::Kill() { + component_controller_.events().OnTerminated( + last_return_code_.second, fuchsia::sys::TerminationReason::EXITED); + + termination_callback_(this); + // WARNING: Don't do anything past this point as this instance may have been + // collected. +} + +void ComponentV1::Detach() { + component_controller_.set_error_handler(nullptr); +} + +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; + }); + + if (found == shell_holders_.end()) { + // This indicates a deeper issue with memory management and should never + // happen. + FML_LOG(ERROR) << "Tried to terminate an unregistered shell holder."; + FML_DCHECK(false); + + return; + } + + // We may launch multiple shell in this component. However, we will + // terminate when the last shell goes away. The error code return 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()}; + } else { + FML_LOG(ERROR) << "Failed to get return code from terminated shell holder."; + } + + shell_holders_.erase(found); + + if (shell_holders_.empty()) { + FML_VLOG(-1) << "Killing component because all shell holders have been " + "terminated."; + Kill(); + // WARNING: Don't do anything past this point because the delegate may have + // collected this instance via the termination callback. + } +} + +void ComponentV1::CreateView( + zx::eventpair token, + fidl::InterfaceRequest /*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 ComponentV1::CreateViewWithViewRef( + zx::eventpair view_token, + fuchsia::ui::views::ViewRefControl control_ref, + fuchsia::ui::views::ViewRef view_ref) { + if (!svc_) { + FML_LOG(ERROR) + << "Component incoming services was invalid when attempting to " + "create a shell for a view provider request."; + return; + } + + shell_holders_.emplace(std::make_unique( + *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 + dart_entrypoint_args_, // dart entrypoint args + true // v1 component + )); +} + +void ComponentV1::CreateView2(fuchsia::ui::app::CreateView2Args view_args) { + if (!svc_) { + FML_LOG(ERROR) + << "Component incoming services was invalid when attempting to " + "create a shell for a view provider request."; + return; + } + + shell_holders_.emplace(std::make_unique( + *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 + dart_entrypoint_args_, // dart entrypoint args + true // v1 component + )); +} + +#if !defined(DART_PRODUCT) +void ComponentV1::WriteProfileToTrace() const { + for (const auto& engine : shell_holders_) { + engine->WriteProfileToTrace(); + } +} +#endif // !defined(DART_PRODUCT) + +} // namespace flutter_runner diff --git a/shell/platform/fuchsia/flutter/component_v1.h b/shell/platform/fuchsia/flutter/component_v1.h new file mode 100644 index 0000000000000..efdc2a338428d --- /dev/null +++ b/shell/platform/fuchsia/flutter/component_v1.h @@ -0,0 +1,140 @@ +// 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_V1_H_ +#define FLUTTER_SHELL_PLATFORM_FUCHSIA_COMPONENT_V1_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "flutter/common/settings.h" +#include "flutter/fml/macros.h" + +#include "engine.h" +#include "flutter_runner_product_configuration.h" +#include "program_metadata.h" +#include "unique_fdio_ns.h" + +namespace flutter_runner { + +class ComponentV1; + +struct ActiveComponentV1 { + std::unique_ptr platform_thread; + std::unique_ptr component; + + ActiveComponentV1& operator=(ActiveComponentV1&& other) noexcept { + if (this != &other) { + this->platform_thread.reset(other.platform_thread.release()); + this->component.reset(other.component.release()); + } + return *this; + } + + ~ActiveComponentV1() = default; +}; + +// Represents an instance of a CF v1 Flutter component that contains one or more +// Flutter engine instances. +class ComponentV1 final : public Engine::Delegate, + public fuchsia::sys::ComponentController, + public fuchsia::ui::app::ViewProvider { + public: + using TerminationCallback = fit::function; + + // 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 ActiveComponentV1 Create( + TerminationCallback termination_callback, + fuchsia::sys::Package package, + fuchsia::sys::StartupInfo startup_info, + std::shared_ptr runner_incoming_services, + fidl::InterfaceRequest controller); + + // Must be called on the same thread returned from the create call. The thread + // may be collected after. + ~ComponentV1(); + + static ProgramMetadata ParseProgramMetadata( + const fidl::VectorPtr& program_metadata); + + const std::string& GetDebugLabel() const; + +#if !defined(DART_PRODUCT) + void WriteProfileToTrace() const; +#endif // !defined(DART_PRODUCT) + + private: + ComponentV1( + TerminationCallback termination_callback, + fuchsia::sys::Package package, + fuchsia::sys::StartupInfo startup_info, + std::shared_ptr runner_incoming_services, + fidl::InterfaceRequest controller); + + // |fuchsia::sys::ComponentController| + void Kill() override; + + // |fuchsia::sys::ComponentController| + void Detach() override; + + // |fuchsia::ui::app::ViewProvider| + void CreateView( + zx::eventpair token, + fidl::InterfaceRequest incoming_services, + fuchsia::sys::ServiceProviderHandle 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; + + 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 component_controller_; + fuchsia::io::DirectoryPtr directory_ptr_; + fuchsia::io::NodePtr cloned_directory_ptr_; + fidl::InterfaceRequest directory_request_; + std::unique_ptr outgoing_dir_; + std::shared_ptr svc_; + std::shared_ptr runner_incoming_services_; + fidl::BindingSet shells_bindings_; + + fml::RefPtr isolate_snapshot_; + std::set> shell_holders_; + std::pair last_return_code_; + std::vector dart_entrypoint_args_; + fml::WeakPtrFactory weak_factory_; // Must be the last member. + FML_DISALLOW_COPY_AND_ASSIGN(ComponentV1); +}; + +} // namespace flutter_runner + +#endif // FLUTTER_SHELL_PLATFORM_FUCHSIA_COMPONENT_V1_H_ diff --git a/shell/platform/fuchsia/flutter/component_v1_unittest.cc b/shell/platform/fuchsia/flutter/component_v1_unittest.cc new file mode 100644 index 0000000000000..c9abaccdc6e65 --- /dev/null +++ b/shell/platform/fuchsia/flutter/component_v1_unittest.cc @@ -0,0 +1,64 @@ +// 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_v1.h" + +#include +#include + +namespace flutter_runner { +namespace { + +TEST(ComponentV1, ParseProgramMetadata) { + // The ProgramMetadata field may be null. We should parse this as if no + // fields were specified. + ProgramMetadata result = ComponentV1::ParseProgramMetadata(nullptr); + + EXPECT_EQ(result.data_path, ""); + EXPECT_EQ(result.assets_path, ""); + EXPECT_EQ(result.old_gen_heap_size, std::nullopt); + + // The ProgramMetadata field may be empty. Treat this the same as null. + fidl::VectorPtr program_metadata(size_t{0}); + result = ComponentV1::ParseProgramMetadata(program_metadata); + + EXPECT_EQ(result.data_path, ""); + EXPECT_EQ(result.assets_path, ""); + EXPECT_EQ(result.old_gen_heap_size, std::nullopt); + + // The assets_path defaults to the "data" value if unspecified. + program_metadata = {{"data", "foobar"}}; + result = ComponentV1::ParseProgramMetadata(program_metadata); + + EXPECT_EQ(result.data_path, "pkg/foobar"); + EXPECT_EQ(result.assets_path, "pkg/foobar"); + EXPECT_EQ(result.old_gen_heap_size, std::nullopt); + + // Invalid keys are ignored. + program_metadata = {{"not_data", "foo"}, {"data", "bar"}, {"assets", "baz"}}; + result = ComponentV1::ParseProgramMetadata(program_metadata); + + EXPECT_EQ(result.data_path, "pkg/bar"); + EXPECT_EQ(result.assets_path, "pkg/baz"); + EXPECT_EQ(result.old_gen_heap_size, std::nullopt); + + // The old_gen_heap_size can be specified. + program_metadata = {{"old_gen_heap_size", "100"}}; + result = ComponentV1::ParseProgramMetadata(program_metadata); + + EXPECT_EQ(result.data_path, ""); + EXPECT_EQ(result.assets_path, ""); + EXPECT_EQ(result.old_gen_heap_size, 100); + + // Invalid old_gen_heap_sizes should be ignored. + program_metadata = {{"old_gen_heap_size", "asdf100"}}; + result = ComponentV1::ParseProgramMetadata(program_metadata); + + EXPECT_EQ(result.data_path, ""); + EXPECT_EQ(result.assets_path, ""); + EXPECT_EQ(result.old_gen_heap_size, std::nullopt); +} + +} // anonymous namespace +} // namespace flutter_runner diff --git a/shell/platform/fuchsia/flutter/meta/aot_product_runtime b/shell/platform/fuchsia/flutter/meta/aot_product_runtime new file mode 100644 index 0000000000000..244bbc8d9722d --- /dev/null +++ b/shell/platform/fuchsia/flutter/meta/aot_product_runtime @@ -0,0 +1,3 @@ +{ + "runner": "fuchsia-pkg://fuchsia.com/flutter_aot_product_runner#meta/flutter_aot_product_runner.cmx" +} diff --git a/shell/platform/fuchsia/flutter/meta/aot_runtime b/shell/platform/fuchsia/flutter/meta/aot_runtime new file mode 100644 index 0000000000000..8e1b724662f0c --- /dev/null +++ b/shell/platform/fuchsia/flutter/meta/aot_runtime @@ -0,0 +1,3 @@ +{ + "runner": "fuchsia-pkg://fuchsia.com/flutter_aot_runner#meta/flutter_aot_runner.cmx" +} \ No newline at end of file diff --git a/shell/platform/fuchsia/flutter/meta/flutter_aot_product_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_aot_product_runner.cmx new file mode 100644 index 0000000000000..ec1e1975c6e58 --- /dev/null +++ b/shell/platform/fuchsia/flutter/meta/flutter_aot_product_runner.cmx @@ -0,0 +1,33 @@ +{ + "program": { + "data": "data" + }, + "sandbox": { + "features": [ + "config-data", + "isolated-cache-storage", + "root-ssl-certificates", + "vulkan" + ], + "services": [ + "fuchsia.accessibility.semantics.SemanticsManager", + "fuchsia.device.NameProvider", + "fuchsia.feedback.CrashReporter", + "fuchsia.fonts.Provider", + "fuchsia.intl.PropertyProvider", + "fuchsia.logger.LogSink", + "fuchsia.memorypressure.Provider", + "fuchsia.net.name.Lookup", + "fuchsia.posix.socket.Provider", + "fuchsia.sysmem.Allocator", + "fuchsia.tracing.provider.Registry", + "fuchsia.ui.composition.Allocator", + "fuchsia.ui.composition.Flatland", + "fuchsia.ui.input.ImeService", + "fuchsia.ui.input3.Keyboard", + "fuchsia.ui.scenic.Scenic", + "fuchsia.ui.pointerinjector.Registry", + "fuchsia.vulkan.loader.Loader" + ] + } +} diff --git a/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx new file mode 100644 index 0000000000000..ec1e1975c6e58 --- /dev/null +++ b/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx @@ -0,0 +1,33 @@ +{ + "program": { + "data": "data" + }, + "sandbox": { + "features": [ + "config-data", + "isolated-cache-storage", + "root-ssl-certificates", + "vulkan" + ], + "services": [ + "fuchsia.accessibility.semantics.SemanticsManager", + "fuchsia.device.NameProvider", + "fuchsia.feedback.CrashReporter", + "fuchsia.fonts.Provider", + "fuchsia.intl.PropertyProvider", + "fuchsia.logger.LogSink", + "fuchsia.memorypressure.Provider", + "fuchsia.net.name.Lookup", + "fuchsia.posix.socket.Provider", + "fuchsia.sysmem.Allocator", + "fuchsia.tracing.provider.Registry", + "fuchsia.ui.composition.Allocator", + "fuchsia.ui.composition.Flatland", + "fuchsia.ui.input.ImeService", + "fuchsia.ui.input3.Keyboard", + "fuchsia.ui.scenic.Scenic", + "fuchsia.ui.pointerinjector.Registry", + "fuchsia.vulkan.loader.Loader" + ] + } +} diff --git a/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx new file mode 100644 index 0000000000000..b0a626ca1b065 --- /dev/null +++ b/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx @@ -0,0 +1,34 @@ +{ + "program": { + "data": "data" + }, + "sandbox": { + "features": [ + "config-data", + "deprecated-ambient-replace-as-executable", + "isolated-cache-storage", + "root-ssl-certificates", + "vulkan" + ], + "services": [ + "fuchsia.accessibility.semantics.SemanticsManager", + "fuchsia.device.NameProvider", + "fuchsia.feedback.CrashReporter", + "fuchsia.fonts.Provider", + "fuchsia.intl.PropertyProvider", + "fuchsia.logger.LogSink", + "fuchsia.memorypressure.Provider", + "fuchsia.net.name.Lookup", + "fuchsia.posix.socket.Provider", + "fuchsia.sysmem.Allocator", + "fuchsia.tracing.provider.Registry", + "fuchsia.ui.composition.Allocator", + "fuchsia.ui.composition.Flatland", + "fuchsia.ui.input.ImeService", + "fuchsia.ui.input3.Keyboard", + "fuchsia.ui.scenic.Scenic", + "fuchsia.ui.pointerinjector.Registry", + "fuchsia.vulkan.loader.Loader" + ] + } +} diff --git a/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx new file mode 100644 index 0000000000000..b0a626ca1b065 --- /dev/null +++ b/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx @@ -0,0 +1,34 @@ +{ + "program": { + "data": "data" + }, + "sandbox": { + "features": [ + "config-data", + "deprecated-ambient-replace-as-executable", + "isolated-cache-storage", + "root-ssl-certificates", + "vulkan" + ], + "services": [ + "fuchsia.accessibility.semantics.SemanticsManager", + "fuchsia.device.NameProvider", + "fuchsia.feedback.CrashReporter", + "fuchsia.fonts.Provider", + "fuchsia.intl.PropertyProvider", + "fuchsia.logger.LogSink", + "fuchsia.memorypressure.Provider", + "fuchsia.net.name.Lookup", + "fuchsia.posix.socket.Provider", + "fuchsia.sysmem.Allocator", + "fuchsia.tracing.provider.Registry", + "fuchsia.ui.composition.Allocator", + "fuchsia.ui.composition.Flatland", + "fuchsia.ui.input.ImeService", + "fuchsia.ui.input3.Keyboard", + "fuchsia.ui.scenic.Scenic", + "fuchsia.ui.pointerinjector.Registry", + "fuchsia.vulkan.loader.Loader" + ] + } +} diff --git a/shell/platform/fuchsia/flutter/meta/jit_product_runtime b/shell/platform/fuchsia/flutter/meta/jit_product_runtime new file mode 100644 index 0000000000000..03707e7721409 --- /dev/null +++ b/shell/platform/fuchsia/flutter/meta/jit_product_runtime @@ -0,0 +1,3 @@ +{ + "runner": "fuchsia-pkg://fuchsia.com/flutter_jit_product_runner#meta/flutter_jit_product_runner.cmx" +} diff --git a/shell/platform/fuchsia/flutter/meta/jit_runtime b/shell/platform/fuchsia/flutter/meta/jit_runtime new file mode 100644 index 0000000000000..d23fb95a7279b --- /dev/null +++ b/shell/platform/fuchsia/flutter/meta/jit_runtime @@ -0,0 +1,3 @@ +{ + "runner": "fuchsia-pkg://fuchsia.com/flutter_jit_runner#meta/flutter_jit_runner.cmx" +} \ No newline at end of file diff --git a/shell/platform/fuchsia/flutter/runner.cc b/shell/platform/fuchsia/flutter/runner.cc index e49c6890f872f..97cb8dda9b8b6 100644 --- a/shell/platform/fuchsia/flutter/runner.cc +++ b/shell/platform/fuchsia/flutter/runner.cc @@ -187,6 +187,8 @@ Runner::Runner(fml::RefPtr task_runner, SetThreadName("io.flutter.runner.main"); + context_->outgoing()->AddPublicService( + std::bind(&Runner::RegisterComponentV1, this, std::placeholders::_1)); context_->outgoing() ->AddPublicService( std::bind(&Runner::RegisterComponentV2, this, std::placeholders::_1)); @@ -212,6 +214,82 @@ Runner::~Runner() { #endif // !defined(DART_PRODUCT) } +// CF v1 lifecycle methods. + +void Runner::RegisterComponentV1( + fidl::InterfaceRequest request) { + active_components_v1_bindings_.AddBinding(this, std::move(request)); +} + +void Runner::StartComponent( + fuchsia::sys::Package package, + fuchsia::sys::StartupInfo startup_info, + fidl::InterfaceRequest controller) { + // TRACE_DURATION currently requires that the string data does not change + // in the traced scope. Since |package| gets moved in the Component::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. + std::string url_copy = package.resolved_url; + TRACE_EVENT1("flutter", "StartComponent", "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. + ComponentV1::TerminationCallback termination_callback = + [component_runner = this](const ComponentV1* component) { + component_runner->task_runner_->PostTask( + [component_runner, component]() { + component_runner->OnComponentV1Terminate(component); + }); + }; + + ActiveComponentV1 active_component = ComponentV1::Create( + std::move(termination_callback), // termination callback + std::move(package), // component package + std::move(startup_info), // startup info + context_->svc(), // runner incoming services + std::move(controller) // controller request + ); + + auto key = active_component.component.get(); + active_components_v1_[key] = std::move(active_component); +} + +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; + } + ActiveComponentV1& active_component = app->second; + + // Grab the items out of the entry because we will have to rethread the + // destruction. + std::unique_ptr component_to_destroy = + std::move(active_component.component); + std::unique_ptr component_thread = + std::move(active_component.platform_thread); + + // Delete the entry. + 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( @@ -331,6 +409,7 @@ void Runner::SetupTraceObserver() { latch.Wait(); } }; + write_profile_trace_for_components(runner->active_components_v1_); write_profile_trace_for_components(runner->active_components_v2_); Dart_StopProfiling(); diff --git a/shell/platform/fuchsia/flutter/runner.h b/shell/platform/fuchsia/flutter/runner.h index 4c077b6e81819..2d5c71146f260 100644 --- a/shell/platform/fuchsia/flutter/runner.h +++ b/shell/platform/fuchsia/flutter/runner.h @@ -15,6 +15,7 @@ #include #include +#include "component_v1.h" #include "component_v2.h" #include "flutter/fml/macros.h" #include "fml/memory/ref_ptr.h" @@ -24,13 +25,14 @@ namespace flutter_runner { -/// Publishes the CF v2 runner service. +/// 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::component::runner::ComponentRunner /* CF v2 */ { + : public fuchsia::sys::Runner /* CF v1 */, + public fuchsia::component::runner::ComponentRunner /* CF v2 */ { public: // Does not take ownership of context. Runner(fml::RefPtr task_runner, @@ -39,6 +41,25 @@ class Runner final ~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 + controller) override; + + /// Registers a new CF v1 component with this runner, binding the component + /// to this runner. + void RegisterComponentV1( + fidl::InterfaceRequest request); + + /// 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| @@ -76,6 +97,11 @@ class Runner final sys::ComponentContext* context_; + // CF v1 component state. + fidl::BindingSet active_components_v1_bindings_; + std::unordered_map + active_components_v1_; + // CF v2 component state. /// The components that are currently bound to this runner. diff --git a/tools/fuchsia/flutter/flutter_build_config.gni b/tools/fuchsia/flutter/flutter_build_config.gni index a322a7e25587e..b7c033a46fc2a 100644 --- a/tools/fuchsia/flutter/flutter_build_config.gni +++ b/tools/fuchsia/flutter/flutter_build_config.gni @@ -12,6 +12,7 @@ import("//flutter/common/config.gni") # Builds the component in a non-product JIT build. This will # launch the vm service in the runner. flutter_debug_build_cfg = { + runtime_meta = "//flutter/shell/platform/fuchsia/flutter/meta/jit_runtime" runner_dep = "//flutter/shell/platform/fuchsia/flutter:flutter_jit_runner" platform_name = "flutter_runner" is_aot = false @@ -24,6 +25,7 @@ flutter_debug_build_cfg = { # This configuration is not compatible with a --release build since the # profile aot runner is built without asserts. flutter_aot_debug_build_cfg = { + runtime_meta = "//flutter/shell/platform/fuchsia/flutter/meta/aot_runtime" runner_dep = "//flutter/shell/platform/fuchsia/flutter:flutter_aot_runner" platform_name = "flutter_runner" is_aot = true @@ -34,6 +36,9 @@ flutter_aot_debug_build_cfg = { # Builds the component in a non-product AOT build. This will # launch the vm service in the runner. flutter_profile_build_cfg = { + runtime_meta = + "//flutter/shell/platform/fuchsia/flutter/meta/aot_runtime" # profile + # runner runner_dep = "//flutter/shell/platform/fuchsia/flutter:flutter_aot_runner" platform_name = "flutter_runner" is_aot = true @@ -44,6 +49,8 @@ flutter_profile_build_cfg = { # Builds the component in a product JIT build. This will # not launch the vm service in the runner. flutter_jit_release_build_cfg = { + runtime_meta = + "//flutter/shell/platform/fuchsia/flutter/meta/jit_product_runtime" runner_dep = "//flutter/shell/platform/fuchsia/flutter:flutter_jit_product_runner" platform_name = "flutter_runner" @@ -55,6 +62,8 @@ flutter_jit_release_build_cfg = { # Builds the component in a product AOT build. This will # not launch the vm service in the runner. flutter_release_build_cfg = { + runtime_meta = + "//flutter/shell/platform/fuchsia/flutter/meta/aot_product_runtime" runner_dep = "//flutter/shell/platform/fuchsia/flutter:flutter_aot_product_runner" platform_name = "flutter_runner"