-
Notifications
You must be signed in to change notification settings - Fork 6k
Split AOT Android Embedder and shell #22179
Conversation
6883948
to
d2eccae
Compare
b515a4b
to
339d75c
Compare
...form/android/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManager.java
Outdated
Show resolved
Hide resolved
...form/android/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManager.java
Show resolved
Hide resolved
jobject jAssetManager, | ||
jstring jAssetBundlePath) { | ||
auto asset_manager = std::make_shared<flutter::AssetManager>(); | ||
asset_manager->PushBack(std::make_unique<flutter::APKAssetProvider>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this is an issue, but this will potentially lose any other asset resolvers that were in the old asset manager (such as directories holding assets that were pushed during a hot reload)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will look into this!
shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
Outdated
Show resolved
Hide resolved
shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java
Outdated
Show resolved
Hide resolved
|
||
public void loadAssets(@NonNull String moduleName, int loadingUnitId) { | ||
try { | ||
context = context.createPackageContext(context.getPackageName(), 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this intentionally overwriting the context that was passed into the constructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. This context overwrite ensures that the newly installed dynamic feature module is present in the context when we ask for a new asset manager.
3f65733
to
bc6f047
Compare
@@ -469,7 +469,7 @@ deps = { | |||
'packages': [ | |||
{ | |||
'package': 'flutter/android/embedding_bundle', | |||
'version': 'last_updated:2020-05-20T01:36:16-0700' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is all fine for a local build but you still need to update
engine/shell/platform/android/BUILD.gn
Line 394 in 6166712
script = "//flutter/tools/androidx/generate_pom_file.py" |
i.e. I don't think this will compile on LUCI. @jason-simmons might know more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The LUCI tests were able to all pass on this PR this afternoon, I'll take a look at updating it in the above mentioned spot too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it'll pass on LUCI since the tests will run in the "development path". But I think it won't package up the dependencies in the vendored engine build on cloud storage.
shell/common/engine.cc
Outdated
|
||
// // |RuntimeDelegate| | ||
// Dart_Handle Engine::OnDartLoadLibrary(intptr_t loading_unit_id) { | ||
// return delegate_.OnDartLoadLibrary(loading_unit_id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I'm just reading in order so comments might not be relevant as I read the rest of the files)
Don't know what delegate is. Just a reminder to make sure this is designed generically such that it can fit through the embedder.h API in the future for other platforms.
shell/common/engine.h
Outdated
/// @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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the right abstraction to expect the embedder/embeddings to construct a Dart_Handle instance? What would you envision them to fill it with? Does it need an indirection/wrapping that's more accessible to the embedder/embeddings?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll just abstract away/handle the dart_handles. This is left over from when I was doing more with the results in experiments.
/// 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should clarify at this level what are we loading specifically? Is it the Dart specific loading units or is it the platform-specific bundles? We should be exact with the name. i.e. in the asset-only bundle case, that asset's still loaded through the Dart API or is it a different API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will be adding separate dart API for loading by android module name. This is currently strictly for the dart loadLibrary() code path. I'll link to additional info in a separate PR introducing the new API for module name/asset only loading.
// Called when the install request is sent successfully. This is different than a successful | ||
// install which is handled in FeatureInstallStateUpdatedListener. | ||
.addOnSuccessListener( | ||
sessionId -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jason-simmons you sure we should start allowing java 8 usages in our engine? We'd be forcing our users to upgrade if they were still on java 7?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The engine embedder code is already using Java lambdas, and the engine build scripts link to an Android lambda support library
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh interesting. You're saying there are ways to support this without java 8?
...form/android/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManager.java
Show resolved
Hide resolved
|
||
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"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if we can hardcode this name. We probably want to fix flutter/flutter#42214 at some point. I think there are other dupes but I can't find them. Also is this name right for google3?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the "app" in this name is not in reference to the android dir "app" module. It is the default .so name outputted by gen_snapshot. @rmacnak-google can you confirm?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In any case, I can come up with any naming scheme, this will likely change in the future as the tooling becomes more complete.
// 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]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is apparently the recommended way to obtain the ABI in lollipop+. The other way was deprecated in favor of this.
flutterJNI.loadDartLibrary( | ||
loadingUnitId, | ||
aotSharedLibraryName, | ||
apkPaths.toArray(new String[apkPaths.size()]), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whatever's reading specific files out of the APK, we should do it here in this file or at least somewhere in java since it's android specific.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(will move to android-engine, see above comments)
12819ed
to
a65913b
Compare
Tagging flutter/flutter#57617 |
65ce657
to
587d084
Compare
Let me know when you're ready for another round. |
Yep, working on getting the asset manager to persist the other asset resolvers instead of overwriting it. Will let you know when ready |
ca22807
to
1a51a4b
Compare
cc @xster should be ready for another round! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, reviewed halfway. Will finish the rest tonight. There's also still some leftover comments from last time it seems.
@@ -469,7 +469,7 @@ deps = { | |||
'packages': [ | |||
{ | |||
'package': 'flutter/android/embedding_bundle', | |||
'version': 'last_updated:2020-05-20T01:36:16-0700' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it'll pass on LUCI since the tests will run in the "development path". But I think it won't package up the dependencies in the vendored engine build on cloud storage.
shell/common/engine.h
Outdated
@@ -767,6 +777,27 @@ class Engine final : public RuntimeDelegate, | |||
/// | |||
const std::string& InitialRoute() const { return initial_route_; } | |||
|
|||
//-------------------------------------------------------------------------- | |||
/// @brief Loads the dart shared library into the dart VM. When the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the verb right? Does the implementation of this function execute the act of "loading" or does it just signal that it can be consumed now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it depends on what you would consider "loading". If you consider dlopen-ing a file and resolving symbols loading, then indeed it just passes the results of that on. On the other hand, this method does directly call Dart_DeferredLoadComplete, which "loads" the symbols into the running isolate. In this case, this method does invoke loading with the provided symbols.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ya, I just realized my understanding was incorrect. I think in your diagram, there's actually another bubble somewhere in the Runtime/VM box that's for "consuming the bytes than represent the AOT program" like how you have a small bubble for dlopen etc.
In that case, maybe this function should just be called loadDartDeferredLibrary too since the implementation behind this function (the vm) loads the program given to it.
shell/common/engine.h
Outdated
/// @brief Loads the dart shared library into the dart VM. When the | ||
/// dart library is loaded successfully, the dart future | ||
/// returned by the originating loadLibrary() call completes. | ||
/// Each shared library is a loading unit, which consists of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't understand what this sentence meant to communicate :) It describes the what but the why might be missing
/// deferred libraries that can be compiled split from the | ||
/// base dart library by gen_snapshot. | ||
/// | ||
/// @param[in] loading_unit_id The unique id of the deferred library's |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explicitly state this loading unit id's relationship with the previous request call? Or are they independent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You didn't address this one. The 2 functions' docs should explicitly cross-reference each other.
shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/DynamicFeatureManager.java
Outdated
Show resolved
Hide resolved
shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/DynamicFeatureManager.java
Outdated
Show resolved
Hide resolved
* loadingUnitId or moduleName must be valid or non-null. | ||
* | ||
* @param loadingUnitId The unique identifier associated with a dart deferred library. This id is | ||
* assigned by gen_snapshot and can be referenced via bundle_config.yaml. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reference the front facing flutter build rather than gen_snapshot.
* Passing a loadingUnitId larger than the highest valid loading unit's id will | ||
* cause the dart loadLibrary() to complete with a failure. | ||
* | ||
* @param moduleName The dynamic feature module name as defined in bundle_config.yaml. Flutter's |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Be a bit more explicit in terms of who would call these variants specifically in what combination. i.e. the dart side API loadLibrary leads to a call with a loadingUnitId value based on what output file from gen_snapshot? We currently expect the Java application itself (or through a plugin) to call the variant with moduleName. Who calls with both parameters filled?
* cause the dart loadLibrary() to complete with a failure. | ||
* | ||
* @param moduleName The dynamic feature module name as defined in bundle_config.yaml. Flutter's | ||
* default dynamic feature system stores a mapping of loadingUnitId -> moduleName |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what the subclasser should do with this sentence. What are we instructing the implementer to do?
afc55e8
to
47d9229
Compare
/** | ||
* Extract and load any assets and resources from the module for use by Flutter. | ||
* | ||
* <p>Assets shoud be loaded before the dart derferred library is loaded, as successful loading of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo
* however, the two parameters are still present for custom implementations that store assets | ||
* outside of Android's native system. | ||
* | ||
* @param loadingUnitId The unique identifier associated with a dart deferred library. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add another paragraph to describe who should call this
* <p>Assets shoud be loaded before the dart derferred library is loaded, as successful loading of | ||
* the dart loading unit indicates the dynamic feature is fully loaded. | ||
* | ||
* <p>Depending on the structure of the feature module, there may or may not be assets to extract. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Up until this point, it's a bit vague in terms of what the implementer's minimum responsibility should be. i.e. what new assumptions does the engine make after you call loadDartDeferredLibrary through JNI again? Seems like it's just updateAssetManager. If so, make it explicit.
* modules do not have an associated loadingUnitId. Instead, an invalid ID like -1 may be passed to | ||
* download only with moduleName. On the other hand, it can be possible to resolve the moduleName based | ||
* on the loadingUnitId. This resolution is done if moduleName is null. At least one of | ||
* loadingUnitId or moduleName must be valid or non-null. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's not very clear what the developer implementer should do
...form/android/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManager.java
Outdated
Show resolved
Hide resolved
} | ||
|
||
private String loadingUnitIdToModuleName(int loadingUnitId) { | ||
// Loading unit id to module name mapping stored in android Strings |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it? How does that happen? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is stored by the tooling in the build process. This may change if/when I come up with a better way to accomplish this.
exception -> { | ||
switch (((SplitInstallException) exception).getErrorCode()) { | ||
case SplitInstallErrorCode.NETWORK_ERROR: | ||
flutterJNI.dynamicFeatureInstallFailure( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this overlap with the SplitInstallStateUpdatedListener callback above? i.e. could dynamicFeatureInstallFailure happen twice per request?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, these errors indicate the install request was never registered/started. The errors above indicate a properly fired request has failed.
} | ||
|
||
public void uninstallFeature(int loadingUnitId, String moduleName) { | ||
// TODO(garyq): support uninstalling. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you're not supporting it, let's not add it to the interface yet?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just wanted to clarify that the TODOs I'm adding in this PR are meant to be actually implemented shortly. They usually indicate critical components of the feature that may just be a bit too big or out of scope for a single PR, and will be landed before the feature is considered ready.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SG
handle = ::dlopen(path.c_str(), RTLD_NOW); | ||
search_paths.pop_back(); | ||
if (handle == nullptr) { | ||
FML_LOG(ERROR) << "No dart shared library found at \"" << path |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are we expecting failures for the first entries of the searchpaths? i.e. is this spammy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do expect some failures, I'll remove this log.
|
||
// Resolve symbols. | ||
uint8_t* isolate_data = | ||
static_cast<uint8_t*>(::dlsym(handle, DartSnapshot::kIsolateDataSymbol)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we're going through all the effort of recreating this, it's then not clear why aren't we just using the fml library to start with?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I currently still want explicit control over this process as there are minor nuances that are done differently, I expect to refactor this to use existing FML once the process is more locked down.
I think all my comments are minor. Generally LGTM. I think this PR's ready for tests |
d0b4b1c
to
e2c5858
Compare
Fit x tests
Implement injector
0c71f71
to
b8cfc01
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Woohoo!
@@ -5,7 +5,9 @@ | |||
package io.flutter; | |||
|
|||
import android.support.annotation.NonNull; | |||
import android.support.annotation.Nullable; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I made a mistake here. If you sync to head, these should be androidx dependencies. The new one should be androidx too.
} | ||
|
||
public void destroy() { | ||
splitInstallManager.unregisterListener(listener); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
while you're here, might as well remove the JNI reference. i.e. during the destruction sequence, there is no guarantee that there won't be a race where a load asset platform channel call lands right as some items are being destroyed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You didn't address this one.
@@ -374,6 +376,7 @@ public void destroy() { | |||
platformViewsController.onDetachedFromJNI(); | |||
dartExecutor.onDetachedFromJNI(); | |||
flutterJNI.removeEngineLifecycleListener(engineLifecycleListener); | |||
flutterJNI.setDynamicFeatureManager(null); | |||
flutterJNI.detachFromNativeAndReleaseResources(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if FlutterInjector.instance().dynamicFeatureManager() is not null, call destroy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You didn't address this one. I think nothing's calling DynamicFeatureManager.destroy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Github UI didn't register this one changing as well, see lines below.
public void requestDartDeferredLibrary(int loadingUnitId) { | ||
if (dynamicFeatureManager != null) { | ||
dynamicFeatureManager.downloadDynamicFeature(loadingUnitId, null); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is where the rubber meets the road, let's show a scary message here so it's easier to debug. i.e. you used a split aot feature by calling the dart loadLibrary function but didn't set a manager. Please see integration doc: .... Split AOT will not work unless you complete integration from native side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You didn't address this one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did, the Github UI just didn't register it as outdated since the comment is on a }
:)
* loading Dart deferred libraries. A typical code-flow begins with a Dart call to loadLibrary() on | ||
* deferred imported library. See https://dart.dev/guides/language/language-tour#deferred-loading | ||
* This call retrieves a unique identifier called the loading unit id, which is assigned by | ||
* gen_snapshot during compilation. The loading unit id is passed down through the engine and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add a line to say where the user should look to find that id
* <p>Flutter dynamic feature support is still in early developer preview and should not be used in | ||
* production apps yet. | ||
* | ||
* <p>The Flutter default implementation is PlayStoreDynamicFeatureManager. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit (mainly for this class because it's our "main" java doc): use {@link ...}
everywhere in this file when referring to other java classes. Use full qualifiers to avoid class imports if needed. Use {@code ...}
to refer to non-java code.
{@link #method...}
can reference class methods
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'll go ahead and go through and add all the links.
* is fully initialized, this method should be called to provide the FlutterJNI instance to use | ||
* for use in loadDartLibrary and loadAssets. | ||
*/ | ||
public abstract void setJNI(FlutterJNI flutterJNI); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yak creep / optional: one way you can get around this is letting the FlutterJNI be in the injector too. Then you can just read from the injector right before use.
*/ | ||
public abstract void uninstallFeature(int loadingUnitId, String moduleName); | ||
|
||
/** Destructor that cleans up any leftover objects that are no longer needed. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit on technical wording: Java doesn't have destructors (something that immediately prompts for memory to be release) since it's garbage collected. The best we can say is calling this releases all its resources. After calling this, this object is no longer usable.
|
||
private boolean verifyJNI() { | ||
if (flutterJNI == null) { | ||
Log.e(TAG, "No FlutterJNI provided."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this appears without context. Be a bit more detailed. You must set this to integrate split modules before calling dart apis or loading assets via platform channel.
* 53fc019 Split AOT Android Embedder and shell (flutter/engine#22179) * fc55814 Implement Scene.toImage() in CanvasKit mode. (flutter/engine#22085) * c45e02a Roll Dart SDK from 12fded61a2bc to a06d469024fd (1 revision) (flutter/engine#22623) * 550c750 Remove opt outs for dart:ui (flutter/engine#22603) * f2803ac [fuchsia] shader warmup fixes (flutter/engine#22439) * ce94c4e Roll Dart SDK from a06d469024fd to b8fea79a2549 (1 revision) (flutter/engine#22630) * 76b6acb Roll Fuchsia Linux SDK from aAb3NJv_h... to X1ue-JZsc... (flutter/engine#22631) * 976e887 Roll Skia from ed289e777cfa to 9dce4d081f8a (3 revisions) (flutter/engine#22632) * 885bd65 Roll Fuchsia Mac SDK from DQpWjEN59... to wGZWtwuY4... (flutter/engine#22633) * 8971b82 Roll Dart SDK from b8fea79a2549 to 861ebcb175b6 (1 revision) (flutter/engine#22634) * a09cdfd Roll Skia from 9dce4d081f8a to 8c5889937172 (1 revision) (flutter/engine#22635) * a9f332c Roll Dart SDK from 861ebcb175b6 to 1adf3d5fa9d0 (1 revision) (flutter/engine#22636) * 1bf5c8b [web] Implement tilemode for gradient shaders. (flutter/engine#22597) * 97cacfb Add more runtime intrinsic symbols to the export checker script (flutter/engine#22641)
Description
go/flutter-aot-split
go/flutter-split-aot-instructions
For flutter/flutter#57617 and flutter/flutter#62229
This PR contains android embedder and engine shell implementations of Split AOT that handles downloading the dynamic feature module, and passing the necessary data to the runtime in order to load a dart shared library.
In the android embedder, we add DynamicFeatureManager, which provides the general API for downloading dynamic features. The flutter play store default implementation is PlayStoreDynamicFeatureManager, which interfaces with the play store to download the APK as well as load assets and load the split dart library. Third party custom implementations of DynamicFeatureManager may be provided to FlutterJNI or the FlutterEngine java class to enable custom download and load behavior.
The shell code passes down the request to download a library via PlatformView to the embedder, as well as passes the APK paths up to the runtime controller to find the lib and load it.
The boundary of this PR is engine.cc/h, where the code that connects it to the runtime implementation (in a future PR, preliminary version can be viewed at #21173) resides. I will be landing the runtime portion of this feature after more work is done on it.
The API in this PR is not meant to be permanent, but rather a concrete working step towards fully functional split AOT apps as it gets landed/developed over next few weeks.
The WIP tooling that generates the AAB bundles can be found at flutter/flutter#63773
Tests
Tests for PlayStoreDynamicFeatureManager and FlutterInjector. Shell tests need full shell implementation before they can work.