| // 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 "flutter/shell/common/engine.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "flutter/common/settings.h" |
| #include "flutter/fml/eintr_wrapper.h" |
| #include "flutter/fml/file.h" |
| #include "flutter/fml/make_copyable.h" |
| #include "flutter/fml/paths.h" |
| #include "flutter/fml/trace_event.h" |
| #include "flutter/fml/unique_fd.h" |
| #include "flutter/lib/snapshot/snapshot.h" |
| #include "flutter/lib/ui/text/font_collection.h" |
| #include "flutter/shell/common/animator.h" |
| #include "flutter/shell/common/platform_view.h" |
| #include "flutter/shell/common/shell.h" |
| #include "rapidjson/document.h" |
| #include "third_party/dart/runtime/include/dart_tools_api.h" |
| #include "third_party/skia/include/core/SkCanvas.h" |
| #include "third_party/skia/include/core/SkPictureRecorder.h" |
| |
| namespace flutter { |
| |
| static constexpr char kAssetChannel[] = "flutter/assets"; |
| static constexpr char kLifecycleChannel[] = "flutter/lifecycle"; |
| static constexpr char kNavigationChannel[] = "flutter/navigation"; |
| static constexpr char kLocalizationChannel[] = "flutter/localization"; |
| static constexpr char kSettingsChannel[] = "flutter/settings"; |
| static constexpr char kIsolateChannel[] = "flutter/isolate"; |
| |
| Engine::Engine( |
| Delegate& delegate, |
| const PointerDataDispatcherMaker& dispatcher_maker, |
| std::shared_ptr<fml::ConcurrentTaskRunner> image_decoder_task_runner, |
| TaskRunners task_runners, |
| Settings settings, |
| std::unique_ptr<Animator> animator, |
| fml::WeakPtr<IOManager> io_manager, |
| const std::shared_ptr<FontCollection>& font_collection, |
| std::unique_ptr<RuntimeController> runtime_controller) |
| : delegate_(delegate), |
| settings_(std::move(settings)), |
| animator_(std::move(animator)), |
| runtime_controller_(std::move(runtime_controller)), |
| activity_running_(true), |
| have_surface_(false), |
| font_collection_(font_collection), |
| image_decoder_(task_runners, image_decoder_task_runner, io_manager), |
| task_runners_(std::move(task_runners)), |
| weak_factory_(this) { |
| pointer_data_dispatcher_ = dispatcher_maker(*this); |
| } |
| |
| Engine::Engine(Delegate& delegate, |
| const PointerDataDispatcherMaker& dispatcher_maker, |
| DartVM& vm, |
| fml::RefPtr<const DartSnapshot> isolate_snapshot, |
| TaskRunners task_runners, |
| const PlatformData& platform_data, |
| Settings settings, |
| std::unique_ptr<Animator> animator, |
| fml::WeakPtr<IOManager> io_manager, |
| fml::RefPtr<SkiaUnrefQueue> unref_queue, |
| fml::WeakPtr<SnapshotDelegate> snapshot_delegate, |
| std::shared_ptr<VolatilePathTracker> volatile_path_tracker) |
| : Engine(delegate, |
| dispatcher_maker, |
| vm.GetConcurrentWorkerTaskRunner(), |
| task_runners, |
| settings, |
| std::move(animator), |
| io_manager, |
| std::make_shared<FontCollection>(), |
| nullptr) { |
| runtime_controller_ = std::make_unique<RuntimeController>( |
| *this, // runtime delegate |
| &vm, // VM |
| std::move(isolate_snapshot), // isolate snapshot |
| task_runners_, // task runners |
| std::move(snapshot_delegate), // snapshot delegate |
| GetWeakPtr(), // hint freed delegate |
| std::move(io_manager), // io manager |
| std::move(unref_queue), // Skia unref queue |
| image_decoder_.GetWeakPtr(), // image decoder |
| settings_.advisory_script_uri, // advisory script uri |
| settings_.advisory_script_entrypoint, // advisory script entrypoint |
| settings_.idle_notification_callback, // idle notification callback |
| platform_data, // platform data |
| settings_.isolate_create_callback, // isolate create callback |
| settings_.isolate_shutdown_callback, // isolate shutdown callback |
| settings_.persistent_isolate_data, // persistent isolate data |
| std::move(volatile_path_tracker) // volatile path tracker |
| ); |
| } |
| |
| std::unique_ptr<Engine> Engine::Spawn( |
| Delegate& delegate, |
| const PointerDataDispatcherMaker& dispatcher_maker, |
| Settings settings, |
| std::unique_ptr<Animator> animator) const { |
| auto result = std::make_unique<Engine>( |
| /*delegate=*/delegate, |
| /*dispatcher_maker=*/dispatcher_maker, |
| /*image_decoder_task_runner=*/ |
| runtime_controller_->GetDartVM()->GetConcurrentWorkerTaskRunner(), |
| /*task_runners=*/task_runners_, |
| /*settings=*/settings, |
| /*animator=*/std::move(animator), |
| /*io_manager=*/runtime_controller_->GetIOManager(), |
| /*font_collection=*/font_collection_, |
| /*runtime_controller=*/nullptr); |
| result->runtime_controller_ = runtime_controller_->Spawn( |
| *result, // runtime delegate |
| settings_.advisory_script_uri, // advisory script uri |
| settings_.advisory_script_entrypoint, // advisory script entrypoint |
| settings_.idle_notification_callback, // idle notification callback |
| settings_.isolate_create_callback, // isolate create callback |
| settings_.isolate_shutdown_callback, // isolate shutdown callback |
| settings_.persistent_isolate_data // persistent isolate data |
| ); |
| return result; |
| } |
| |
| Engine::~Engine() = default; |
| |
| fml::WeakPtr<Engine> Engine::GetWeakPtr() const { |
| return weak_factory_.GetWeakPtr(); |
| } |
| |
| void Engine::SetupDefaultFontManager() { |
| TRACE_EVENT0("flutter", "Engine::SetupDefaultFontManager"); |
| font_collection_->SetupDefaultFontManager(); |
| } |
| |
| std::shared_ptr<AssetManager> Engine::GetAssetManager() { |
| return asset_manager_; |
| } |
| |
| bool Engine::UpdateAssetManager( |
| std::shared_ptr<AssetManager> new_asset_manager) { |
| if (asset_manager_ == new_asset_manager) { |
| return false; |
| } |
| |
| asset_manager_ = new_asset_manager; |
| |
| if (!asset_manager_) { |
| return false; |
| } |
| |
| // Using libTXT as the text engine. |
| font_collection_->RegisterFonts(asset_manager_); |
| |
| if (settings_.use_test_fonts) { |
| font_collection_->RegisterTestFonts(); |
| } |
| |
| return true; |
| } |
| |
| bool Engine::Restart(RunConfiguration configuration) { |
| TRACE_EVENT0("flutter", "Engine::Restart"); |
| if (!configuration.IsValid()) { |
| FML_LOG(ERROR) << "Engine run configuration was invalid."; |
| return false; |
| } |
| delegate_.OnPreEngineRestart(); |
| runtime_controller_ = runtime_controller_->Clone(); |
| UpdateAssetManager(nullptr); |
| return Run(std::move(configuration)) == Engine::RunStatus::Success; |
| } |
| |
| Engine::RunStatus Engine::Run(RunConfiguration configuration) { |
| if (!configuration.IsValid()) { |
| FML_LOG(ERROR) << "Engine run configuration was invalid."; |
| return RunStatus::Failure; |
| } |
| |
| last_entry_point_ = configuration.GetEntrypoint(); |
| last_entry_point_library_ = configuration.GetEntrypointLibrary(); |
| |
| UpdateAssetManager(configuration.GetAssetManager()); |
| |
| if (runtime_controller_->IsRootIsolateRunning()) { |
| return RunStatus::FailureAlreadyRunning; |
| } |
| |
| if (!runtime_controller_->LaunchRootIsolate( |
| settings_, // |
| configuration.GetEntrypoint(), // |
| configuration.GetEntrypointLibrary(), // |
| configuration.TakeIsolateConfiguration()) // |
| ) { |
| return RunStatus::Failure; |
| } |
| |
| auto service_id = runtime_controller_->GetRootIsolateServiceID(); |
| if (service_id.has_value()) { |
| fml::RefPtr<PlatformMessage> service_id_message = |
| fml::MakeRefCounted<flutter::PlatformMessage>( |
| kIsolateChannel, |
| std::vector<uint8_t>(service_id.value().begin(), |
| service_id.value().end()), |
| nullptr); |
| HandlePlatformMessage(service_id_message); |
| } |
| |
| return Engine::RunStatus::Success; |
| } |
| |
| void Engine::BeginFrame(fml::TimePoint frame_time) { |
| TRACE_EVENT0("flutter", "Engine::BeginFrame"); |
| runtime_controller_->BeginFrame(frame_time); |
| } |
| |
| void Engine::ReportTimings(std::vector<int64_t> timings) { |
| TRACE_EVENT0("flutter", "Engine::ReportTimings"); |
| runtime_controller_->ReportTimings(std::move(timings)); |
| } |
| |
| void Engine::HintFreed(size_t size) { |
| hint_freed_bytes_since_last_idle_ += size; |
| } |
| |
| void Engine::NotifyIdle(int64_t deadline) { |
| auto trace_event = std::to_string(deadline - Dart_TimelineGetMicros()); |
| TRACE_EVENT1("flutter", "Engine::NotifyIdle", "deadline_now_delta", |
| trace_event.c_str()); |
| runtime_controller_->NotifyIdle(deadline, hint_freed_bytes_since_last_idle_); |
| hint_freed_bytes_since_last_idle_ = 0; |
| } |
| |
| std::optional<uint32_t> Engine::GetUIIsolateReturnCode() { |
| return runtime_controller_->GetRootIsolateReturnCode(); |
| } |
| |
| Dart_Port Engine::GetUIIsolateMainPort() { |
| return runtime_controller_->GetMainPort(); |
| } |
| |
| std::string Engine::GetUIIsolateName() { |
| return runtime_controller_->GetIsolateName(); |
| } |
| |
| bool Engine::UIIsolateHasLivePorts() { |
| return runtime_controller_->HasLivePorts(); |
| } |
| |
| tonic::DartErrorHandleType Engine::GetUIIsolateLastError() { |
| return runtime_controller_->GetLastError(); |
| } |
| |
| void Engine::OnOutputSurfaceCreated() { |
| have_surface_ = true; |
| StartAnimatorIfPossible(); |
| ScheduleFrame(); |
| } |
| |
| void Engine::OnOutputSurfaceDestroyed() { |
| have_surface_ = false; |
| StopAnimator(); |
| } |
| |
| void Engine::SetViewportMetrics(const ViewportMetrics& metrics) { |
| bool dimensions_changed = |
| viewport_metrics_.physical_height != metrics.physical_height || |
| viewport_metrics_.physical_width != metrics.physical_width || |
| viewport_metrics_.device_pixel_ratio != metrics.device_pixel_ratio; |
| viewport_metrics_ = metrics; |
| runtime_controller_->SetViewportMetrics(viewport_metrics_); |
| if (animator_) { |
| if (dimensions_changed) { |
| animator_->SetDimensionChangePending(); |
| } |
| if (have_surface_) { |
| ScheduleFrame(); |
| } |
| } |
| } |
| |
| void Engine::DispatchPlatformMessage(fml::RefPtr<PlatformMessage> message) { |
| std::string channel = message->channel(); |
| if (channel == kLifecycleChannel) { |
| if (HandleLifecyclePlatformMessage(message.get())) { |
| return; |
| } |
| } else if (channel == kLocalizationChannel) { |
| if (HandleLocalizationPlatformMessage(message.get())) { |
| return; |
| } |
| } else if (channel == kSettingsChannel) { |
| HandleSettingsPlatformMessage(message.get()); |
| return; |
| } else if (!runtime_controller_->IsRootIsolateRunning() && |
| channel == kNavigationChannel) { |
| // If there's no runtime_, we may still need to set the initial route. |
| HandleNavigationPlatformMessage(std::move(message)); |
| return; |
| } |
| |
| if (runtime_controller_->IsRootIsolateRunning() && |
| runtime_controller_->DispatchPlatformMessage(std::move(message))) { |
| return; |
| } |
| |
| FML_DLOG(WARNING) << "Dropping platform message on channel: " << channel; |
| } |
| |
| bool Engine::HandleLifecyclePlatformMessage(PlatformMessage* message) { |
| const auto& data = message->data(); |
| std::string state(reinterpret_cast<const char*>(data.data()), data.size()); |
| if (state == "AppLifecycleState.paused" || |
| state == "AppLifecycleState.detached") { |
| activity_running_ = false; |
| StopAnimator(); |
| } else if (state == "AppLifecycleState.resumed" || |
| state == "AppLifecycleState.inactive") { |
| activity_running_ = true; |
| StartAnimatorIfPossible(); |
| } |
| |
| // Always schedule a frame when the app does become active as per API |
| // recommendation |
| // https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive?language=objc |
| if (state == "AppLifecycleState.resumed" && have_surface_) { |
| ScheduleFrame(); |
| } |
| runtime_controller_->SetLifecycleState(state); |
| // Always forward these messages to the framework by returning false. |
| return false; |
| } |
| |
| bool Engine::HandleNavigationPlatformMessage( |
| fml::RefPtr<PlatformMessage> message) { |
| const auto& data = message->data(); |
| |
| rapidjson::Document document; |
| document.Parse(reinterpret_cast<const char*>(data.data()), data.size()); |
| if (document.HasParseError() || !document.IsObject()) { |
| return false; |
| } |
| auto root = document.GetObject(); |
| auto method = root.FindMember("method"); |
| if (method->value != "setInitialRoute") { |
| return false; |
| } |
| auto route = root.FindMember("args"); |
| initial_route_ = std::move(route->value.GetString()); |
| return true; |
| } |
| |
| bool Engine::HandleLocalizationPlatformMessage(PlatformMessage* message) { |
| const auto& data = message->data(); |
| |
| rapidjson::Document document; |
| document.Parse(reinterpret_cast<const char*>(data.data()), data.size()); |
| if (document.HasParseError() || !document.IsObject()) { |
| return false; |
| } |
| auto root = document.GetObject(); |
| auto method = root.FindMember("method"); |
| if (method == root.MemberEnd()) { |
| return false; |
| } |
| const size_t strings_per_locale = 4; |
| if (method->value == "setLocale") { |
| // Decode and pass the list of locale data onwards to dart. |
| auto args = root.FindMember("args"); |
| if (args == root.MemberEnd() || !args->value.IsArray()) { |
| return false; |
| } |
| |
| if (args->value.Size() % strings_per_locale != 0) { |
| return false; |
| } |
| std::vector<std::string> locale_data; |
| for (size_t locale_index = 0; locale_index < args->value.Size(); |
| locale_index += strings_per_locale) { |
| if (!args->value[locale_index].IsString() || |
| !args->value[locale_index + 1].IsString()) { |
| return false; |
| } |
| locale_data.push_back(args->value[locale_index].GetString()); |
| locale_data.push_back(args->value[locale_index + 1].GetString()); |
| locale_data.push_back(args->value[locale_index + 2].GetString()); |
| locale_data.push_back(args->value[locale_index + 3].GetString()); |
| } |
| |
| return runtime_controller_->SetLocales(locale_data); |
| } |
| return false; |
| } |
| |
| void Engine::HandleSettingsPlatformMessage(PlatformMessage* message) { |
| const auto& data = message->data(); |
| std::string jsonData(reinterpret_cast<const char*>(data.data()), data.size()); |
| if (runtime_controller_->SetUserSettingsData(std::move(jsonData)) && |
| have_surface_) { |
| ScheduleFrame(); |
| } |
| } |
| |
| void Engine::DispatchPointerDataPacket( |
| std::unique_ptr<PointerDataPacket> packet, |
| uint64_t trace_flow_id) { |
| TRACE_EVENT0("flutter", "Engine::DispatchPointerDataPacket"); |
| TRACE_FLOW_STEP("flutter", "PointerEvent", trace_flow_id); |
| pointer_data_dispatcher_->DispatchPacket(std::move(packet), trace_flow_id); |
| } |
| |
| void Engine::DispatchKeyDataPacket(std::unique_ptr<KeyDataPacket> packet, |
| KeyDataResponse callback) { |
| TRACE_EVENT0("flutter", "Engine::DispatchKeyDataPacket"); |
| if (runtime_controller_) { |
| runtime_controller_->DispatchKeyDataPacket(*packet, std::move(callback)); |
| } |
| } |
| |
| void Engine::DispatchSemanticsAction(int id, |
| SemanticsAction action, |
| std::vector<uint8_t> args) { |
| runtime_controller_->DispatchSemanticsAction(id, action, std::move(args)); |
| } |
| |
| void Engine::SetSemanticsEnabled(bool enabled) { |
| runtime_controller_->SetSemanticsEnabled(enabled); |
| } |
| |
| void Engine::SetAccessibilityFeatures(int32_t flags) { |
| runtime_controller_->SetAccessibilityFeatures(flags); |
| } |
| |
| void Engine::StopAnimator() { |
| animator_->Stop(); |
| } |
| |
| void Engine::StartAnimatorIfPossible() { |
| if (activity_running_ && have_surface_) { |
| animator_->Start(); |
| } |
| } |
| |
| std::string Engine::DefaultRouteName() { |
| if (!initial_route_.empty()) { |
| return initial_route_; |
| } |
| return "/"; |
| } |
| |
| void Engine::ScheduleFrame(bool regenerate_layer_tree) { |
| animator_->RequestFrame(regenerate_layer_tree); |
| } |
| |
| void Engine::Render(std::unique_ptr<flutter::LayerTree> layer_tree) { |
| if (!layer_tree) { |
| return; |
| } |
| |
| // Ensure frame dimensions are sane. |
| if (layer_tree->frame_size().isEmpty() || |
| layer_tree->device_pixel_ratio() <= 0.0f) { |
| return; |
| } |
| |
| animator_->Render(std::move(layer_tree)); |
| } |
| |
| void Engine::UpdateSemantics(SemanticsNodeUpdates update, |
| CustomAccessibilityActionUpdates actions) { |
| delegate_.OnEngineUpdateSemantics(std::move(update), std::move(actions)); |
| } |
| |
| void Engine::HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) { |
| if (message->channel() == kAssetChannel) { |
| HandleAssetPlatformMessage(std::move(message)); |
| } else { |
| delegate_.OnEngineHandlePlatformMessage(std::move(message)); |
| } |
| } |
| |
| void Engine::OnRootIsolateCreated() { |
| delegate_.OnRootIsolateCreated(); |
| } |
| |
| void Engine::UpdateIsolateDescription(const std::string isolate_name, |
| int64_t isolate_port) { |
| delegate_.UpdateIsolateDescription(isolate_name, isolate_port); |
| } |
| |
| std::unique_ptr<std::vector<std::string>> Engine::ComputePlatformResolvedLocale( |
| const std::vector<std::string>& supported_locale_data) { |
| return delegate_.ComputePlatformResolvedLocale(supported_locale_data); |
| } |
| |
| void Engine::SetNeedsReportTimings(bool needs_reporting) { |
| delegate_.SetNeedsReportTimings(needs_reporting); |
| } |
| |
| FontCollection& Engine::GetFontCollection() { |
| return *font_collection_; |
| } |
| |
| void Engine::DoDispatchPacket(std::unique_ptr<PointerDataPacket> packet, |
| uint64_t trace_flow_id) { |
| animator_->EnqueueTraceFlowId(trace_flow_id); |
| if (runtime_controller_) { |
| runtime_controller_->DispatchPointerDataPacket(*packet); |
| } |
| } |
| |
| void Engine::ScheduleSecondaryVsyncCallback(uintptr_t id, |
| const fml::closure& callback) { |
| animator_->ScheduleSecondaryVsyncCallback(id, callback); |
| } |
| |
| void Engine::HandleAssetPlatformMessage(fml::RefPtr<PlatformMessage> message) { |
| fml::RefPtr<PlatformMessageResponse> response = message->response(); |
| if (!response) { |
| return; |
| } |
| const auto& data = message->data(); |
| std::string asset_name(reinterpret_cast<const char*>(data.data()), |
| data.size()); |
| |
| if (asset_manager_) { |
| std::unique_ptr<fml::Mapping> asset_mapping = |
| asset_manager_->GetAsMapping(asset_name); |
| if (asset_mapping) { |
| response->Complete(std::move(asset_mapping)); |
| return; |
| } |
| } |
| |
| response->CompleteEmpty(); |
| } |
| |
| const std::string& Engine::GetLastEntrypoint() const { |
| return last_entry_point_; |
| } |
| |
| const std::string& Engine::GetLastEntrypointLibrary() const { |
| return last_entry_point_library_; |
| } |
| |
| // |RuntimeDelegate| |
| void Engine::RequestDartDeferredLibrary(intptr_t loading_unit_id) { |
| return delegate_.RequestDartDeferredLibrary(loading_unit_id); |
| } |
| |
| void Engine::LoadDartDeferredLibrary( |
| intptr_t loading_unit_id, |
| std::unique_ptr<const fml::Mapping> snapshot_data, |
| std::unique_ptr<const fml::Mapping> snapshot_instructions) { |
| if (runtime_controller_->IsRootIsolateRunning()) { |
| runtime_controller_->LoadDartDeferredLibrary( |
| loading_unit_id, std::move(snapshot_data), |
| std::move(snapshot_instructions)); |
| } else { |
| LoadDartDeferredLibraryError(loading_unit_id, "No running root isolate.", |
| true); |
| } |
| } |
| |
| void Engine::LoadDartDeferredLibraryError(intptr_t loading_unit_id, |
| const std::string error_message, |
| bool transient) { |
| if (runtime_controller_->IsRootIsolateRunning()) { |
| runtime_controller_->LoadDartDeferredLibraryError(loading_unit_id, |
| error_message, transient); |
| } |
| } |
| |
| } // namespace flutter |