1 | // Copyright 2013 The Flutter Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | #include "flutter/shell/common/engine.h" |
6 | |
7 | #include <memory> |
8 | #include <string> |
9 | #include <utility> |
10 | #include <vector> |
11 | |
12 | #include "flutter/common/settings.h" |
13 | #include "flutter/fml/eintr_wrapper.h" |
14 | #include "flutter/fml/file.h" |
15 | #include "flutter/fml/make_copyable.h" |
16 | #include "flutter/fml/paths.h" |
17 | #include "flutter/fml/trace_event.h" |
18 | #include "flutter/fml/unique_fd.h" |
19 | #include "flutter/lib/snapshot/snapshot.h" |
20 | #include "flutter/lib/ui/text/font_collection.h" |
21 | #include "flutter/shell/common/animator.h" |
22 | #include "flutter/shell/common/platform_view.h" |
23 | #include "flutter/shell/common/shell.h" |
24 | #include "rapidjson/document.h" |
25 | #include "third_party/dart/runtime/include/dart_tools_api.h" |
26 | #include "third_party/skia/include/core/SkCanvas.h" |
27 | #include "third_party/skia/include/core/SkPictureRecorder.h" |
28 | |
29 | namespace flutter { |
30 | |
31 | static constexpr char kAssetChannel[] = "flutter/assets" ; |
32 | static constexpr char kLifecycleChannel[] = "flutter/lifecycle" ; |
33 | static constexpr char kNavigationChannel[] = "flutter/navigation" ; |
34 | static constexpr char kLocalizationChannel[] = "flutter/localization" ; |
35 | static constexpr char kSettingsChannel[] = "flutter/settings" ; |
36 | static constexpr char kIsolateChannel[] = "flutter/isolate" ; |
37 | |
38 | Engine::Engine( |
39 | Delegate& delegate, |
40 | const PointerDataDispatcherMaker& dispatcher_maker, |
41 | std::shared_ptr<fml::ConcurrentTaskRunner> image_decoder_task_runner, |
42 | TaskRunners task_runners, |
43 | Settings settings, |
44 | std::unique_ptr<Animator> animator, |
45 | fml::WeakPtr<IOManager> io_manager, |
46 | std::unique_ptr<RuntimeController> runtime_controller) |
47 | : delegate_(delegate), |
48 | settings_(std::move(settings)), |
49 | animator_(std::move(animator)), |
50 | runtime_controller_(std::move(runtime_controller)), |
51 | activity_running_(true), |
52 | have_surface_(false), |
53 | image_decoder_(task_runners, image_decoder_task_runner, io_manager), |
54 | task_runners_(std::move(task_runners)), |
55 | weak_factory_(this) { |
56 | pointer_data_dispatcher_ = dispatcher_maker(*this); |
57 | } |
58 | |
59 | Engine::Engine(Delegate& delegate, |
60 | const PointerDataDispatcherMaker& dispatcher_maker, |
61 | DartVM& vm, |
62 | fml::RefPtr<const DartSnapshot> isolate_snapshot, |
63 | TaskRunners task_runners, |
64 | const PlatformData platform_data, |
65 | Settings settings, |
66 | std::unique_ptr<Animator> animator, |
67 | fml::WeakPtr<IOManager> io_manager, |
68 | fml::RefPtr<SkiaUnrefQueue> unref_queue, |
69 | fml::WeakPtr<SnapshotDelegate> snapshot_delegate) |
70 | : Engine(delegate, |
71 | dispatcher_maker, |
72 | vm.GetConcurrentWorkerTaskRunner(), |
73 | task_runners, |
74 | settings, |
75 | std::move(animator), |
76 | io_manager, |
77 | nullptr) { |
78 | runtime_controller_ = std::make_unique<RuntimeController>( |
79 | *this, // runtime delegate |
80 | &vm, // VM |
81 | std::move(isolate_snapshot), // isolate snapshot |
82 | task_runners_, // task runners |
83 | std::move(snapshot_delegate), |
84 | std::move(io_manager), // io manager |
85 | std::move(unref_queue), // Skia unref queue |
86 | image_decoder_.GetWeakPtr(), // image decoder |
87 | settings_.advisory_script_uri, // advisory script uri |
88 | settings_.advisory_script_entrypoint, // advisory script entrypoint |
89 | settings_.idle_notification_callback, // idle notification callback |
90 | platform_data, // platform data |
91 | settings_.isolate_create_callback, // isolate create callback |
92 | settings_.isolate_shutdown_callback, // isolate shutdown callback |
93 | settings_.persistent_isolate_data // persistent isolate data |
94 | ); |
95 | } |
96 | |
97 | Engine::~Engine() = default; |
98 | |
99 | float Engine::GetDisplayRefreshRate() const { |
100 | return animator_->GetDisplayRefreshRate(); |
101 | } |
102 | |
103 | fml::WeakPtr<Engine> Engine::GetWeakPtr() const { |
104 | return weak_factory_.GetWeakPtr(); |
105 | } |
106 | |
107 | void Engine::SetupDefaultFontManager() { |
108 | TRACE_EVENT0("flutter" , "Engine::SetupDefaultFontManager" ); |
109 | font_collection_.SetupDefaultFontManager(); |
110 | } |
111 | |
112 | bool Engine::UpdateAssetManager( |
113 | std::shared_ptr<AssetManager> new_asset_manager) { |
114 | if (asset_manager_ == new_asset_manager) { |
115 | return false; |
116 | } |
117 | |
118 | asset_manager_ = new_asset_manager; |
119 | |
120 | if (!asset_manager_) { |
121 | return false; |
122 | } |
123 | |
124 | // Using libTXT as the text engine. |
125 | font_collection_.RegisterFonts(asset_manager_); |
126 | |
127 | if (settings_.use_test_fonts) { |
128 | font_collection_.RegisterTestFonts(); |
129 | } |
130 | |
131 | return true; |
132 | } |
133 | |
134 | bool Engine::Restart(RunConfiguration configuration) { |
135 | TRACE_EVENT0("flutter" , "Engine::Restart" ); |
136 | if (!configuration.IsValid()) { |
137 | FML_LOG(ERROR) << "Engine run configuration was invalid." ; |
138 | return false; |
139 | } |
140 | delegate_.OnPreEngineRestart(); |
141 | runtime_controller_ = runtime_controller_->Clone(); |
142 | UpdateAssetManager(nullptr); |
143 | return Run(std::move(configuration)) == Engine::RunStatus::Success; |
144 | } |
145 | |
146 | Engine::RunStatus Engine::Run(RunConfiguration configuration) { |
147 | if (!configuration.IsValid()) { |
148 | FML_LOG(ERROR) << "Engine run configuration was invalid." ; |
149 | return RunStatus::Failure; |
150 | } |
151 | |
152 | last_entry_point_ = configuration.GetEntrypoint(); |
153 | last_entry_point_library_ = configuration.GetEntrypointLibrary(); |
154 | |
155 | auto isolate_launch_status = |
156 | PrepareAndLaunchIsolate(std::move(configuration)); |
157 | if (isolate_launch_status == Engine::RunStatus::Failure) { |
158 | FML_LOG(ERROR) << "Engine not prepare and launch isolate." ; |
159 | return isolate_launch_status; |
160 | } else if (isolate_launch_status == |
161 | Engine::RunStatus::FailureAlreadyRunning) { |
162 | return isolate_launch_status; |
163 | } |
164 | |
165 | std::shared_ptr<DartIsolate> isolate = |
166 | runtime_controller_->GetRootIsolate().lock(); |
167 | |
168 | bool isolate_running = |
169 | isolate && isolate->GetPhase() == DartIsolate::Phase::Running; |
170 | |
171 | if (isolate_running) { |
172 | tonic::DartState::Scope scope(isolate.get()); |
173 | |
174 | if (settings_.root_isolate_create_callback) { |
175 | settings_.root_isolate_create_callback(); |
176 | } |
177 | |
178 | if (settings_.root_isolate_shutdown_callback) { |
179 | isolate->AddIsolateShutdownCallback( |
180 | settings_.root_isolate_shutdown_callback); |
181 | } |
182 | |
183 | std::string service_id = isolate->GetServiceId(); |
184 | fml::RefPtr<PlatformMessage> service_id_message = |
185 | fml::MakeRefCounted<flutter::PlatformMessage>( |
186 | kIsolateChannel, |
187 | std::vector<uint8_t>(service_id.begin(), service_id.end()), |
188 | nullptr); |
189 | HandlePlatformMessage(service_id_message); |
190 | } |
191 | |
192 | return isolate_running ? Engine::RunStatus::Success |
193 | : Engine::RunStatus::Failure; |
194 | } |
195 | |
196 | Engine::RunStatus Engine::PrepareAndLaunchIsolate( |
197 | RunConfiguration configuration) { |
198 | TRACE_EVENT0("flutter" , "Engine::PrepareAndLaunchIsolate" ); |
199 | |
200 | UpdateAssetManager(configuration.GetAssetManager()); |
201 | |
202 | auto isolate_configuration = configuration.TakeIsolateConfiguration(); |
203 | |
204 | std::shared_ptr<DartIsolate> isolate = |
205 | runtime_controller_->GetRootIsolate().lock(); |
206 | |
207 | if (!isolate) { |
208 | return RunStatus::Failure; |
209 | } |
210 | |
211 | // This can happen on iOS after a plugin shows a native window and returns to |
212 | // the Flutter ViewController. |
213 | if (isolate->GetPhase() == DartIsolate::Phase::Running) { |
214 | FML_DLOG(WARNING) << "Isolate was already running!" ; |
215 | return RunStatus::FailureAlreadyRunning; |
216 | } |
217 | |
218 | if (!isolate_configuration->PrepareIsolate(*isolate)) { |
219 | FML_LOG(ERROR) << "Could not prepare to run the isolate." ; |
220 | return RunStatus::Failure; |
221 | } |
222 | |
223 | if (configuration.GetEntrypointLibrary().empty()) { |
224 | if (!isolate->Run(configuration.GetEntrypoint(), |
225 | settings_.dart_entrypoint_args)) { |
226 | FML_LOG(ERROR) << "Could not run the isolate." ; |
227 | return RunStatus::Failure; |
228 | } |
229 | } else { |
230 | if (!isolate->RunFromLibrary(configuration.GetEntrypointLibrary(), |
231 | configuration.GetEntrypoint(), |
232 | settings_.dart_entrypoint_args)) { |
233 | FML_LOG(ERROR) << "Could not run the isolate." ; |
234 | return RunStatus::Failure; |
235 | } |
236 | } |
237 | |
238 | return RunStatus::Success; |
239 | } |
240 | |
241 | void Engine::BeginFrame(fml::TimePoint frame_time) { |
242 | TRACE_EVENT0("flutter" , "Engine::BeginFrame" ); |
243 | runtime_controller_->BeginFrame(frame_time); |
244 | } |
245 | |
246 | void Engine::ReportTimings(std::vector<int64_t> timings) { |
247 | TRACE_EVENT0("flutter" , "Engine::ReportTimings" ); |
248 | runtime_controller_->ReportTimings(std::move(timings)); |
249 | } |
250 | |
251 | void Engine::NotifyIdle(int64_t deadline) { |
252 | auto trace_event = std::to_string(deadline - Dart_TimelineGetMicros()); |
253 | TRACE_EVENT1("flutter" , "Engine::NotifyIdle" , "deadline_now_delta" , |
254 | trace_event.c_str()); |
255 | runtime_controller_->NotifyIdle(deadline); |
256 | } |
257 | |
258 | std::pair<bool, uint32_t> Engine::GetUIIsolateReturnCode() { |
259 | return runtime_controller_->GetRootIsolateReturnCode(); |
260 | } |
261 | |
262 | Dart_Port Engine::GetUIIsolateMainPort() { |
263 | return runtime_controller_->GetMainPort(); |
264 | } |
265 | |
266 | std::string Engine::GetUIIsolateName() { |
267 | return runtime_controller_->GetIsolateName(); |
268 | } |
269 | |
270 | bool Engine::UIIsolateHasLivePorts() { |
271 | return runtime_controller_->HasLivePorts(); |
272 | } |
273 | |
274 | tonic::DartErrorHandleType Engine::GetUIIsolateLastError() { |
275 | return runtime_controller_->GetLastError(); |
276 | } |
277 | |
278 | void Engine::OnOutputSurfaceCreated() { |
279 | have_surface_ = true; |
280 | StartAnimatorIfPossible(); |
281 | ScheduleFrame(); |
282 | } |
283 | |
284 | void Engine::OnOutputSurfaceDestroyed() { |
285 | have_surface_ = false; |
286 | StopAnimator(); |
287 | } |
288 | |
289 | void Engine::SetViewportMetrics(const ViewportMetrics& metrics) { |
290 | bool dimensions_changed = |
291 | viewport_metrics_.physical_height != metrics.physical_height || |
292 | viewport_metrics_.physical_width != metrics.physical_width || |
293 | viewport_metrics_.device_pixel_ratio != metrics.device_pixel_ratio; |
294 | viewport_metrics_ = metrics; |
295 | runtime_controller_->SetViewportMetrics(viewport_metrics_); |
296 | if (animator_) { |
297 | if (dimensions_changed) { |
298 | animator_->SetDimensionChangePending(); |
299 | } |
300 | if (have_surface_) { |
301 | ScheduleFrame(); |
302 | } |
303 | } |
304 | } |
305 | |
306 | void Engine::DispatchPlatformMessage(fml::RefPtr<PlatformMessage> message) { |
307 | std::string channel = message->channel(); |
308 | if (channel == kLifecycleChannel) { |
309 | if (HandleLifecyclePlatformMessage(message.get())) { |
310 | return; |
311 | } |
312 | } else if (channel == kLocalizationChannel) { |
313 | if (HandleLocalizationPlatformMessage(message.get())) { |
314 | return; |
315 | } |
316 | } else if (channel == kSettingsChannel) { |
317 | HandleSettingsPlatformMessage(message.get()); |
318 | return; |
319 | } else if (!runtime_controller_->IsRootIsolateRunning() && |
320 | channel == kNavigationChannel) { |
321 | // If there's no runtime_, we may still need to set the initial route. |
322 | HandleNavigationPlatformMessage(std::move(message)); |
323 | return; |
324 | } |
325 | |
326 | if (runtime_controller_->IsRootIsolateRunning() && |
327 | runtime_controller_->DispatchPlatformMessage(std::move(message))) { |
328 | return; |
329 | } |
330 | |
331 | FML_DLOG(WARNING) << "Dropping platform message on channel: " << channel; |
332 | } |
333 | |
334 | bool Engine::HandleLifecyclePlatformMessage(PlatformMessage* message) { |
335 | const auto& data = message->data(); |
336 | std::string state(reinterpret_cast<const char*>(data.data()), data.size()); |
337 | if (state == "AppLifecycleState.paused" || |
338 | state == "AppLifecycleState.detached" ) { |
339 | activity_running_ = false; |
340 | StopAnimator(); |
341 | } else if (state == "AppLifecycleState.resumed" || |
342 | state == "AppLifecycleState.inactive" ) { |
343 | activity_running_ = true; |
344 | StartAnimatorIfPossible(); |
345 | } |
346 | |
347 | // Always schedule a frame when the app does become active as per API |
348 | // recommendation |
349 | // https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive?language=objc |
350 | if (state == "AppLifecycleState.resumed" && have_surface_) { |
351 | ScheduleFrame(); |
352 | } |
353 | runtime_controller_->SetLifecycleState(state); |
354 | // Always forward these messages to the framework by returning false. |
355 | return false; |
356 | } |
357 | |
358 | bool Engine::HandleNavigationPlatformMessage( |
359 | fml::RefPtr<PlatformMessage> message) { |
360 | const auto& data = message->data(); |
361 | |
362 | rapidjson::Document document; |
363 | document.Parse(reinterpret_cast<const char*>(data.data()), data.size()); |
364 | if (document.HasParseError() || !document.IsObject()) { |
365 | return false; |
366 | } |
367 | auto root = document.GetObject(); |
368 | auto method = root.FindMember("method" ); |
369 | if (method->value != "setInitialRoute" ) { |
370 | return false; |
371 | } |
372 | auto route = root.FindMember("args" ); |
373 | initial_route_ = std::move(route->value.GetString()); |
374 | return true; |
375 | } |
376 | |
377 | bool Engine::HandleLocalizationPlatformMessage(PlatformMessage* message) { |
378 | const auto& data = message->data(); |
379 | |
380 | rapidjson::Document document; |
381 | document.Parse(reinterpret_cast<const char*>(data.data()), data.size()); |
382 | if (document.HasParseError() || !document.IsObject()) { |
383 | return false; |
384 | } |
385 | auto root = document.GetObject(); |
386 | auto method = root.FindMember("method" ); |
387 | if (method == root.MemberEnd()) { |
388 | return false; |
389 | } |
390 | const size_t strings_per_locale = 4; |
391 | if (method->value == "setLocale" ) { |
392 | // Decode and pass the list of locale data onwards to dart. |
393 | auto args = root.FindMember("args" ); |
394 | if (args == root.MemberEnd() || !args->value.IsArray()) { |
395 | return false; |
396 | } |
397 | |
398 | if (args->value.Size() % strings_per_locale != 0) { |
399 | return false; |
400 | } |
401 | std::vector<std::string> locale_data; |
402 | for (size_t locale_index = 0; locale_index < args->value.Size(); |
403 | locale_index += strings_per_locale) { |
404 | if (!args->value[locale_index].IsString() || |
405 | !args->value[locale_index + 1].IsString()) { |
406 | return false; |
407 | } |
408 | locale_data.push_back(args->value[locale_index].GetString()); |
409 | locale_data.push_back(args->value[locale_index + 1].GetString()); |
410 | locale_data.push_back(args->value[locale_index + 2].GetString()); |
411 | locale_data.push_back(args->value[locale_index + 3].GetString()); |
412 | } |
413 | |
414 | return runtime_controller_->SetLocales(locale_data); |
415 | } |
416 | return false; |
417 | } |
418 | |
419 | void Engine::HandleSettingsPlatformMessage(PlatformMessage* message) { |
420 | const auto& data = message->data(); |
421 | std::string jsonData(reinterpret_cast<const char*>(data.data()), data.size()); |
422 | if (runtime_controller_->SetUserSettingsData(std::move(jsonData)) && |
423 | have_surface_) { |
424 | ScheduleFrame(); |
425 | } |
426 | } |
427 | |
428 | void Engine::DispatchPointerDataPacket( |
429 | std::unique_ptr<PointerDataPacket> packet, |
430 | uint64_t trace_flow_id) { |
431 | TRACE_EVENT0("flutter" , "Engine::DispatchPointerDataPacket" ); |
432 | TRACE_FLOW_STEP("flutter" , "PointerEvent" , trace_flow_id); |
433 | pointer_data_dispatcher_->DispatchPacket(std::move(packet), trace_flow_id); |
434 | } |
435 | |
436 | void Engine::DispatchSemanticsAction(int id, |
437 | SemanticsAction action, |
438 | std::vector<uint8_t> args) { |
439 | runtime_controller_->DispatchSemanticsAction(id, action, std::move(args)); |
440 | } |
441 | |
442 | void Engine::SetSemanticsEnabled(bool enabled) { |
443 | runtime_controller_->SetSemanticsEnabled(enabled); |
444 | } |
445 | |
446 | void Engine::SetAccessibilityFeatures(int32_t flags) { |
447 | runtime_controller_->SetAccessibilityFeatures(flags); |
448 | } |
449 | |
450 | void Engine::StopAnimator() { |
451 | animator_->Stop(); |
452 | } |
453 | |
454 | void Engine::StartAnimatorIfPossible() { |
455 | if (activity_running_ && have_surface_) { |
456 | animator_->Start(); |
457 | } |
458 | } |
459 | |
460 | std::string Engine::DefaultRouteName() { |
461 | if (!initial_route_.empty()) { |
462 | return initial_route_; |
463 | } |
464 | return "/" ; |
465 | } |
466 | |
467 | void Engine::ScheduleFrame(bool regenerate_layer_tree) { |
468 | animator_->RequestFrame(regenerate_layer_tree); |
469 | } |
470 | |
471 | void Engine::Render(std::unique_ptr<flutter::LayerTree> layer_tree) { |
472 | if (!layer_tree) { |
473 | return; |
474 | } |
475 | |
476 | // Ensure frame dimensions are sane. |
477 | if (layer_tree->frame_size().isEmpty() || |
478 | layer_tree->device_pixel_ratio() <= 0.0f) { |
479 | return; |
480 | } |
481 | |
482 | animator_->Render(std::move(layer_tree)); |
483 | } |
484 | |
485 | void Engine::UpdateSemantics(SemanticsNodeUpdates update, |
486 | CustomAccessibilityActionUpdates actions) { |
487 | delegate_.OnEngineUpdateSemantics(std::move(update), std::move(actions)); |
488 | } |
489 | |
490 | void Engine::HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) { |
491 | if (message->channel() == kAssetChannel) { |
492 | HandleAssetPlatformMessage(std::move(message)); |
493 | } else { |
494 | delegate_.OnEngineHandlePlatformMessage(std::move(message)); |
495 | } |
496 | } |
497 | |
498 | void Engine::UpdateIsolateDescription(const std::string isolate_name, |
499 | int64_t isolate_port) { |
500 | delegate_.UpdateIsolateDescription(isolate_name, isolate_port); |
501 | } |
502 | |
503 | std::unique_ptr<std::vector<std::string>> Engine::ComputePlatformResolvedLocale( |
504 | const std::vector<std::string>& supported_locale_data) { |
505 | return delegate_.ComputePlatformResolvedLocale(supported_locale_data); |
506 | } |
507 | |
508 | void Engine::SetNeedsReportTimings(bool needs_reporting) { |
509 | delegate_.SetNeedsReportTimings(needs_reporting); |
510 | } |
511 | |
512 | FontCollection& Engine::GetFontCollection() { |
513 | return font_collection_; |
514 | } |
515 | |
516 | void Engine::DoDispatchPacket(std::unique_ptr<PointerDataPacket> packet, |
517 | uint64_t trace_flow_id) { |
518 | animator_->EnqueueTraceFlowId(trace_flow_id); |
519 | if (runtime_controller_) { |
520 | runtime_controller_->DispatchPointerDataPacket(*packet); |
521 | } |
522 | } |
523 | |
524 | void Engine::ScheduleSecondaryVsyncCallback(const fml::closure& callback) { |
525 | animator_->ScheduleSecondaryVsyncCallback(callback); |
526 | } |
527 | |
528 | void Engine::HandleAssetPlatformMessage(fml::RefPtr<PlatformMessage> message) { |
529 | fml::RefPtr<PlatformMessageResponse> response = message->response(); |
530 | if (!response) { |
531 | return; |
532 | } |
533 | const auto& data = message->data(); |
534 | std::string asset_name(reinterpret_cast<const char*>(data.data()), |
535 | data.size()); |
536 | |
537 | if (asset_manager_) { |
538 | std::unique_ptr<fml::Mapping> asset_mapping = |
539 | asset_manager_->GetAsMapping(asset_name); |
540 | if (asset_mapping) { |
541 | response->Complete(std::move(asset_mapping)); |
542 | return; |
543 | } |
544 | } |
545 | |
546 | response->CompleteEmpty(); |
547 | } |
548 | |
549 | const std::string& Engine::GetLastEntrypoint() const { |
550 | return last_entry_point_; |
551 | } |
552 | |
553 | const std::string& Engine::GetLastEntrypointLibrary() const { |
554 | return last_entry_point_library_; |
555 | } |
556 | |
557 | } // namespace flutter |
558 | |