diff --git a/DEPS b/DEPS index 4e77c00034a10..cd8856e94f7ef 100644 --- a/DEPS +++ b/DEPS @@ -469,7 +469,7 @@ deps = { 'packages': [ { 'package': 'flutter/android/embedding_bundle', - 'version': 'last_updated:2020-05-20T01:36:16-0700' + 'version': 'last_updated:2020-09-11T17:57:41-0700' } ], 'condition': 'download_android_deps', diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn index 9788794f7da83..09207b330c3c4 100644 --- a/runtime/BUILD.gn +++ b/runtime/BUILD.gn @@ -37,6 +37,8 @@ group("libdart") { source_set("runtime") { sources = [ + "dart_deferred_load_handler.cc", + "dart_deferred_load_handler.h", "dart_isolate.cc", "dart_isolate.h", "dart_isolate_group_data.cc", diff --git a/runtime/dart_deferred_load_handler.cc b/runtime/dart_deferred_load_handler.cc new file mode 100644 index 0000000000000..0f203ee4937b9 --- /dev/null +++ b/runtime/dart_deferred_load_handler.cc @@ -0,0 +1,38 @@ +// Copyright 2020 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/runtime/dart_deferred_load_handler.h" + +#include "flutter/runtime/runtime_controller.h" + +namespace flutter { + +void* DartDeferredLoadHandler::runtime_controller_ = nullptr; + +Dart_DeferredLoadHandler + DartDeferredLoadHandler::empty_dart_deferred_load_handler = + &DartDeferredLoadHandler::EmptyDartLoadLibrary; + +Dart_DeferredLoadHandler DartDeferredLoadHandler::dart_deferred_load_handler = + &DartDeferredLoadHandler::OnDartLoadLibrary; + +void DartDeferredLoadHandler::RegisterRuntimeController( + void* runtime_controller) { + runtime_controller_ = runtime_controller; +} + +Dart_Handle DartDeferredLoadHandler::OnDartLoadLibrary( + intptr_t loading_unit_id) { + if (runtime_controller_ != nullptr) + return static_cast(runtime_controller_) + ->OnDartLoadLibrary(loading_unit_id); + return Dart_Null(); +} + +Dart_Handle DartDeferredLoadHandler::EmptyDartLoadLibrary( + intptr_t loading_unit_id) { + return Dart_Null(); +} + +} // namespace flutter diff --git a/runtime/dart_deferred_load_handler.h b/runtime/dart_deferred_load_handler.h new file mode 100644 index 0000000000000..a6947570ea132 --- /dev/null +++ b/runtime/dart_deferred_load_handler.h @@ -0,0 +1,33 @@ +// Copyright 2020 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_RUNTIME_DART_DEFERRED_LOAD_HANDLER_H_ +#define FLUTTER_RUNTIME_DART_DEFERRED_LOAD_HANDLER_H_ + +#include +#include + +#include "third_party/dart/runtime/include/dart_api.h" + +namespace flutter { + +class DartDeferredLoadHandler { + public: + // TODO: Fix deps to not use void* + static void RegisterRuntimeController(void* runtime_controller); + + static Dart_DeferredLoadHandler empty_dart_deferred_load_handler; + static Dart_DeferredLoadHandler dart_deferred_load_handler; + + private: + static Dart_Handle OnDartLoadLibrary(intptr_t loading_unit_id); + // No-op function that returns Dart_Null() for when the isolate is not + // expected to handle deferred libraries. + static Dart_Handle EmptyDartLoadLibrary(intptr_t loading_unit_id); + static void* runtime_controller_; +}; + +} // namespace flutter + +#endif // FLUTTER_RUNTIME_DART_DEFERRED_LOAD_HANDLER_H_ diff --git a/runtime/dart_isolate.cc b/runtime/dart_isolate.cc index b834957a9281a..cf5f9cd8df584 100644 --- a/runtime/dart_isolate.cc +++ b/runtime/dart_isolate.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "flutter/runtime/dart_isolate.h" +#include "flutter/runtime/dart_deferred_load_handler.h" #include #include @@ -86,6 +87,7 @@ std::weak_ptr DartIsolate::CreateRunningRootIsolate( std::string advisory_script_uri, std::string advisory_script_entrypoint, Flags isolate_flags, + Dart_DeferredLoadHandler& deferred_load_handler, const fml::closure& isolate_create_callback, const fml::closure& isolate_shutdown_callback, std::optional dart_entrypoint, @@ -116,6 +118,7 @@ std::weak_ptr DartIsolate::CreateRunningRootIsolate( advisory_script_uri, // advisory_script_entrypoint, // isolate_flags, // + deferred_load_handler, // isolate_create_callback, // isolate_shutdown_callback // ) @@ -149,8 +152,8 @@ std::weak_ptr DartIsolate::CreateRunningRootIsolate( } if (settings.root_isolate_create_callback) { - // Isolate callbacks always occur in isolate scope and before user code has - // had a chance to run. + // Isolate callbacks always occur in isolate scope and before user code + // has had a chance to run. tonic::DartState::Scope scope(isolate.get()); settings.root_isolate_create_callback(*isolate.get()); } @@ -186,6 +189,7 @@ std::weak_ptr DartIsolate::CreateRootIsolate( std::string advisory_script_uri, std::string advisory_script_entrypoint, Flags flags, + Dart_DeferredLoadHandler& deferred_load_handler, const fml::closure& isolate_create_callback, const fml::closure& isolate_shutdown_callback) { TRACE_EVENT0("flutter", "DartIsolate::CreateRootIsolate"); @@ -222,7 +226,7 @@ std::weak_ptr DartIsolate::CreateRootIsolate( auto isolate_flags = flags.Get(); Dart_Isolate vm_isolate = CreateDartIsolateGroup( std::move(isolate_group_data), std::move(isolate_data), &isolate_flags, - error.error()); + deferred_load_handler, error.error()); if (error) { FML_LOG(ERROR) << "CreateDartIsolateGroup failed: " << error.str(); @@ -288,7 +292,8 @@ std::string DartIsolate::GetServiceId() { return service_id; } -bool DartIsolate::Initialize(Dart_Isolate dart_isolate) { +bool DartIsolate::Initialize(Dart_Isolate dart_isolate, + Dart_DeferredLoadHandler& deferred_load_handler) { TRACE_EVENT0("flutter", "DartIsolate::Initialize"); if (phase_ != Phase::Uninitialized) { return false; @@ -320,6 +325,9 @@ bool DartIsolate::Initialize(Dart_Isolate dart_isolate) { return false; } + // Dart_SetDeferredLoadHandler(&DartDeferredLoadHandler); + Dart_SetDeferredLoadHandler(deferred_load_handler); + if (!UpdateThreadPoolNames()) { return false; } @@ -332,6 +340,33 @@ fml::RefPtr DartIsolate::GetMessageHandlingTaskRunner() const { return message_handling_task_runner_; } +bool DartIsolate::LoadLoadingUnit(intptr_t loading_unit_id, + const uint8_t* snapshot_data, + const uint8_t* snapshot_instructions) { + tonic::DartState::Scope scope(this); + Dart_Handle result = Dart_DeferredLoadComplete(loading_unit_id, snapshot_data, + snapshot_instructions); + if (Dart_IsApiError(result)) { + FML_LOG(ERROR) << "LOADING FAILED " << loading_unit_id; + result = + Dart_DeferredLoadCompleteError(loading_unit_id, Dart_GetError(result), + /*transient*/ true); + return false; + } + return true; +} + +void DartIsolate::LoadLoadingUnitFailure(intptr_t loading_unit_id, + const std::string error_message, + bool transient) { + tonic::DartState::Scope scope(this); + Dart_Handle result = Dart_DeferredLoadCompleteError( + loading_unit_id, error_message.c_str(), transient); + if (Dart_IsApiError(result)) { + FML_LOG(ERROR) << "Dart error: " << Dart_GetError(result); + } +} + void DartIsolate::SetMessageHandlingTaskRunner( fml::RefPtr runner) { if (!IsRootIsolate() || !runner) { @@ -649,8 +684,8 @@ bool DartIsolate::RunFromLibrary(std::optional library_name, bool DartIsolate::Shutdown() { TRACE_EVENT0("flutter", "DartIsolate::Shutdown"); // This call may be re-entrant since Dart_ShutdownIsolate can invoke the - // cleanup callback which deletes the embedder side object of the dart isolate - // (a.k.a. this). + // cleanup callback which deletes the embedder side object of the dart + // isolate (a.k.a. this). if (phase_ == Phase::Shutdown) { return false; } @@ -716,8 +751,9 @@ Dart_Isolate DartIsolate::DartCreateAndStartServiceIsolate( DART_VM_SERVICE_ISOLATE_NAME, // script uri DART_VM_SERVICE_ISOLATE_NAME, // script entrypoint DartIsolate::Flags{flags}, // flags - nullptr, // isolate create callback - nullptr // isolate shutdown callback + DartDeferredLoadHandler::empty_dart_deferred_load_handler, + nullptr, // isolate create callback + nullptr // isolate shutdown callback ); std::shared_ptr service_isolate = weak_service_isolate.lock(); @@ -780,8 +816,9 @@ Dart_Isolate DartIsolate::DartIsolateGroupCreateCallback( // The VM attempts to start the VM service for us on |Dart_Initialize|. In // such a case, the callback data will be null and the script URI will be // DART_VM_SERVICE_ISOLATE_NAME. In such cases, we just create the service - // isolate like normal but dont hold a reference to it at all. We also start - // this isolate since we will never again reference it from the engine. + // isolate like normal but dont hold a reference to it at all. We also + // start this isolate since we will never again reference it from the + // engine. return DartCreateAndStartServiceIsolate(package_root, // package_config, // flags, // @@ -826,7 +863,8 @@ Dart_Isolate DartIsolate::DartIsolateGroupCreateCallback( false))); // is_root_isolate Dart_Isolate vm_isolate = CreateDartIsolateGroup( - std::move(isolate_group_data), std::move(isolate_data), flags, error); + std::move(isolate_group_data), std::move(isolate_data), flags, + DartDeferredLoadHandler::empty_dart_deferred_load_handler, error); if (*error) { FML_LOG(ERROR) << "CreateDartIsolateGroup failed: " << error; @@ -871,7 +909,9 @@ bool DartIsolate::DartIsolateInitializeCallback(void** child_callback_data, false))); // is_root_isolate // root isolate should have been created via CreateRootIsolate - if (!InitializeIsolate(*embedder_isolate, isolate, error)) { + if (!InitializeIsolate( + *embedder_isolate, isolate, + DartDeferredLoadHandler::empty_dart_deferred_load_handler, error)) { return false; } @@ -887,6 +927,7 @@ Dart_Isolate DartIsolate::CreateDartIsolateGroup( std::unique_ptr> isolate_group_data, std::unique_ptr> isolate_data, Dart_IsolateFlags* flags, + Dart_DeferredLoadHandler& deferred_load_handler, char** error) { TRACE_EVENT0("flutter", "DartIsolate::CreateDartIsolateGroup"); @@ -902,12 +943,14 @@ Dart_Isolate DartIsolate::CreateDartIsolateGroup( return nullptr; } - // Ownership of the isolate data objects has been transferred to the Dart VM. + // Ownership of the isolate data objects has been transferred to the Dart + // VM. std::shared_ptr embedder_isolate(*isolate_data); isolate_group_data.release(); isolate_data.release(); - if (!InitializeIsolate(std::move(embedder_isolate), isolate, error)) { + if (!InitializeIsolate(std::move(embedder_isolate), isolate, + deferred_load_handler, error)) { return nullptr; } @@ -917,9 +960,10 @@ Dart_Isolate DartIsolate::CreateDartIsolateGroup( bool DartIsolate::InitializeIsolate( std::shared_ptr embedder_isolate, Dart_Isolate isolate, + Dart_DeferredLoadHandler& deferred_load_handler, char** error) { TRACE_EVENT0("flutter", "DartIsolate::InitializeIsolate"); - if (!embedder_isolate->Initialize(isolate)) { + if (!embedder_isolate->Initialize(isolate, deferred_load_handler)) { *error = fml::strdup("Embedder could not initialize the Dart isolate."); FML_DLOG(ERROR) << *error; return false; @@ -932,9 +976,9 @@ bool DartIsolate::InitializeIsolate( return false; } - // Root isolates will be setup by the engine and the service isolate (which is - // also a root isolate) by the utility routines in the VM. However, secondary - // isolates will be run by the VM if they are marked as runnable. + // Root isolates will be setup by the engine and the service isolate (which + // is also a root isolate) by the utility routines in the VM. However, + // secondary isolates will be run by the VM if they are marked as runnable. if (!embedder_isolate->IsRootIsolate()) { auto child_isolate_preparer = embedder_isolate->GetIsolateGroupData().GetChildIsolatePreparer(); diff --git a/runtime/dart_isolate.h b/runtime/dart_isolate.h index 5e6913d52b185..ca9ea2b80ad52 100644 --- a/runtime/dart_isolate.h +++ b/runtime/dart_isolate.h @@ -225,6 +225,7 @@ class DartIsolate : public UIDartState { std::string advisory_script_uri, std::string advisory_script_entrypoint, Flags flags, + Dart_DeferredLoadHandler& deferred_load_handler, const fml::closure& isolate_create_callback, const fml::closure& isolate_shutdown_callback, std::optional dart_entrypoint, @@ -384,6 +385,14 @@ class DartIsolate : public UIDartState { /// fml::RefPtr GetMessageHandlingTaskRunner() const; + bool LoadLoadingUnit(intptr_t loading_unit_id, + const uint8_t* snapshot_data, + const uint8_t* snapshot_instructions); + + void LoadLoadingUnitFailure(intptr_t loading_unit_id, + const std::string error_message, + bool transient); + private: friend class IsolateConfiguration; class AutoFireClosure { @@ -418,6 +427,7 @@ class DartIsolate : public UIDartState { std::string advisory_script_uri, std::string advisory_script_entrypoint, Flags flags, + Dart_DeferredLoadHandler& deferred_load_handler, const fml::closure& isolate_create_callback, const fml::closure& isolate_shutdown_callback); @@ -432,7 +442,9 @@ class DartIsolate : public UIDartState { std::string advisory_script_entrypoint, bool is_root_isolate); - [[nodiscard]] bool Initialize(Dart_Isolate isolate); + [[nodiscard]] bool Initialize( + Dart_Isolate isolate, + Dart_DeferredLoadHandler& deferred_load_handler); void SetMessageHandlingTaskRunner(fml::RefPtr runner); @@ -472,10 +484,12 @@ class DartIsolate : public UIDartState { std::unique_ptr> isolate_group_data, std::unique_ptr> isolate_data, Dart_IsolateFlags* flags, + Dart_DeferredLoadHandler& deferred_load_handler, char** error); static bool InitializeIsolate(std::shared_ptr embedder_isolate, Dart_Isolate isolate, + Dart_DeferredLoadHandler& deferred_load_handler, char** error); // |Dart_IsolateShutdownCallback| diff --git a/runtime/dart_isolate_unittests.cc b/runtime/dart_isolate_unittests.cc index a90599fd8a3fb..be62acbabd811 100644 --- a/runtime/dart_isolate_unittests.cc +++ b/runtime/dart_isolate_unittests.cc @@ -8,6 +8,7 @@ #include "flutter/fml/synchronization/count_down_latch.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/fml/thread.h" +#include "flutter/runtime/dart_deferred_load_handler.h" #include "flutter/runtime/dart_vm.h" #include "flutter/runtime/dart_vm_lifecycle.h" #include "flutter/runtime/isolate_configuration.h" @@ -52,18 +53,19 @@ TEST_F(DartIsolateTest, RootIsolateCreationAndShutdown) { IsolateConfiguration::InferFromSettings(settings); auto weak_isolate = DartIsolate::CreateRunningRootIsolate( - vm_data->GetSettings(), // settings - vm_data->GetIsolateSnapshot(), // isolate snapshot - std::move(task_runners), // task runners - nullptr, // window - {}, // snapshot delegate - {}, // hint freed delegate - {}, // io manager - {}, // unref queue - {}, // image decoder - "main.dart", // advisory uri - "main", // advisory entrypoint, - DartIsolate::Flags{}, // flags + vm_data->GetSettings(), // settings + vm_data->GetIsolateSnapshot(), // isolate snapshot + std::move(task_runners), // task runners + nullptr, // window + {}, // snapshot delegate + {}, // hint freed delegate + {}, // io manager + {}, // unref queue + {}, // image decoder + "main.dart", // advisory uri + "main", // advisory entrypoint, + DartIsolate::Flags{}, // flags + DartDeferredLoadHandler::empty_dart_deferred_load_handler, settings.isolate_create_callback, // isolate create callback settings.isolate_shutdown_callback, // isolate shutdown callback "main", // dart entrypoint @@ -92,18 +94,19 @@ TEST_F(DartIsolateTest, IsolateShutdownCallbackIsInIsolateScope) { auto isolate_configuration = IsolateConfiguration::InferFromSettings(settings); auto weak_isolate = DartIsolate::CreateRunningRootIsolate( - vm_data->GetSettings(), // settings - vm_data->GetIsolateSnapshot(), // isolate snapshot - std::move(task_runners), // task runners - nullptr, // window - {}, // snapshot delegate - {}, // hint freed delegate - {}, // io manager - {}, // unref queue - {}, // image decoder - "main.dart", // advisory uri - "main", // advisory entrypoint - DartIsolate::Flags{}, // flags + vm_data->GetSettings(), // settings + vm_data->GetIsolateSnapshot(), // isolate snapshot + std::move(task_runners), // task runners + nullptr, // window + {}, // snapshot delegate + {}, // hint freed delegate + {}, // io manager + {}, // unref queue + {}, // image decoder + "main.dart", // advisory uri + "main", // advisory entrypoint + DartIsolate::Flags{}, // flags + DartDeferredLoadHandler::empty_dart_deferred_load_handler, settings.isolate_create_callback, // isolate create callback settings.isolate_shutdown_callback, // isolate shutdown callback "main", // dart entrypoint @@ -350,18 +353,19 @@ TEST_F(DartIsolateTest, CanCreateServiceIsolate) { auto isolate_configuration = IsolateConfiguration::InferFromSettings(settings); auto weak_isolate = DartIsolate::CreateRunningRootIsolate( - vm_data->GetSettings(), // settings - vm_data->GetIsolateSnapshot(), // isolate snapshot - std::move(task_runners), // task runners - nullptr, // window - {}, // snapshot delegate - {}, // hint freed delegate - {}, // io manager - {}, // unref queue - {}, // image decoder - "main.dart", // advisory uri - "main", // advisory entrypoint, - DartIsolate::Flags{}, // flags + vm_data->GetSettings(), // settings + vm_data->GetIsolateSnapshot(), // isolate snapshot + std::move(task_runners), // task runners + nullptr, // window + {}, // snapshot delegate + {}, // hint freed delegate + {}, // io manager + {}, // unref queue + {}, // image decoder + "main.dart", // advisory uri + "main", // advisory entrypoint, + DartIsolate::Flags{}, // flags + DartDeferredLoadHandler::empty_dart_deferred_load_handler, settings.isolate_create_callback, // isolate create callback settings.isolate_shutdown_callback, // isolate shutdown callback "main", // dart entrypoint diff --git a/runtime/dart_lifecycle_unittests.cc b/runtime/dart_lifecycle_unittests.cc index 8576b3fb11292..793247654a47d 100644 --- a/runtime/dart_lifecycle_unittests.cc +++ b/runtime/dart_lifecycle_unittests.cc @@ -6,6 +6,7 @@ #include "flutter/fml/paths.h" #include "flutter/fml/synchronization/count_down_latch.h" #include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/runtime/dart_deferred_load_handler.h" #include "flutter/runtime/dart_vm.h" #include "flutter/runtime/dart_vm_lifecycle.h" #include "flutter/runtime/isolate_configuration.h" @@ -57,18 +58,19 @@ static std::shared_ptr CreateAndRunRootIsolate( auto isolate = DartIsolate::CreateRunningRootIsolate( - vm.GetSettings(), // settings - vm.GetIsolateSnapshot(), // isolate_snapshot - runners, // task_runners - {}, // window - {}, // snapshot_delegate - {}, // hint_freed_delegate - {}, // io_manager - {}, // unref_queue - {}, // image_decoder - "main.dart", // advisory_script_uri - entrypoint.c_str(), // advisory_script_entrypoint - DartIsolate::Flags{}, // flags + vm.GetSettings(), // settings + vm.GetIsolateSnapshot(), // isolate_snapshot + runners, // task_runners + {}, // window + {}, // snapshot_delegate + {}, // hint_freed_delegate + {}, // io_manager + {}, // unref_queue + {}, // image_decoder + "main.dart", // advisory_script_uri + entrypoint.c_str(), // advisory_script_entrypoint + DartIsolate::Flags{}, // flags + DartDeferredLoadHandler::empty_dart_deferred_load_handler, settings.isolate_create_callback, // isolate create callback settings.isolate_shutdown_callback, // isolate shutdown callback, entrypoint, // dart entrypoint diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 3551e1967134e..185f451189c39 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -4,6 +4,9 @@ #include "flutter/runtime/runtime_controller.h" +#include +#include + #include "flutter/fml/message_loop.h" #include "flutter/fml/trace_event.h" #include "flutter/lib/ui/compositing/scene.h" @@ -11,6 +14,7 @@ #include "flutter/lib/ui/window/platform_configuration.h" #include "flutter/lib/ui/window/viewport_metrics.h" #include "flutter/lib/ui/window/window.h" +#include "flutter/runtime/dart_deferred_load_handler.h" #include "flutter/runtime/isolate_configuration.h" #include "flutter/runtime/runtime_delegate.h" #include "third_party/tonic/dart_message_handler.h" @@ -53,7 +57,9 @@ RuntimeController::RuntimeController( platform_data_(std::move(p_platform_data)), isolate_create_callback_(p_isolate_create_callback), isolate_shutdown_callback_(p_isolate_shutdown_callback), - persistent_isolate_data_(std::move(p_persistent_isolate_data)) {} + persistent_isolate_data_(std::move(p_persistent_isolate_data)) { + DartDeferredLoadHandler::RegisterRuntimeController(this); +} RuntimeController::~RuntimeController() { FML_DCHECK(Dart_CurrentIsolate() == nullptr); @@ -353,23 +359,24 @@ bool RuntimeController::LaunchRootIsolate( auto strong_root_isolate = DartIsolate::CreateRunningRootIsolate( - settings, // - isolate_snapshot_, // - task_runners_, // - std::make_unique(this), // - snapshot_delegate_, // - hint_freed_delegate_, // - io_manager_, // - unref_queue_, // - image_decoder_, // - advisory_script_uri_, // - advisory_script_entrypoint_, // - DartIsolate::Flags{}, // - isolate_create_callback_, // - isolate_shutdown_callback_, // - dart_entrypoint, // - dart_entrypoint_library, // - std::move(isolate_configuration) // + settings, // + isolate_snapshot_, // + task_runners_, // + std::make_unique(this), // + snapshot_delegate_, // + hint_freed_delegate_, // + io_manager_, // + unref_queue_, // + image_decoder_, // + advisory_script_uri_, // + advisory_script_entrypoint_, // + DartIsolate::Flags{}, // + DartDeferredLoadHandler::dart_deferred_load_handler, // + isolate_create_callback_, // + isolate_shutdown_callback_, // + dart_entrypoint, // + dart_entrypoint_library, // + std::move(isolate_configuration) // ) .lock(); @@ -415,6 +422,78 @@ std::optional RuntimeController::GetRootIsolateReturnCode() { return root_isolate_return_code_; } +void RuntimeController::CompleteDartLoadLibrary( + intptr_t loading_unit_id, + std::string lib_name, + std::vector& apkPaths, + std::string abi) { + std::vector searchPaths; + for (std::string apkPath : apkPaths) { + searchPaths.push_back(apkPath + "!/lib/" + abi + "/" + lib_name); + } + + // TODO: Switch to using the NativeLibrary class, eg: + // + // fml::RefPtr native_lib = + // fml::NativeLibrary::Create(lib_name.c_str()); + void* handle = nullptr; + while (handle == nullptr && !searchPaths.empty()) { + std::string path = searchPaths.back(); + handle = ::dlopen(path.c_str(), RTLD_NOW); + searchPaths.pop_back(); + if (handle == nullptr) { + FML_LOG(ERROR) << "Opening lib \"" << lib_name + << "\" failed: " + std::string(::dlerror()); + } + } + if (handle == nullptr) { + root_isolate_.lock()->LoadLoadingUnitFailure( + loading_unit_id, "No lib .so found for provided search paths.", true); + return; + } + + uint8_t* isolate_data = + static_cast(::dlsym(handle, DartSnapshot::kIsolateDataSymbol)); + if (isolate_data == nullptr) { + // Mac sometimes requires an underscore prefix. + std::stringstream underscore_symbol_name; + underscore_symbol_name << "_" << DartSnapshot::kIsolateDataSymbol; + isolate_data = static_cast( + ::dlsym(handle, underscore_symbol_name.str().c_str())); + if (isolate_data == nullptr) { + // FML_LOG(ERROR) << "Could not resolve symbol in library: " + // << DartSnapshot::kIsolateDataSymbol; + root_isolate_.lock()->LoadLoadingUnitFailure( + loading_unit_id, "Could not resolve data symbol in library", true); + return; + } + } + uint8_t* isolate_instructions = static_cast( + ::dlsym(handle, DartSnapshot::kIsolateInstructionsSymbol)); + if (isolate_instructions == nullptr) { + // Mac sometimes requires an underscore prefix. + std::stringstream underscore_symbol_name; + underscore_symbol_name << "_" << DartSnapshot::kIsolateInstructionsSymbol; + isolate_instructions = static_cast( + ::dlsym(handle, underscore_symbol_name.str().c_str())); + if (isolate_data == nullptr) { + // FML_LOG(ERROR) << "Could not resolve symbol in library: " + // << DartSnapshot::kIsolateInstructionsSymbol; + root_isolate_.lock()->LoadLoadingUnitFailure( + loading_unit_id, "Could not resolve instructions symbol in library", + true); + return; + } + } + + root_isolate_.lock()->LoadLoadingUnit(loading_unit_id, isolate_data, + isolate_instructions); +} + +Dart_Handle RuntimeController::OnDartLoadLibrary(intptr_t loading_unit_id) { + return client_.OnDartLoadLibrary(loading_unit_id); +} + RuntimeController::Locale::Locale(std::string language_code_, std::string country_code_, std::string script_code_, diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 22941c7388a1b..02fc925403ccc 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -473,6 +473,41 @@ class RuntimeController : public PlatformConfigurationClient { /// std::optional GetRootIsolateReturnCode(); + //-------------------------------------------------------------------------- + /// @brief Loads the dart shared library from disk and into the dart VM + /// based off of the search parameters. When the dart library is + /// loaded successfully, the dart future returned by the + /// originating loadLibrary() call completes. + /// + /// @param[in] loading_unit_id The unique id of the deferred library's + /// loading unit. + /// + /// @param[in] lib_name The file name of the .so shared library + /// file. + /// + /// @param[in] apkPaths The paths of the APKs that may or may not + /// contain the lib_name file. + /// + /// @param[in] abi The abi of the library, eg, arm64-v8a + /// + void CompleteDartLoadLibrary(intptr_t loading_unit_id, + std::string lib_name, + std::vector& apkPaths, + std::string abi); + + //-------------------------------------------------------------------------- + /// @brief Invoked when the dart VM requests that a deferred library + /// be loaded. Notifies the engine that the requested loading + /// unit should be downloaded and loaded. + /// + /// @param[in] loading_unit_id The unique id of the deferred library's + /// loading unit. + /// + /// @return A Dart_Handle that is Dart_Null on success, and a dart error + /// on failure. + /// + Dart_Handle OnDartLoadLibrary(intptr_t loading_unit_id); + protected: /// Constructor for Mocks. RuntimeController(RuntimeDelegate& client, TaskRunners p_task_runners); diff --git a/runtime/runtime_delegate.h b/runtime/runtime_delegate.h index 55978b4dbc39f..e5a2113c4380d 100644 --- a/runtime/runtime_delegate.h +++ b/runtime/runtime_delegate.h @@ -43,6 +43,8 @@ class RuntimeDelegate { ComputePlatformResolvedLocale( const std::vector& supported_locale_data) = 0; + virtual Dart_Handle OnDartLoadLibrary(intptr_t loading_unit_id) = 0; + protected: virtual ~RuntimeDelegate(); }; diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 6c9304f1ad945..6ac9c2bbfeb5d 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -507,4 +507,19 @@ const std::string& Engine::GetLastEntrypointLibrary() const { return last_entry_point_library_; } +// |RuntimeDelegate| +Dart_Handle Engine::OnDartLoadLibrary(intptr_t loading_unit_id) { + return delegate_.OnDartLoadLibrary(loading_unit_id); +} + +void Engine::CompleteDartLoadLibrary(intptr_t loading_unit_id, + std::string lib_name, + std::vector& apkPaths, + std::string abi) { + if (runtime_controller_->IsRootIsolateRunning()) { + runtime_controller_->CompleteDartLoadLibrary(loading_unit_id, lib_name, + apkPaths, abi); + } +} + } // namespace flutter diff --git a/shell/common/engine.h b/shell/common/engine.h index 1eb2cf36ecc72..c5c623508af7e 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -30,6 +30,7 @@ #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/run_configuration.h" #include "flutter/shell/common/shell_io_manager.h" +#include "third_party/dart/runtime/include/dart_api.h" #include "third_party/skia/include/core/SkPicture.h" namespace flutter { @@ -260,6 +261,19 @@ class Engine final : public RuntimeDelegate, virtual std::unique_ptr> ComputePlatformResolvedLocale( const std::vector& supported_locale_data) = 0; + + //-------------------------------------------------------------------------- + /// @brief Invoked when the dart VM requests that a deferred library + /// be loaded. Notifies the engine that the requested loading + /// unit should be downloaded and loaded. + /// + /// @param[in] loading_unit_id The unique id of the deferred library's + /// loading unit. + /// + /// @return A Dart_Handle that is Dart_Null on success, and a dart error + /// on failure. + /// + virtual Dart_Handle OnDartLoadLibrary(intptr_t loading_unit_id) = 0; }; //---------------------------------------------------------------------------- @@ -767,6 +781,28 @@ class Engine final : public RuntimeDelegate, /// const std::string& InitialRoute() const { return initial_route_; } + //-------------------------------------------------------------------------- + /// @brief Loads the dart shared library from disk and into the dart VM + /// based off of the search parameters. When the dart library is + /// loaded successfully, the dart future returned by the + /// originating loadLibrary() call completes. + /// + /// @param[in] loading_unit_id The unique id of the deferred library's + /// loading unit. + /// + /// @param[in] lib_name The file name of the .so shared library + /// file. + /// + /// @param[in] apkPaths The paths of the APKs that may or may not + /// contain the lib_name file. + /// + /// @param[in] abi The abi of the library, eg, arm64-v8a + /// + void CompleteDartLoadLibrary(intptr_t loading_unit_id, + std::string lib_name, + std::vector& apkPaths, + std::string abi); + private: Engine::Delegate& delegate_; const Settings settings_; @@ -815,6 +851,9 @@ class Engine final : public RuntimeDelegate, std::unique_ptr> ComputePlatformResolvedLocale( const std::vector& supported_locale_data) override; + // |RuntimeDelegate| + Dart_Handle OnDartLoadLibrary(intptr_t loading_unit_id) override; + void SetNeedsReportTimings(bool value) override; void StopAnimator(); diff --git a/shell/common/engine_unittests.cc b/shell/common/engine_unittests.cc index 7405511513a9b..d6f3bf9fa3bcf 100644 --- a/shell/common/engine_unittests.cc +++ b/shell/common/engine_unittests.cc @@ -13,6 +13,7 @@ #include "rapidjson/document.h" #include "rapidjson/stringbuffer.h" #include "rapidjson/writer.h" +#include "third_party/dart/runtime/include/dart_api.h" ///\note Deprecated MOCK_METHOD macros used until this issue is resolved: // https://github.com/google/googletest/issues/2490 @@ -32,6 +33,7 @@ class MockDelegate : public Engine::Delegate { MOCK_METHOD1(ComputePlatformResolvedLocale, std::unique_ptr>( const std::vector&)); + MOCK_METHOD1(OnDartLoadLibrary, Dart_Handle(intptr_t)); }; class MockResponse : public PlatformMessageResponse { @@ -55,6 +57,7 @@ class MockRuntimeDelegate : public RuntimeDelegate { MOCK_METHOD1(ComputePlatformResolvedLocale, std::unique_ptr>( const std::vector&)); + MOCK_METHOD1(OnDartLoadLibrary, Dart_Handle(intptr_t)); }; class MockRuntimeController : public RuntimeController { diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index a933bd3b3c6bf..ce00da2eb2718 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -152,4 +152,16 @@ PlatformView::ComputePlatformResolvedLocales( return out; } +Dart_Handle PlatformView::OnDartLoadLibrary(intptr_t loading_unit_id) { + return Dart_Null(); +} + +void PlatformView::CompleteDartLoadLibrary(intptr_t loading_unit_id, + std::string lib_name, + std::vector& apkPaths, + std::string abi) {} + +void PlatformView::UpdateAssetManager( + std::shared_ptr asset_manager) {} + } // namespace flutter diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 5b376878ee208..b0e7cf8a294dd 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -228,6 +228,50 @@ class PlatformView { virtual std::unique_ptr> ComputePlatformViewResolvedLocale( const std::vector& supported_locale_data) = 0; + + //-------------------------------------------------------------------------- + /// @brief Invoked when the dart VM requests that a deferred library + /// be loaded. Notifies the engine that the requested loading + /// unit should be downloaded and loaded. + /// + /// @param[in] loading_unit_id The unique id of the deferred library's + /// loading unit. + /// + /// @return A Dart_Handle that is Dart_Null on success, and a dart error + /// on failure. + /// + virtual Dart_Handle OnPlatformViewDartLoadLibrary( + intptr_t loading_unit_id) = 0; + + //-------------------------------------------------------------------------- + /// @brief Loads the dart shared library from disk and into the dart VM + /// based off of the search parameters. When the dart library is + /// loaded successfully, the dart future returned by the + /// originating loadLibrary() call completes. + /// + /// @param[in] loading_unit_id The unique id of the deferred library's + /// loading unit. + /// + /// @param[in] lib_name The file name of the .so shared library + /// file. + /// + /// @param[in] apkPaths The paths of the APKs that may or may not + /// contain the lib_name file. + /// + /// @param[in] abi The abi of the library, eg, arm64-v8a + /// + virtual void CompleteDartLoadLibrary(intptr_t loading_unit_id, + std::string lib_name, + std::vector& apkPaths, + std::string abi) = 0; + + //-------------------------------------------------------------------------- + /// @brief Sets the asset manager of the engine to asset_manager + /// + /// @param[in] asset_manager The asset manager to use. + /// + virtual void UpdateAssetManager( + std::shared_ptr asset_manager) = 0; }; //---------------------------------------------------------------------------- @@ -581,6 +625,43 @@ class PlatformView { ComputePlatformResolvedLocales( const std::vector& supported_locale_data); + //-------------------------------------------------------------------------- + /// @brief Invoked when the dart VM requests that a deferred library + /// be loaded. Notifies the engine that the requested loading + /// unit should be downloaded and loaded. + /// + /// @param[in] loading_unit_id The unique id of the deferred library's + /// loading unit. + /// + /// @return A Dart_Handle that is Dart_Null on success, and a dart error + /// on failure. + /// + virtual Dart_Handle OnDartLoadLibrary(intptr_t loading_unit_id); + + //-------------------------------------------------------------------------- + /// @brief Loads the dart shared library from disk and into the dart VM + /// based off of the search parameters. When the dart library is + /// loaded successfully, the dart future returned by the + /// originating loadLibrary() call completes. + /// + /// @param[in] loading_unit_id The unique id of the deferred library's + /// loading unit. + /// + /// @param[in] lib_name The file name of the .so shared library + /// file. + /// + /// @param[in] apkPaths The paths of the APKs that may or may not + /// contain the lib_name file. + /// + /// @param[in] abi The abi of the library, eg, arm64-v8a + /// + virtual void CompleteDartLoadLibrary(intptr_t loading_unit_id, + std::string lib_name, + std::vector& apkPaths, + std::string abi); + + virtual void UpdateAssetManager(std::shared_ptr asset_manager); + protected: PlatformView::Delegate& delegate_; const TaskRunners task_runners_; diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 0f470cff37c6f..1a2c6a02719e3 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -1178,7 +1178,7 @@ void Shell::SetNeedsReportTimings(bool value) { // |Engine::Delegate| std::unique_ptr> Shell::ComputePlatformResolvedLocale( const std::vector& supported_locale_data) { - return ComputePlatformViewResolvedLocale(supported_locale_data); + return platform_view_->ComputePlatformResolvedLocales(supported_locale_data); } // |PlatformView::Delegate| @@ -1188,6 +1188,27 @@ Shell::ComputePlatformViewResolvedLocale( return platform_view_->ComputePlatformResolvedLocales(supported_locale_data); } +void Shell::CompleteDartLoadLibrary(intptr_t loading_unit_id, + std::string lib_name, + std::vector& apkPaths, + std::string abi) { + engine_->CompleteDartLoadLibrary(loading_unit_id, lib_name, apkPaths, abi); +} + +void Shell::UpdateAssetManager(std::shared_ptr asset_manager) { + engine_->UpdateAssetManager(std::move(asset_manager)); +} + +// |Engine::Delegate| +Dart_Handle Shell::OnDartLoadLibrary(intptr_t loading_unit_id) { + return OnPlatformViewDartLoadLibrary(loading_unit_id); +} + +// |PlatformView::Delegate| +Dart_Handle Shell::OnPlatformViewDartLoadLibrary(intptr_t loading_unit_id) { + return platform_view_->OnDartLoadLibrary(loading_unit_id); +} + void Shell::ReportTimings() { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); diff --git a/shell/common/shell.h b/shell/common/shell.h index 8e8fd18b48b3b..ecdebe2e1d80d 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -36,6 +36,7 @@ #include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/shell_io_manager.h" +#include "third_party/dart/runtime/include/dart_api.h" namespace flutter { @@ -511,6 +512,18 @@ class Shell final : public PlatformView::Delegate, std::unique_ptr> ComputePlatformViewResolvedLocale( const std::vector& supported_locale_data) override; + // |PlatformView::Delegate| + Dart_Handle OnPlatformViewDartLoadLibrary(intptr_t loading_unit_id) override; + + // |PlatformView::Delegate| + void CompleteDartLoadLibrary(intptr_t loading_unit_id, + std::string lib_name, + std::vector& apkPaths, + std::string abi) override; + + // |PlatformView::Delegate| + void UpdateAssetManager(std::shared_ptr asset_manager) override; + // |Animator::Delegate| void OnAnimatorBeginFrame(fml::TimePoint frame_target_time) override; @@ -552,6 +565,9 @@ class Shell final : public PlatformView::Delegate, std::unique_ptr> ComputePlatformResolvedLocale( const std::vector& supported_locale_data) override; + // |Engine::Delegate| + Dart_Handle OnDartLoadLibrary(intptr_t loading_unit_id) override; + // |Rasterizer::Delegate| void OnFrameRasterized(const FrameTiming&) override; diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 91d2a368b1057..1e545cc288db9 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -155,6 +155,8 @@ android_java_sources = [ "io/flutter/embedding/engine/dart/DartExecutor.java", "io/flutter/embedding/engine/dart/DartMessenger.java", "io/flutter/embedding/engine/dart/PlatformMessageHandler.java", + "io/flutter/embedding/engine/dynamicfeatures/DynamicFeatureManager.java", + "io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManager.java", "io/flutter/embedding/engine/loader/ApplicationInfoLoader.java", "io/flutter/embedding/engine/loader/FlutterApplicationInfo.java", "io/flutter/embedding/engine/loader/FlutterLoader.java", diff --git a/shell/platform/android/embedding_bundle/build.gradle b/shell/platform/android/embedding_bundle/build.gradle index 7a60d4c16a24c..a1e4f59a4b484 100644 --- a/shell/platform/android/embedding_bundle/build.gradle +++ b/shell/platform/android/embedding_bundle/build.gradle @@ -48,6 +48,8 @@ android { embedding "androidx.lifecycle:lifecycle-common:$lifecycle_version" embedding "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" + embedding "com.google.android.play:core:1.8.0" + // Testing // TODO(xster): remove these android-all compile time dependencies. // Use https://github.com/robolectric/robolectric/blob/master/robolectric/src/main/java/org/robolectric/plugins/LegacyDependencyResolver.java#L24 diff --git a/shell/platform/android/io/flutter/app/FlutterApplication.java b/shell/platform/android/io/flutter/app/FlutterApplication.java index a211c268548cd..1c59cad44316d 100644 --- a/shell/platform/android/io/flutter/app/FlutterApplication.java +++ b/shell/platform/android/io/flutter/app/FlutterApplication.java @@ -5,8 +5,10 @@ package io.flutter.app; import android.app.Activity; +import android.content.Context; import android.app.Application; import androidx.annotation.CallSuper; +import com.google.android.play.core.splitcompat.SplitCompat; import io.flutter.FlutterInjector; /** @@ -33,4 +35,12 @@ public Activity getCurrentActivity() { public void setCurrentActivity(Activity mCurrentActivity) { this.mCurrentActivity = mCurrentActivity; } + + // This override allows split dynamic feature modules to work. + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + // Emulates installation of future on demand modules using SplitCompat. + SplitCompat.install(this); + } } diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index 2e001e7a73965..a4e845aad7317 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -12,6 +12,8 @@ import io.flutter.FlutterInjector; import io.flutter.Log; import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.embedding.engine.dynamicfeatures.DynamicFeatureManager; +import io.flutter.embedding.engine.dynamicfeatures.PlayStoreDynamicFeatureManager; import io.flutter.embedding.engine.loader.FlutterLoader; import io.flutter.embedding.engine.plugins.PluginRegistry; import io.flutter.embedding.engine.plugins.activity.ActivityControlSurface; @@ -98,6 +100,8 @@ public class FlutterEngine { // Engine Lifecycle. @NonNull private final Set engineLifecycleListeners = new HashSet<>(); + @NonNull private DynamicFeatureManager dynamicFeatureManager; + @NonNull private final EngineLifecycleListener engineLifecycleListener = new EngineLifecycleListener() { @@ -299,6 +303,11 @@ public FlutterEngine( flutterJNI.addEngineLifecycleListener(engineLifecycleListener); flutterJNI.setPlatformViewsController(platformViewsController); flutterJNI.setLocalizationPlugin(localizationPlugin); + + dynamicFeatureManager = new PlayStoreDynamicFeatureManager(context, flutterJNI); + flutterJNI.setDynamicFeatureManager(dynamicFeatureManager); + flutterJNI.setDynamicFeatureContext(context); + attachToJni(); // TODO(mattcarroll): FlutterRenderer is temporally coupled to attach(). Remove that coupling if diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index 5e9864c345bdc..02f9bf78cbd89 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -20,6 +20,7 @@ import io.flutter.Log; import io.flutter.embedding.engine.FlutterEngine.EngineLifecycleListener; import io.flutter.embedding.engine.dart.PlatformMessageHandler; +import io.flutter.embedding.engine.dynamicfeatures.DynamicFeatureManager; import io.flutter.embedding.engine.mutatorsstack.FlutterMutatorsStack; import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener; import io.flutter.embedding.engine.renderer.RenderSurface; @@ -183,6 +184,8 @@ public static native void nativeOnVsync( @Nullable private LocalizationPlugin localizationPlugin; @Nullable private PlatformViewsController platformViewsController; + @Nullable private DynamicFeatureManager dynamicFeatureManager; + @NonNull private final Set engineLifecycleListeners = new CopyOnWriteArraySet<>(); @@ -939,6 +942,108 @@ String[] computePlatformResolvedLocale(@NonNull String[] strings) { // ----- End Localization Support ---- + // ----- Start Dynamic Features Support ---- + + /** Sets the dynamic feature manager that is used to download and install split features. */ + @UiThread + public void setDynamicFeatureManager(@NonNull DynamicFeatureManager dynamicFeatureManager) { + ensureRunningOnMainThread(); + this.dynamicFeatureManager = dynamicFeatureManager; + } + + private Context dynamicFeatureContext; + @UiThread + public void setDynamicFeatureContext(@NonNull Context context) { + ensureRunningOnMainThread(); + this.dynamicFeatureContext = context; + } + + @SuppressWarnings("unused") + @UiThread + public void downloadDynamicFeature(int loadingUnitId) { + String loadingUnitIdResName = dynamicFeatureContext.getResources().getString(dynamicFeatureContext.getResources().getIdentifier("loadingUnit" + loadingUnitId, "string", dynamicFeatureContext.getPackageName())); + downloadDynamicFeature(loadingUnitIdResName, loadingUnitId); + } + + // Called by the engine upon invocation of dart loadLibrary() request + @SuppressWarnings("unused") + @UiThread + public void downloadDynamicFeature(String moduleName, int loadingUnitId) { + dynamicFeatureManager.downloadFeature(moduleName, loadingUnitId); + } + + /** + * This should be called for every loading unit to be loaded into the dart isolate. + * + * abi, libName, and apkPaths are used together to search the installed apks for the + * desired .so library. If not found, soPath may be provided as a fallback if a + * pre-extracted .so exists, especially on older devices with libs compressed in the + * apk. + * + * Successful loading of the dart library also completes the loadLibrary() future + * that triggered the install/load process. + */ + @UiThread + public void loadDartLibrary( + int loadingUnitId, + @NonNull String libName, + @NonNull String[] apkPaths, + @NonNull String abi, + @NonNull String soPath) { + ensureRunningOnMainThread(); + ensureAttachedToNative(); + nativeLoadDartLibrary( + nativePlatformViewId, + loadingUnitId, + libName, + apkPaths, + abi, + soPath); + } + private native void nativeLoadDartLibrary( + long nativePlatformViewId, + int loadingUnitId, + @NonNull String libName, + @NonNull String[] apkPaths, + @NonNull String abi, + @NonNull String soPath); + + + /** + * Specifies a new AssetManager that has access to the dynamic feature's assets in addition + * to the base module's assets. + * + * assetBundlePath is the subdirectory that the flutter assets are stored in. The typical + * value is `flutter_assets`. + */ + @UiThread + public void updateAssetManager( + @NonNull AssetManager assetManager, + @NonNull String assetBundlePath + ) { + ensureRunningOnMainThread(); + ensureAttachedToNative(); + nativeUpdateAssetManager(nativePlatformViewId, assetManager, assetBundlePath); + } + private native void nativeUpdateAssetManager( + long nativePlatformViewId, + @NonNull AssetManager assetManager, + @NonNull String assetBundlePath + ); + + // Called when an install encounters a failure during the Android portion of installing a module. + // When transient is false, new attempts to install will automatically result in same error in + // dart before the request is passed to Android. + @SuppressWarnings("unused") + @UiThread + public void dynamicFeatureInstallFailure(@NonNull String moduleName, int loadingUnitId, @NonNull String error, boolean trans) { + ensureRunningOnMainThread(); + nativeDynamicFeatureInstallFailure(moduleName, loadingUnitId, error, trans); + } + private native void nativeDynamicFeatureInstallFailure(@NonNull String moduleName, int loadingUnitId, @NonNull String error, boolean trans); + + // ----- End Dynamic Features Support ---- + // @SuppressWarnings("unused") @UiThread public void onDisplayPlatformView( diff --git a/shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/DynamicFeatureManager.java b/shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/DynamicFeatureManager.java new file mode 100644 index 0000000000000..0d162b292ea53 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/DynamicFeatureManager.java @@ -0,0 +1,56 @@ +// Copyright 2020 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. + +package io.flutter.embedding.engine.dynamicfeatures; + +// Flutter dynamic feature support is still in early developer preview and should +// not be used in production apps yet. + +/** + * Basic interface that handles downloading and loading of dynamic features. + * + * The Flutter default implementation is PlayStoreDynamicFeatureManager. + * + * The methods here may be called independently or in a sequence one after the other to perform + * a full install cycle of download, load assets, and load dart libs. + * + * A dynamic feature module is uniquely identified by a module name. Each feature module may + * contain one or more loading units, uniquely identified by the loading unit ID. + */ +public interface DynamicFeatureManager { + /** + * Request that the feature module be downloaded and installed. + * + * This method is called when loadLibrary() is called on a dart library. + * Upon completion of download, loadAssets and loadDartLibrary should + * be called to complete the dynamic feature load process. + */ + public abstract void downloadFeature(String moduleName, int loadingUnitId); + + /** + * Extract and load any assets and resources from the module for use by Flutter. + * + * Assets shoud be loaded before the dart library is loaded, as successful loading + * of the dart loading unit indicates the dynamic feature is fully loaded. + * + * Depending on the structure of the feature module, there may or may not be assets + * to extract. + */ + public abstract void loadAssets(String moduleName, int loadingUnitId); + + /** + * Load the .so shared library file into the Dart VM. + * + * Upon successful load of the dart library, the feature corresponding to the + * loadingUnitId is considered finished loading, and the dart future completes. + * Developers are expected to begin using symbols and assets from the feature + * module after completion. + */ + public abstract void loadDartLibrary(String moduleName, int loadingUnitId); + + /** + * Uninstall the specified feature module. + */ + public abstract void uninstallFeature(String moduleName, int loadingUnitId); +} diff --git a/shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManager.java b/shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManager.java new file mode 100644 index 0000000000000..67c649f4d3261 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManager.java @@ -0,0 +1,227 @@ +// Copyright 2020 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. + +package io.flutter.embedding.engine.dynamicfeatures; + +import android.content.Context; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.AssetManager; +import android.os.Build; +import androidx.annotation.NonNull; +import com.google.android.play.core.splitinstall.SplitInstallException; +import com.google.android.play.core.splitinstall.SplitInstallManager; +import com.google.android.play.core.splitinstall.SplitInstallManagerFactory; +import com.google.android.play.core.splitinstall.SplitInstallRequest; +import com.google.android.play.core.splitinstall.SplitInstallSessionState; +import com.google.android.play.core.splitinstall.SplitInstallStateUpdatedListener; +import com.google.android.play.core.splitinstall.model.SplitInstallErrorCode; +import com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus; +import io.flutter.Log; +import io.flutter.embedding.engine.FlutterJNI; +import java.util.HashMap; +import java.util.Map; +import java.util.List; +import java.util.ArrayList; +import java.util.Queue; +import java.util.LinkedList; +import java.io.File; + +/** + * Flutter default implementation of DynamicFeatureManager that downloads dynamic feature modules + * from the Google Play store. + */ +public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager { + private static final String TAG = "flutter"; + + private @NonNull SplitInstallManager splitInstallManager; + private @NonNull Map sessionIdToName; + private @NonNull Map sessionIdToLoadingUnitId; + private @NonNull FlutterJNI flutterJNI; + private @NonNull Context context; + + private FeatureInstallStateUpdatedListener listener; + + private class FeatureInstallStateUpdatedListener implements SplitInstallStateUpdatedListener { + public void onStateUpdate(SplitInstallSessionState state) { + if (sessionIdToName.containsKey(state.sessionId())) { + // TODO(garyq): Add capability to access the state from framework. + switch (state.status()) { + case SplitInstallSessionStatus.FAILED: { + Log.d(TAG, "Module \"" + sessionIdToName.get(state.sessionId()) + "\" (sessionId " + state.sessionId() + ") install failed with " + state.errorCode()); + flutterJNI.dynamicFeatureInstallFailure(sessionIdToName.get(state.sessionId()), sessionIdToLoadingUnitId.get(state.sessionId()), "Module install failed with " + state.errorCode(), true); + sessionIdToName.remove(state.sessionId()); + sessionIdToLoadingUnitId.remove(state.sessionId()); + break; + } + case SplitInstallSessionStatus.INSTALLED: { + Log.d(TAG, "Module \"" + sessionIdToName.get(state.sessionId()) + "\" (sessionId " + state.sessionId() + ") installed successfully."); + loadAssets(sessionIdToName.get(state.sessionId()), sessionIdToLoadingUnitId.get(state.sessionId())); + // We only load dart shared lib for the loading unit id requested. Other loading units (if present) + // in the dynamic feature module are not loaded, but can be loaded by calling again with their + // loading unit id. + loadDartLibrary(sessionIdToName.get(state.sessionId()), sessionIdToLoadingUnitId.get(state.sessionId())); + sessionIdToName.remove(state.sessionId()); + sessionIdToLoadingUnitId.remove(state.sessionId()); + break; + } + case SplitInstallSessionStatus.CANCELED: { + Log.d(TAG, "Module \"" + sessionIdToName.get(state.sessionId()) + "\" (sessionId " + state.sessionId() + ") cancelled"); + sessionIdToName.remove(state.sessionId()); + break; + } + case SplitInstallSessionStatus.CANCELING: { + Log.d(TAG, "Module \"" + sessionIdToName.get(state.sessionId()) + "\" (sessionId " + state.sessionId() + ") canceling"); + sessionIdToName.remove(state.sessionId()); + break; + } + case SplitInstallSessionStatus.PENDING: { + Log.d(TAG, "Module \"" + sessionIdToName.get(state.sessionId()) + "\" (sessionId " + state.sessionId() + ") pending."); + break; + } + case SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION: { + Log.d(TAG, "Module \"" + sessionIdToName.get(state.sessionId()) + "\" (sessionId " + state.sessionId() + ") requires user confirmation."); + break; + } + case SplitInstallSessionStatus.DOWNLOADING: { + Log.d(TAG, "Module \"" + sessionIdToName.get(state.sessionId()) + "\" (sessionId " + state.sessionId() + ") downloading."); + break; + } + case SplitInstallSessionStatus.DOWNLOADED: { + Log.d(TAG, "Module \"" + sessionIdToName.get(state.sessionId()) + "\" (sessionId " + state.sessionId() + ") downloaded."); + break; + } + case SplitInstallSessionStatus.INSTALLING: { + Log.d(TAG, "Module \"" + sessionIdToName.get(state.sessionId()) + "\" (sessionId " + state.sessionId() + ") installing."); + break; + } + default: Log.d(TAG, "Status: " + state.status()); + } + } + } + } + + public PlayStoreDynamicFeatureManager(@NonNull Context context, @NonNull FlutterJNI flutterJNI) { + this.context = context; + this.flutterJNI = flutterJNI; + splitInstallManager = SplitInstallManagerFactory.create(context); + listener = new FeatureInstallStateUpdatedListener(); + splitInstallManager.registerListener(listener); + sessionIdToName = new HashMap(); + sessionIdToLoadingUnitId = new HashMap(); + } + + public void downloadFeature(String moduleName, int loadingUnitId) { + if (moduleName == null) { + Log.e(TAG, "Dynamic feature module name was null."); + return; + } + + SplitInstallRequest request = + SplitInstallRequest + .newBuilder() + .addModule(moduleName) + .build(); + + splitInstallManager + // Submits the request to install the module through the + // asynchronous startInstall() task. Your app needs to be + // in the foreground to submit the request. + .startInstall(request) + // Called when the install request is sent successfully. This is different than a successful install + // which is handled in FeatureInstallStateUpdatedListener. + .addOnSuccessListener(sessionId -> { + this.sessionIdToName.put(sessionId, moduleName); + this.sessionIdToLoadingUnitId.put(sessionId, loadingUnitId); + Log.d(TAG, "Request to install module \"" + moduleName + "\" sent with session id " + sessionId + "."); + }) + .addOnFailureListener(exception -> { + switch(((SplitInstallException) exception).getErrorCode()) { + case SplitInstallErrorCode.NETWORK_ERROR: + Log.d(TAG, "Install of dynamic feature module \"" + moduleName + "\" failed with a network error"); + flutterJNI.dynamicFeatureInstallFailure(moduleName, loadingUnitId, "Install of dynamic feature module \"" + moduleName + "\" failed with a network error", true); + break; + case SplitInstallErrorCode.MODULE_UNAVAILABLE: + Log.d(TAG, "Install of dynamic feature module \"" + moduleName + "\" failed as is unavailable."); + flutterJNI.dynamicFeatureInstallFailure(moduleName, loadingUnitId, "Install of dynamic feature module \"" + moduleName + "\" failed as is unavailable.", false); + break; + default: + Log.d(TAG, "Install of dynamic feature module \"" + moduleName + "\" failed with error: \"" + ((SplitInstallException) exception).getErrorCode() + "\": " + ((SplitInstallException) exception).getMessage()); + flutterJNI.dynamicFeatureInstallFailure(moduleName, loadingUnitId, "Install of dynamic feature module \"" + moduleName + "\" failed with error: \"" + ((SplitInstallException) exception).getErrorCode() + "\": " + ((SplitInstallException) exception).getMessage(), false); + break; + } + }); + } + + public void loadAssets(@NonNull String moduleName, int loadingUnitId) { + try { + context = context.createPackageContext(context.getPackageName(), 0); + + AssetManager assetManager = context.getAssets(); + flutterJNI.updateAssetManager( + assetManager, + // TODO(garyq): Made the "flutter_assets" directory dynamic based off of DartEntryPoint. + "flutter_assets"); + } catch (NameNotFoundException e) { + Log.d(TAG, "NameNotFoundException creating context for " + moduleName); + throw new RuntimeException(e); + } + } + + public void loadDartLibrary(String moduleName, int loadingUnitId) { + // This matches/depends on dart's loading unit naming convention, which we use unchanged. + String aotSharedLibraryName = "app.so-" + loadingUnitId + ".part.so"; + + // Possible values: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips, mips64 + String abi; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + abi = Build.SUPPORTED_ABIS[0]; + } else { + abi = Build.CPU_ABI; + } + String pathAbi = abi.replace("-", "_"); // abis are represented with underscores in paths. + + // TODO(garyq): Optimize this apk/file discovery process to use less i/o and be more + // performant. + + // Search directly in APKs first + List apkPaths = new ArrayList(); + // If not found in APKs, we check in extracted native libs for the lib directly. + String soPath = ""; + Queue searchFiles = new LinkedList(); + searchFiles.add(context.getFilesDir()); + while (!searchFiles.isEmpty()) { + File file = searchFiles.remove(); + if (file != null && file.isDirectory()) { + for (File f : file.listFiles()) { + searchFiles.add(f); + } + continue; + } + String name = file.getName(); + if (name.substring(name.length() - 4).equals(".apk") && name.substring(0, moduleName.length()).equals(moduleName) && name.contains(pathAbi)) { + apkPaths.add(file.getAbsolutePath()); + continue; + } + if (name.equals(aotSharedLibraryName)) { + soPath = file.getAbsolutePath(); + } + } + + flutterJNI.loadDartLibrary( + loadingUnitId, + aotSharedLibraryName, + apkPaths.toArray(new String[apkPaths.size()]), + abi, + soPath); + } + + public void uninstallFeature(String moduleName, int loadingUnitId) { + // TODO(garyq): support uninstalling. + } + + void destroy() { + splitInstallManager.unregisterListener(listener); + } + +} diff --git a/shell/platform/android/jni/jni_mock.h b/shell/platform/android/jni/jni_mock.h index 65f790d972ee8..cf08766ddde13 100644 --- a/shell/platform/android/jni/jni_mock.h +++ b/shell/platform/android/jni/jni_mock.h @@ -95,6 +95,11 @@ class JNIMock final : public PlatformViewAndroidJNI { (override)); MOCK_METHOD(double, GetDisplayRefreshRate, (), (override)); + + MOCK_METHOD(bool, + FlutterViewDownloadDynamicFeature, + (int loading_unit_id), + (override)); }; } // namespace flutter diff --git a/shell/platform/android/jni/platform_view_android_jni.h b/shell/platform/android/jni/platform_view_android_jni.h index e06079c1f5c4f..1212ab82aa887 100644 --- a/shell/platform/android/jni/platform_view_android_jni.h +++ b/shell/platform/android/jni/platform_view_android_jni.h @@ -195,6 +195,8 @@ class PlatformViewAndroidJNI { std::vector supported_locales_data) = 0; virtual double GetDisplayRefreshRate() = 0; + + virtual bool FlutterViewDownloadDynamicFeature(int loading_unit_id) = 0; }; } // namespace flutter diff --git a/shell/platform/android/platform_view_android.cc b/shell/platform/android/platform_view_android.cc index 8ebac2aa929b9..7103444ac8efb 100644 --- a/shell/platform/android/platform_view_android.cc +++ b/shell/platform/android/platform_view_android.cc @@ -342,6 +342,29 @@ PlatformViewAndroid::ComputePlatformResolvedLocales( supported_locale_data); } +// |PlatformView| +Dart_Handle PlatformViewAndroid::OnDartLoadLibrary(intptr_t loading_unit_id) { + if (jni_facade_->FlutterViewDownloadDynamicFeature(loading_unit_id)) { + return Dart_Null(); + } + return Dart_Null(); // TODO(garyq): RETURN ERROR +} + +// |PlatformView| +void PlatformViewAndroid::CompleteDartLoadLibrary( + intptr_t loading_unit_id, + std::string lib_name, + std::vector& apkPaths, + std::string abi) { + delegate_.CompleteDartLoadLibrary(loading_unit_id, lib_name, apkPaths, abi); +} + +// |PlatformView| +void PlatformViewAndroid::UpdateAssetManager( + std::shared_ptr asset_manager) { + delegate_.UpdateAssetManager(std::move(asset_manager)); +} + void PlatformViewAndroid::InstallFirstFrameCallback() { // On Platform Task Runner. SetNextFrameCallback( diff --git a/shell/platform/android/platform_view_android.h b/shell/platform/android/platform_view_android.h index b1bf8194976f5..430b947b992e6 100644 --- a/shell/platform/android/platform_view_android.h +++ b/shell/platform/android/platform_view_android.h @@ -96,6 +96,15 @@ class PlatformViewAndroid final : public PlatformView { int64_t texture_id, const fml::jni::JavaObjectWeakGlobalRef& surface_texture); + // |PlatformView| + void CompleteDartLoadLibrary(intptr_t loading_unit_id, + std::string lib_name, + std::vector& apkPaths, + std::string abi) override; + + // |PlatformView| + void UpdateAssetManager(std::shared_ptr asset_manager) override; + private: const std::shared_ptr jni_facade_; std::shared_ptr external_view_embedder_; @@ -137,6 +146,9 @@ class PlatformViewAndroid final : public PlatformView { std::unique_ptr> ComputePlatformResolvedLocales( const std::vector& supported_locale_data) override; + // |PlatformView| + Dart_Handle OnDartLoadLibrary(intptr_t loading_unit_id) override; + void InstallFirstFrameCallback(); void FireFirstFrameCallback(); diff --git a/shell/platform/android/platform_view_android_jni_impl.cc b/shell/platform/android/platform_view_android_jni_impl.cc index 084872a023313..df506c9dd7d89 100644 --- a/shell/platform/android/platform_view_android_jni_impl.cc +++ b/shell/platform/android/platform_view_android_jni_impl.cc @@ -12,6 +12,7 @@ #include "flutter/assets/directory_asset_bundle.h" #include "flutter/common/settings.h" +#include "flutter/fml/command_line.h" #include "flutter/fml/file.h" #include "flutter/fml/platform/android/jni_util.h" #include "flutter/fml/platform/android/jni_weak_ref.h" @@ -19,7 +20,9 @@ #include "flutter/fml/size.h" #include "flutter/lib/ui/plugins/callback_cache.h" #include "flutter/runtime/dart_service_isolate.h" +#include "flutter/runtime/dart_snapshot.h" #include "flutter/shell/common/run_configuration.h" +#include "flutter/shell/common/switches.h" #include "flutter/shell/platform/android/android_external_texture_gl.h" #include "flutter/shell/platform/android/android_shell_holder.h" #include "flutter/shell/platform/android/apk_asset_provider.h" @@ -100,6 +103,8 @@ static jmethodID g_detach_from_gl_context_method = nullptr; static jmethodID g_compute_platform_resolved_locale_method = nullptr; +static jmethodID g_download_dynamic_feature_method = nullptr; + // Called By Java static jmethodID g_on_display_platform_view_method = nullptr; @@ -508,6 +513,52 @@ static jboolean FlutterTextUtilsIsRegionalIndicator(JNIEnv* env, jint codePoint) { return u_hasBinaryProperty(codePoint, UProperty::UCHAR_REGIONAL_INDICATOR); } + +static void LoadDartLibrary(JNIEnv* env, + jobject obj, + jlong shell_holder, + jint jLoadingUnitId, + jstring jLibName, + jobjectArray jApkPaths, + jstring jAbi, + jstring jSoPath) { + std::string abi = fml::jni::JavaStringToString(env, jAbi); + + std::vector apkPaths = + fml::jni::StringArrayToVector(env, jApkPaths); + + ANDROID_SHELL_HOLDER->GetPlatformView()->CompleteDartLoadLibrary( + static_cast(jLoadingUnitId), + fml::jni::JavaStringToString(env, jLibName), apkPaths, abi); + + // TODO(garyq): fallback on soPath. +} + +static void UpdateAssetManager(JNIEnv* env, + jobject obj, + jlong shell_holder, + jobject jAssetManager, + jstring jAssetBundlePath) { + auto asset_manager = std::make_shared(); + asset_manager->PushBack(std::make_unique( + env, // jni environment + jAssetManager, // asset manager + fml::jni::JavaStringToString(env, jAssetBundlePath)) // apk asset dir + ); + + ANDROID_SHELL_HOLDER->GetPlatformView()->UpdateAssetManager( + std::move(asset_manager)); +} + +static void DynamicFeatureInstallFailure(JNIEnv* env, + jobject obj, + jobject moduleName, + jint loadigUnitId, + jobject error, + jboolean transient) { + // TODO(garyq): Implement +} + bool RegisterApi(JNIEnv* env) { static const JNINativeMethod flutter_jni_methods[] = { // Start of methods from FlutterJNI @@ -664,6 +715,23 @@ bool RegisterApi(JNIEnv* env) { .fnPtr = reinterpret_cast(&FlutterTextUtilsIsRegionalIndicator), }, + { + .name = "nativeLoadDartLibrary", + .signature = "(JILjava/lang/String;[Ljava/lang/String;Ljava/lang/" + "String;Ljava/lang/String;)V", + .fnPtr = reinterpret_cast(&LoadDartLibrary), + }, + { + .name = "nativeUpdateAssetManager", + .signature = + "(JLandroid/content/res/AssetManager;Ljava/lang/String;)V", + .fnPtr = reinterpret_cast(&UpdateAssetManager), + }, + { + .name = "nativeDynamicFeatureInstallFailure", + .signature = "(Ljava/lang/String;ILjava/lang/String;Z)V", + .fnPtr = reinterpret_cast(&DynamicFeatureInstallFailure), + }, }; if (env->RegisterNatives(g_flutter_jni_class->obj(), flutter_jni_methods, @@ -907,6 +975,14 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { return false; } + g_download_dynamic_feature_method = env->GetMethodID( + g_flutter_jni_class->obj(), "downloadDynamicFeature", "(I)V"); + + if (g_download_dynamic_feature_method == nullptr) { + FML_LOG(ERROR) << "Could not locate downloadDynamicFeature method"; + return false; + } + return RegisterApi(env); } @@ -1334,4 +1410,20 @@ double PlatformViewAndroidJNIImpl::GetDisplayRefreshRate() { return static_cast(env->GetStaticFloatField(clazz, fid)); } +bool PlatformViewAndroidJNIImpl::FlutterViewDownloadDynamicFeature( + int loading_unit_id) { + JNIEnv* env = fml::jni::AttachCurrentThread(); + + auto java_object = java_object_.get(env); + if (java_object.is_null()) { + return true; + } + + env->CallObjectMethod(java_object.obj(), g_download_dynamic_feature_method, + loading_unit_id); + + FML_CHECK(CheckException(env)); + return true; +} + } // namespace flutter diff --git a/shell/platform/android/platform_view_android_jni_impl.h b/shell/platform/android/platform_view_android_jni_impl.h index 313a9ee921e6c..3f0942d1606e7 100644 --- a/shell/platform/android/platform_view_android_jni_impl.h +++ b/shell/platform/android/platform_view_android_jni_impl.h @@ -80,6 +80,8 @@ class PlatformViewAndroidJNIImpl final : public PlatformViewAndroidJNI { double GetDisplayRefreshRate() override; + bool FlutterViewDownloadDynamicFeature(int loading_unit_id) override; + private: // Reference to FlutterJNI object. const fml::jni::JavaObjectWeakGlobalRef java_object_; diff --git a/testing/dart_isolate_runner.cc b/testing/dart_isolate_runner.cc index 43d77c0165c28..95c6a1e047d60 100644 --- a/testing/dart_isolate_runner.cc +++ b/testing/dart_isolate_runner.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "flutter/testing/dart_isolate_runner.h" +#include "flutter/runtime/dart_deferred_load_handler.h" #include "flutter/runtime/isolate_configuration.h" @@ -110,18 +111,19 @@ std::unique_ptr RunDartCodeInIsolateOnUITaskRunner( auto isolate = DartIsolate::CreateRunningRootIsolate( - settings, // settings - vm_data->GetIsolateSnapshot(), // isolate snapshot - std::move(task_runners), // task runners - nullptr, // window - {}, // snapshot delegate - {}, // hint freed delegate - io_manager, // io manager - {}, // unref queue - {}, // image decoder - "main.dart", // advisory uri - entrypoint.c_str(), // advisory entrypoint - DartIsolate::Flags{}, // flags + settings, // settings + vm_data->GetIsolateSnapshot(), // isolate snapshot + std::move(task_runners), // task runners + nullptr, // window + {}, // snapshot delegate + {}, // hint freed delegate + io_manager, // io manager + {}, // unref queue + {}, // image decoder + "main.dart", // advisory uri + entrypoint.c_str(), // advisory entrypoint + DartIsolate::Flags{}, // flags + DartDeferredLoadHandler::empty_dart_deferred_load_handler, settings.isolate_create_callback, // isolate create callback settings.isolate_shutdown_callback, // isolate shutdown callback entrypoint, // entrypoint