From e4fa163b511465719946fecff300b1a374616fd8 Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Thu, 7 Jan 2021 22:02:05 -0700 Subject: [PATCH] Rename DynamicFeature->DeferredComponent and impl uninstall DeferredComponents (flutter/engine#23224) --- .../ci/licenses_golden/licenses_flutter | 6 +- .../flutter/shell/platform/android/BUILD.gn | 10 +- .../android/io/flutter/FlutterInjector.java | 25 +-- .../app/FlutterPlayStoreSplitApplication.java | 18 ++- .../embedding/engine/FlutterEngine.java | 32 ++-- .../flutter/embedding/engine/FlutterJNI.java | 39 ++--- .../DeferredComponentManager.java} | 144 ++++++++++-------- .../PlayStoreDeferredComponentManager.java} | 77 ++++++---- ...nel.java => DeferredComponentChannel.java} | 58 ++++--- .../android/platform_view_android_jni_impl.cc | 14 +- .../test/io/flutter/FlutterInjectorTest.java | 14 +- .../test/io/flutter/FlutterTestSuite.java | 6 +- ...layStoreDeferredComponentManagerTest.java} | 66 ++++---- .../DeferredComponentChannelTest.java | 123 +++++++++++++++ .../DynamicFeatureChannelTest.java | 115 -------------- 15 files changed, 406 insertions(+), 341 deletions(-) rename engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/{dynamicfeatures/DynamicFeatureManager.java => deferredcomponents/DeferredComponentManager.java} (58%) rename engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/{dynamicfeatures/PlayStoreDynamicFeatureManager.java => deferredcomponents/PlayStoreDeferredComponentManager.java} (82%) rename engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/{DynamicFeatureChannel.java => DeferredComponentChannel.java} (60%) rename engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/{dynamicfeatures/PlayStoreDynamicFeatureManagerTest.java => deferredcomponents/PlayStoreDeferredComponentManagerTest.java} (68%) create mode 100644 engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/systemchannels/DeferredComponentChannelTest.java delete mode 100644 engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/systemchannels/DynamicFeatureChannelTest.java diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index 0a9309d8c38..786a7377d79 100755 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -757,8 +757,8 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/Flutte FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/DartMessenger.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java -FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/DynamicFeatureManager.java -FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManager.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/DeferredComponentManager.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/loader/ApplicationInfoLoader.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterApplicationInfo.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java @@ -788,7 +788,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/render FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/RenderSurface.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureWrapper.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java -FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/DynamicFeatureChannel.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/DeferredComponentChannel.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/LocalizationChannel.java diff --git a/engine/src/flutter/shell/platform/android/BUILD.gn b/engine/src/flutter/shell/platform/android/BUILD.gn index 9aa06b75477..0a2189a8e3a 100644 --- a/engine/src/flutter/shell/platform/android/BUILD.gn +++ b/engine/src/flutter/shell/platform/android/BUILD.gn @@ -157,8 +157,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/deferredcomponents/DeferredComponentManager.java", + "io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java", "io/flutter/embedding/engine/loader/ApplicationInfoLoader.java", "io/flutter/embedding/engine/loader/FlutterApplicationInfo.java", "io/flutter/embedding/engine/loader/FlutterLoader.java", @@ -188,7 +188,7 @@ android_java_sources = [ "io/flutter/embedding/engine/renderer/RenderSurface.java", "io/flutter/embedding/engine/renderer/SurfaceTextureWrapper.java", "io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java", - "io/flutter/embedding/engine/systemchannels/DynamicFeatureChannel.java", + "io/flutter/embedding/engine/systemchannels/DeferredComponentChannel.java", "io/flutter/embedding/engine/systemchannels/KeyEventChannel.java", "io/flutter/embedding/engine/systemchannels/LifecycleChannel.java", "io/flutter/embedding/engine/systemchannels/LocalizationChannel.java", @@ -472,13 +472,13 @@ action("robolectric_tests") { "test/io/flutter/embedding/engine/RenderingComponentTest.java", "test/io/flutter/embedding/engine/dart/DartExecutorTest.java", "test/io/flutter/embedding/engine/dart/DartMessengerTest.java", - "test/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManagerTest.java", + "test/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManagerTest.java", "test/io/flutter/embedding/engine/loader/ApplicationInfoLoaderTest.java", "test/io/flutter/embedding/engine/loader/FlutterLoaderTest.java", "test/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorViewTest.java", "test/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistryTest.java", "test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java", - "test/io/flutter/embedding/engine/systemchannels/DynamicFeatureChannelTest.java", + "test/io/flutter/embedding/engine/systemchannels/DeferredComponentChannelTest.java", "test/io/flutter/embedding/engine/systemchannels/KeyEventChannelTest.java", "test/io/flutter/embedding/engine/systemchannels/PlatformChannelTest.java", "test/io/flutter/embedding/engine/systemchannels/RestorationChannelTest.java", diff --git a/engine/src/flutter/shell/platform/android/io/flutter/FlutterInjector.java b/engine/src/flutter/shell/platform/android/io/flutter/FlutterInjector.java index d703007ff96..1d5132e7ca6 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/FlutterInjector.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/FlutterInjector.java @@ -7,7 +7,7 @@ package io.flutter; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import io.flutter.embedding.engine.dynamicfeatures.DynamicFeatureManager; +import io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager; import io.flutter.embedding.engine.loader.FlutterLoader; /** @@ -65,13 +65,13 @@ public final class FlutterInjector { } private FlutterInjector( - @NonNull FlutterLoader flutterLoader, DynamicFeatureManager dynamicFeatureManager) { + @NonNull FlutterLoader flutterLoader, DeferredComponentManager deferredComponentManager) { this.flutterLoader = flutterLoader; - this.dynamicFeatureManager = dynamicFeatureManager; + this.deferredComponentManager = deferredComponentManager; } private FlutterLoader flutterLoader; - private DynamicFeatureManager dynamicFeatureManager; + private DeferredComponentManager deferredComponentManager; /** Returns the {@link FlutterLoader} instance to use for the Flutter Android engine embedding. */ @NonNull @@ -80,12 +80,12 @@ public final class FlutterInjector { } /** - * Returns the {@link DynamicFeatureManager} instance to use for the Flutter Android engine + * Returns the {@link DeferredComponentManager} instance to use for the Flutter Android engine * embedding. */ @Nullable - public DynamicFeatureManager dynamicFeatureManager() { - return dynamicFeatureManager; + public DeferredComponentManager deferredComponentManager() { + return deferredComponentManager; } /** @@ -96,7 +96,7 @@ public final class FlutterInjector { */ public static final class Builder { private FlutterLoader flutterLoader; - private DynamicFeatureManager dynamicFeatureManager; + private DeferredComponentManager deferredComponentManager; /** * Sets a {@link FlutterLoader} override. * @@ -107,8 +107,9 @@ public final class FlutterInjector { return this; } - public Builder setDynamicFeatureManager(@Nullable DynamicFeatureManager dynamicFeatureManager) { - this.dynamicFeatureManager = dynamicFeatureManager; + public Builder setDeferredComponentManager( + @Nullable DeferredComponentManager deferredComponentManager) { + this.deferredComponentManager = deferredComponentManager; return this; } @@ -116,7 +117,7 @@ public final class FlutterInjector { if (flutterLoader == null) { flutterLoader = new FlutterLoader(); } - // DynamicFeatureManager's intended default is null. + // DeferredComponentManager's intended default is null. } /** @@ -126,7 +127,7 @@ public final class FlutterInjector { public FlutterInjector build() { fillDefaults(); - return new FlutterInjector(flutterLoader, dynamicFeatureManager); + return new FlutterInjector(flutterLoader, deferredComponentManager); } } } diff --git a/engine/src/flutter/shell/platform/android/io/flutter/app/FlutterPlayStoreSplitApplication.java b/engine/src/flutter/shell/platform/android/io/flutter/app/FlutterPlayStoreSplitApplication.java index 91502c8568f..7a415d71ff5 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/app/FlutterPlayStoreSplitApplication.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/app/FlutterPlayStoreSplitApplication.java @@ -7,11 +7,11 @@ package io.flutter.app; import androidx.annotation.CallSuper; import com.google.android.play.core.splitcompat.SplitCompatApplication; import io.flutter.FlutterInjector; -import io.flutter.embedding.engine.dynamicfeatures.PlayStoreDynamicFeatureManager; +import io.flutter.embedding.engine.deferredcomponents.PlayStoreDeferredComponentManager; /** * Flutter's extension of {@link SplitCompatApplication} that injects a {@link - * PlayStoreDynamicFeatureManager} with {@link FlutterInjector} to enable Split AOT Flutter apps. + * PlayStoreDeferredComponentManager} with {@link FlutterInjector} to enable Split AOT Flutter apps. * *

To use this class, either have your custom application class extend * FlutterPlayStoreSplitApplication or use it directly in the app's AndroidManifest.xml by adding @@ -29,11 +29,11 @@ import io.flutter.embedding.engine.dynamicfeatures.PlayStoreDynamicFeatureManage * * This class is meant to be used with the Google Play store. Custom non-play store applications do * not need to extend SplitCompatApplication and should inject a custom {@link - * io.flutter.embedding.engine.dynamicfeatures.DynamicFeatureManager} implementation like so: + * io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager} implementation like so: * *

{@code
  * FlutterInjector.setInstance(
- *      new FlutterInjector.Builder().setDynamicFeatureManager(yourCustomManager).build());
+ *      new FlutterInjector.Builder().setDeferredComponentManager(yourCustomManager).build());
  * }
*/ public class FlutterPlayStoreSplitApplication extends SplitCompatApplication { @@ -41,11 +41,13 @@ public class FlutterPlayStoreSplitApplication extends SplitCompatApplication { @CallSuper public void onCreate() { super.onCreate(); - // Create and inject a PlayStoreDynamicFeatureManager, which is the default manager for + // Create and inject a PlayStoreDeferredComponentManager, which is the default manager for // interacting with the Google Play Store. - PlayStoreDynamicFeatureManager dynamicFeatureManager = - new PlayStoreDynamicFeatureManager(this, null); + PlayStoreDeferredComponentManager deferredComponentManager = + new PlayStoreDeferredComponentManager(this, null); FlutterInjector.setInstance( - new FlutterInjector.Builder().setDynamicFeatureManager(dynamicFeatureManager).build()); + new FlutterInjector.Builder() + .setDeferredComponentManager(deferredComponentManager) + .build()); } } diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index ec489f25ae6..16007fa1e23 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -12,7 +12,7 @@ import androidx.annotation.Nullable; 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.deferredcomponents.DeferredComponentManager; import io.flutter.embedding.engine.loader.FlutterLoader; import io.flutter.embedding.engine.plugins.PluginRegistry; import io.flutter.embedding.engine.plugins.activity.ActivityControlSurface; @@ -22,7 +22,7 @@ import io.flutter.embedding.engine.plugins.service.ServiceControlSurface; import io.flutter.embedding.engine.renderer.FlutterRenderer; import io.flutter.embedding.engine.renderer.RenderSurface; import io.flutter.embedding.engine.systemchannels.AccessibilityChannel; -import io.flutter.embedding.engine.systemchannels.DynamicFeatureChannel; +import io.flutter.embedding.engine.systemchannels.DeferredComponentChannel; import io.flutter.embedding.engine.systemchannels.KeyEventChannel; import io.flutter.embedding.engine.systemchannels.LifecycleChannel; import io.flutter.embedding.engine.systemchannels.LocalizationChannel; @@ -83,7 +83,7 @@ public class FlutterEngine { // System channels. @NonNull private final AccessibilityChannel accessibilityChannel; - @NonNull private final DynamicFeatureChannel dynamicFeatureChannel; + @NonNull private final DeferredComponentChannel deferredComponentChannel; @NonNull private final KeyEventChannel keyEventChannel; @NonNull private final LifecycleChannel lifecycleChannel; @NonNull private final LocalizationChannel localizationChannel; @@ -278,11 +278,11 @@ public class FlutterEngine { this.dartExecutor = new DartExecutor(flutterJNI, assetManager); this.dartExecutor.onAttachedToJNI(); - DynamicFeatureManager dynamicFeatureManager = - FlutterInjector.instance().dynamicFeatureManager(); + DeferredComponentManager deferredComponentManager = + FlutterInjector.instance().deferredComponentManager(); accessibilityChannel = new AccessibilityChannel(dartExecutor, flutterJNI); - dynamicFeatureChannel = new DynamicFeatureChannel(dartExecutor); + deferredComponentChannel = new DeferredComponentChannel(dartExecutor); keyEventChannel = new KeyEventChannel(dartExecutor); lifecycleChannel = new LifecycleChannel(dartExecutor); localizationChannel = new LocalizationChannel(dartExecutor); @@ -294,8 +294,8 @@ public class FlutterEngine { systemChannel = new SystemChannel(dartExecutor); textInputChannel = new TextInputChannel(dartExecutor); - if (dynamicFeatureManager != null) { - dynamicFeatureManager.setDynamicFeatureChannel(dynamicFeatureChannel); + if (deferredComponentManager != null) { + deferredComponentManager.setDeferredComponentChannel(deferredComponentChannel); } this.localizationPlugin = new LocalizationPlugin(context, localizationChannel); @@ -310,7 +310,7 @@ public class FlutterEngine { flutterJNI.addEngineLifecycleListener(engineLifecycleListener); flutterJNI.setPlatformViewsController(platformViewsController); flutterJNI.setLocalizationPlugin(localizationPlugin); - flutterJNI.setDynamicFeatureManager(FlutterInjector.instance().dynamicFeatureManager()); + flutterJNI.setDeferredComponentManager(FlutterInjector.instance().deferredComponentManager()); attachToJni(); @@ -387,11 +387,11 @@ public class FlutterEngine { platformViewsController.onDetachedFromJNI(); dartExecutor.onDetachedFromJNI(); flutterJNI.removeEngineLifecycleListener(engineLifecycleListener); - flutterJNI.setDynamicFeatureManager(null); + flutterJNI.setDeferredComponentManager(null); flutterJNI.detachFromNativeAndReleaseResources(); - if (FlutterInjector.instance().dynamicFeatureManager() != null) { - FlutterInjector.instance().dynamicFeatureManager().destroy(); - dynamicFeatureChannel.setDynamicFeatureManager(null); + if (FlutterInjector.instance().deferredComponentManager() != null) { + FlutterInjector.instance().deferredComponentManager().destroy(); + deferredComponentChannel.setDeferredComponentManager(null); } } @@ -496,10 +496,10 @@ public class FlutterEngine { return settingsChannel; } - /** System channel that allows manual installation and state querying of dynamic features. */ + /** System channel that allows manual installation and state querying of deferred components. */ @NonNull - public DynamicFeatureChannel getDynamicFeatureChannel() { - return dynamicFeatureChannel; + public DeferredComponentChannel getDeferredComponentChannel() { + return deferredComponentChannel; } /** System channel that sends memory pressure warnings from Android to Flutter. */ diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index c5a12252f5a..e122e2528a9 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -20,7 +20,7 @@ import androidx.annotation.VisibleForTesting; 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.deferredcomponents.DeferredComponentManager; import io.flutter.embedding.engine.mutatorsstack.FlutterMutatorsStack; import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener; import io.flutter.embedding.engine.renderer.RenderSurface; @@ -226,7 +226,7 @@ public class FlutterJNI { @Nullable private LocalizationPlugin localizationPlugin; @Nullable private PlatformViewsController platformViewsController; - @Nullable private DynamicFeatureManager dynamicFeatureManager; + @Nullable private DeferredComponentManager deferredComponentManager; @NonNull private final Set engineLifecycleListeners = new CopyOnWriteArraySet<>(); @@ -984,15 +984,16 @@ public class FlutterJNI { // ----- End Localization Support ---- - // ----- Start Dynamic Features Support ---- + // ----- Start Deferred Components Support ---- - /** Sets the dynamic feature manager that is used to download and install split features. */ + /** Sets the deferred component manager that is used to download and install split features. */ @UiThread - public void setDynamicFeatureManager(@Nullable DynamicFeatureManager dynamicFeatureManager) { + public void setDeferredComponentManager( + @Nullable DeferredComponentManager deferredComponentManager) { ensureRunningOnMainThread(); - this.dynamicFeatureManager = dynamicFeatureManager; - if (dynamicFeatureManager != null) { - dynamicFeatureManager.setJNI(this); + this.deferredComponentManager = deferredComponentManager; + if (deferredComponentManager != null) { + deferredComponentManager.setJNI(this); } } @@ -1000,7 +1001,7 @@ public class FlutterJNI { * Called by dart to request that a Dart deferred library corresponding to loadingUnitId be * downloaded (if necessary) and loaded into the dart vm. * - *

This method delegates the task to DynamicFeatureManager, which handles the download and + *

This method delegates the task to DeferredComponentManager, which handles the download and * loading of the dart library and any assets. * * @param loadingUnitId The loadingUnitId is assigned during compile time by gen_snapshot and is @@ -1009,13 +1010,13 @@ public class FlutterJNI { @SuppressWarnings("unused") @UiThread public void requestDartDeferredLibrary(int loadingUnitId) { - if (dynamicFeatureManager != null) { - dynamicFeatureManager.installDynamicFeature(loadingUnitId, null); + if (deferredComponentManager != null) { + deferredComponentManager.installDeferredComponent(loadingUnitId, null); } else { // TODO(garyq): Add link to setup/instructions guide wiki. Log.e( TAG, - "No DynamicFeatureManager found. Android setup must be completed before using split AOT dynamic features."); + "No DeferredComponentManager found. Android setup must be completed before using split AOT deferred components."); } } @@ -1048,8 +1049,8 @@ public class FlutterJNI { /** * Adds the specified AssetManager as an APKAssetResolver in the Flutter Engine's AssetManager. * - *

This may be used to update the engine AssetManager when a new dynamic feature is installed - * and a new Android AssetManager is created with access to new assets. + *

This may be used to update the engine AssetManager when a new deferred component is + * installed and a new Android AssetManager is created with access to new assets. * * @param assetManager An android AssetManager that is able to access the newly downloaded assets. * @param assetBundlePath The subdirectory that the flutter assets are stored in. The typical @@ -1071,7 +1072,7 @@ public class FlutterJNI { /** * Indicates that a failure was encountered during the Android portion of downloading a dynamic * feature module and loading a dart deferred library, which is typically done by - * DynamicFeatureManager. + * DeferredComponentManager. * *

This will inform dart that the future returned by loadLibrary() should complete with an * error. @@ -1084,16 +1085,16 @@ public class FlutterJNI { */ @SuppressWarnings("unused") @UiThread - public void dynamicFeatureInstallFailure( + public void deferredComponentInstallFailure( int loadingUnitId, @NonNull String error, boolean isTransient) { ensureRunningOnMainThread(); - nativeDynamicFeatureInstallFailure(loadingUnitId, error, isTransient); + nativeDeferredComponentInstallFailure(loadingUnitId, error, isTransient); } - private native void nativeDynamicFeatureInstallFailure( + private native void nativeDeferredComponentInstallFailure( int loadingUnitId, @NonNull String error, boolean isTransient); - // ----- End Dynamic Features Support ---- + // ----- End Deferred Components Support ---- // @SuppressWarnings("unused") @UiThread diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/DynamicFeatureManager.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/DeferredComponentManager.java similarity index 58% rename from engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/DynamicFeatureManager.java rename to engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/DeferredComponentManager.java index 5acdf59c066..f516cf38f56 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/DynamicFeatureManager.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/DeferredComponentManager.java @@ -2,40 +2,40 @@ // 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; +package io.flutter.embedding.engine.deferredcomponents; import io.flutter.embedding.engine.FlutterJNI; -import io.flutter.embedding.engine.systemchannels.DynamicFeatureChannel; +import io.flutter.embedding.engine.systemchannels.DeferredComponentChannel; // TODO: add links to external documentation on how to use split aot features. /** - * Basic interface that handles downloading and loading of dynamic features. + * Basic interface that handles downloading and loading of deferred components. * - *

Flutter dynamic feature support is still in early developer preview and should not be used in - * production apps yet. + *

Flutter deferred component support is still in early developer preview and should not be used + * in production apps yet. * - *

The Flutter default implementation is PlayStoreDynamicFeatureManager. + *

The Flutter default implementation is PlayStoreDeferredComponentManager. * - *

DynamicFeatureManager handles the embedder/Android level tasks of downloading, installing, and - * 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 + *

DeferredComponentManager handles the embedder/Android level tasks of downloading, installing, + * and 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 - * invokes installDynamicFeature. Once the feature module is downloaded, loadAssets and + * invokes installDeferredComponent. Once the feature module is downloaded, loadAssets and * loadDartLibrary should be invoked. loadDartLibrary should find shared library .so files for the * engine to open and pass the .so path to FlutterJNI.loadDartDeferredLibrary. loadAssets should * typically ensure the new assets are available to the engine's asset manager by passing an updated * Android AssetManager to the engine via FlutterJNI.updateAssetManager. * *

The loadAssets and loadDartLibrary methods are separated out because they may also be called - * manually via platform channel messages. A full installDynamicFeature implementation should call - * these two methods as needed. + * manually via platform channel messages. A full installDeferredComponent implementation should + * call these two methods as needed. * - *

A dynamic feature module is uniquely identified by a module name as defined in + *

A deferred component module is uniquely identified by a module name as defined in * bundle_config.yaml. Each feature module may contain one or more loading units, uniquely * identified by the loading unit ID and assets. */ -public interface DynamicFeatureManager { +public interface DeferredComponentManager { /** * Sets the FlutterJNI to be used to communication with the Flutter native engine. * @@ -48,25 +48,25 @@ public interface DynamicFeatureManager { public abstract void setJNI(FlutterJNI flutterJNI); /** - * Sets the DynamicFeatureChannel system channel to handle the framework API to directly call - * methods in DynamicFeatureManager. + * Sets the DeferredComponentChannel system channel to handle the framework API to directly call + * methods in DeferredComponentManager. * - *

A DynamicFeatureChannel is required to handle assets-only dynamic features and manually - * installed dynamic features. + *

A DeferredComponentChannel is required to handle assets-only deferred components and + * manually installed deferred components. * *

Since this class may be instantiated for injection before the FlutterEngine and System - * Channels are initialized, this method should be called to provide the DynamicFeatureChannel. - * Similarly, the {@link DynamicFeatureChannel.setDynamicFeatureManager} method should also be - * called with this DynamicFeatureManager instance to properly forward method invocations. + * Channels are initialized, this method should be called to provide the DeferredComponentChannel. + * Similarly, the {@link DeferredComponentChannel.setDeferredComponentManager} method should also + * be called with this DeferredComponentManager instance to properly forward method invocations. * - *

The {@link DynamicFeatureChannel} passes manual invocations of {@link installDynamicFeature} - * and {@link getDynamicFeatureInstallState} from the method channel to this - * DynamicFeatureManager. Upon completion of the install process, sucessful installations should - * notify the DynamicFeatureChannel by calling {@link - * DynamicFeatureChannel.completeInstallSuccess} while errors and failures should call {@link - * DynamicFeatureChannel.completeInstallError}. + *

The {@link DeferredComponentChannel} passes manual invocations of {@link + * installDeferredComponent} and {@link getDeferredComponentInstallState} from the method channel + * to this DeferredComponentManager. Upon completion of the install process, sucessful + * installations should notify the DeferredComponentChannel by calling {@link + * DeferredComponentChannel.completeInstallSuccess} while errors and failures should call {@link + * DeferredComponentChannel.completeInstallError}. */ - public abstract void setDynamicFeatureChannel(DynamicFeatureChannel channel); + public abstract void setDeferredComponentChannel(DeferredComponentChannel channel); /** * Request that the feature module be downloaded and installed. @@ -75,10 +75,10 @@ public interface DynamicFeatureManager { * example, the Play Store dynamic delivery implementation uses SplitInstallManager to request the * download of the module. Download is not complete when this method returns. The download process * should be listened for and upon completion of download, listeners should invoke loadAssets - * first and then loadDartLibrary to complete the dynamic feature load process. Assets-only - * dynamic features should also call {@link DynamicFeatureChannel.completeInstallSuccess} or - * {@link DynamicFeatureChannel.completeInstallError} to complete the method channel invocation's - * dart Future. + * first and then loadDartLibrary to complete the deferred component load process. Assets-only + * deferred components should also call {@link DeferredComponentChannel.completeInstallSuccess} or + * {@link DeferredComponentChannel.completeInstallError} to complete the method channel + * invocation's dart Future. * *

Both parameters are not always necessary to identify which module to install. Asset-only * modules do not have an associated loadingUnitId. Instead, an invalid ID like -1 may be passed @@ -93,46 +93,47 @@ public interface DynamicFeatureManager { *

When invoked manually as part of loading an assets-only module, loadingUnitId is -1 * (invalid) and moduleName is supplied. Without a loadingUnitId, this method just downloads the * module by name and attempts to load assets via loadAssets while loadDartLibrary is skipped, - * even if the dynamic feature module includes valid dart libs. To load dart libs, call + * even if the deferred component module includes valid dart libs. To load dart libs, call * `loadLibrary()` using the first way described in the previous paragraph as the method channel * invocation will not load dart shared libraries. * *

While the Future retuned by either `loadLibary` or the method channel invocation will * indicate when the code and assets are ready to be used, informational querying of the install - * process' state can be done with {@link getDynamicFeatureInstallState}, though the results of - * this query should not be used to decide if the dynamic feature is ready to use. Only the Future - * completion should be used to do this. + * process' state can be done with {@link getDeferredComponentInstallState}, though the results of + * this query should not be used to decide if the deferred component is ready to use. Only the + * Future completion should be used to do this. * * @param loadingUnitId The unique identifier associated with a Dart deferred library. This id is * assigned by the compiler and can be seen for reference in bundle_config.yaml. This ID is * primarily used in loadDartLibrary to indicate to Dart which Dart library is being loaded. * Loading unit ids range from 0 to the number existing loading units. Passing a negative * loading unit id indicates that no Dart deferred library should be loaded after download - * completes. This is the case when the dynamic feature module is an assets-only module. If a - * negative loadingUnitId is passed, then moduleName must not be null. 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. This may be - * null if the dynamic feature to be loaded is associated with a loading unit/deferred dart - * library. In this case, it is this method's responsibility to map the loadingUnitId to its - * corresponding moduleName. When loading asset-only or other dynamic features without an - * associated Dart deferred library, loading unit id should a negative value and moduleName + * completes. This is the case when the deferred component module is an assets-only module. If + * a negative loadingUnitId is passed, then moduleName must not be null. 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 deferred component module name as defined in bundle_config.yaml. This may + * be null if the deferred component to be loaded is associated with a loading unit/deferred + * dart library. In this case, it is this method's responsibility to map the loadingUnitId to + * its corresponding moduleName. When loading asset-only or other deferred components without + * an associated Dart deferred library, loading unit id should a negative value and moduleName * must be non-null. */ - public abstract void installDynamicFeature(int loadingUnitId, String moduleName); + public abstract void installDeferredComponent(int loadingUnitId, String moduleName); /** * Gets the current state of the installation session corresponding to the specified loadingUnitId * and/or moduleName. * - *

Invocations of {@link installDynamicFeature} typically result in asynchronous downloading + *

Invocations of {@link installDeferredComponent} typically result in asynchronous downloading * and other tasks. This method enables querying of the state of the installation. Querying the * installation state is purely informational and does not impact the installation process. The - * results of this query should not be used to decide if the dynamic feature is ready to use. Upon - * completion of installation, the Future returned by the installation request will complete. Only - * after dart Future completion is it safe to use code and assets from the dynamic feature. + * results of this query should not be used to decide if the deferred component is ready to use. + * Upon completion of installation, the Future returned by the installation request will complete. + * Only after dart Future completion is it safe to use code and assets from the deferred + * component. * - *

If no dynamic feature has been installed or requested to be installed by the provided + *

If no deferred component has been installed or requested to be installed by the provided * loadingUnitId or moduleName, then this method will return null. * *

Depending on the implementation, the returned String may vary. The Play store default @@ -140,9 +141,9 @@ public interface DynamicFeatureManager { * "installed" states. * *

Only sucessfully requested modules have state. Modules that are invalid or have not been - * requested with {@link installDynamicFeature} will not have a state. Due to the asynchronous + * requested with {@link installDeferredComponent} will not have a state. Due to the asynchronous * nature of the download process, modules may not immediately have a valid state upon return of - * {@link installDynamicFeature}, though valid modules will eventually obtain a state. + * {@link installDeferredComponent}, though valid modules will eventually obtain a state. * *

Both parameters are not always necessary to identify which module to install. Asset-only * modules do not have an associated loadingUnitId. Instead, an invalid ID like -1 may be passed @@ -151,36 +152,36 @@ public interface DynamicFeatureManager { * loadingUnitId or moduleName must be valid or non-null. * * @param loadingUnitId The unique identifier associated with a Dart deferred library. - * @param moduleName The dynamic feature module name as defined in bundle_config.yaml. + * @param moduleName The deferred component module name as defined in bundle_config.yaml. */ - public abstract String getDynamicFeatureInstallState(int loadingUnitId, String moduleName); + public abstract String getDeferredComponentInstallState(int loadingUnitId, String moduleName); /** * Extract and load any assets and resources from the module for use by Flutter. * *

This method should provide a refreshed AssetManager to FlutterJNI.updateAssetManager that - * can access the new assets. If no assets are included as part of the dynamic feature, then + * can access the new assets. If no assets are included as part of the deferred component, then * nothing needs to be done. * - *

If using the Play Store dynamic feature delivery, refresh the context via: {@code + *

If using the Play Store deferred component delivery, refresh the context via: {@code * context.createPackageContext(context.getPackageName(), 0);} This returns a new context, from * which an updated asset manager may be obtained and passed to updateAssetManager in FlutterJNI. * This process does not require loadingUnitId or moduleName, however, the two parameters are * still present for custom implementations that store assets outside of Android's native system. * *

Assets shoud be loaded before the Dart deferred library is loaded, as successful loading of - * the Dart loading unit indicates the dynamic feature is fully loaded. Implementations of - * installDynamicFeature should invoke this after successful download. + * the Dart loading unit indicates the deferred component is fully loaded. Implementations of + * installDeferredComponent should invoke this after successful download. * * @param loadingUnitId The unique identifier associated with a Dart deferred library. - * @param moduleName The dynamic feature module name as defined in bundle_config.yaml. + * @param moduleName The deferred component module name as defined in bundle_config.yaml. */ public abstract void loadAssets(int loadingUnitId, String moduleName); /** * Load the .so shared library file into the Dart VM. * - *

When the download of a dynamic feature module completes, this method should be called to + *

When the download of a deferred component module completes, this method should be called to * find the path .so library file. The path(s) should then be passed to * FlutterJNI.loadDartDeferredLibrary to be dlopen-ed and loaded into the Dart VM. * @@ -198,22 +199,35 @@ public interface DynamicFeatureManager { * primarily used in loadDartLibrary to indicate to Dart which Dart library is being loaded. * Loading unit ids range from 0 to the number existing loading units. Negative loading unit * ids are considered invalid and this method will result in a no-op. - * @param moduleName The dynamic feature module name as defined in bundle_config.yaml. If using - * Play Store dynamic feature delivery, this name corresponds to the root name on the + * @param moduleName The deferred component module name as defined in bundle_config.yaml. If using + * Play Store deferred component delivery, this name corresponds to the root name on the * installed APKs in which to search for the desired shared library .so file. */ public abstract void loadDartLibrary(int loadingUnitId, String moduleName); /** - * Uninstall the specified feature module. + * Request that the specified feature module be uninstalled. + * + *

Since uninstallation requires significant disk i/o, this method only signals the intent to + * uninstall. Actual uninstallation (eg, removal of assets and files) may occur at a later time. + * However, once uninstallation is requested, the deferred component should not be used anymore + * until {@link installDeferredComponent} is called again. + * + *

Uninstallation, once complete, removes downloaded files and will require redownloading to + * install again. * *

Both parameters are not always necessary to identify which module to uninstall. Asset-only * 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. + * + * @return false if no deferred component was found matching the input, true if an uninstall was + * successfully requested. + * @param loadingUnitId The unique identifier associated with a Dart deferred library. + * @param moduleName The deferred component module name as defined in bundle_config.yaml. */ - public abstract void uninstallFeature(int loadingUnitId, String moduleName); + public abstract boolean uninstallDeferredComponent(int loadingUnitId, String moduleName); /** * Cleans up and releases resources. This object is no longer usable after calling this method. diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManager.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java similarity index 82% rename from engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManager.java rename to engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java index 41032e13bba..8ce4b4eba16 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManager.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java @@ -2,7 +2,7 @@ // 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; +package io.flutter.embedding.engine.deferredcomponents; import android.annotation.SuppressLint; import android.content.Context; @@ -23,7 +23,7 @@ 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 io.flutter.embedding.engine.systemchannels.DynamicFeatureChannel; +import io.flutter.embedding.engine.systemchannels.DeferredComponentChannel; import java.io.File; import java.util.ArrayList; import java.util.HashMap; @@ -33,15 +33,15 @@ import java.util.Map; import java.util.Queue; /** - * Flutter default implementation of DynamicFeatureManager that downloads dynamic feature modules - * from the Google Play store. + * Flutter default implementation of DeferredComponentManager that downloads deferred component + * modules from the Google Play store. */ -public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager { - private static final String TAG = "PlayStoreDynamicFeatureManager"; +public class PlayStoreDeferredComponentManager implements DeferredComponentManager { + private static final String TAG = "PlayStoreDeferredComponentManager"; private @NonNull SplitInstallManager splitInstallManager; private @Nullable FlutterJNI flutterJNI; - private @Nullable DynamicFeatureChannel channel; + private @Nullable DeferredComponentChannel channel; private @NonNull Context context; // Each request to install a feature module gets a session ID. These maps associate // the session ID with the loading unit and module name that was requested. @@ -65,13 +65,14 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager { String.format( "Module \"%s\" (sessionId %d) install failed with: %s", sessionIdToName.get(sessionId), sessionId, state.errorCode())); - flutterJNI.dynamicFeatureInstallFailure( + flutterJNI.deferredComponentInstallFailure( sessionIdToLoadingUnitId.get(sessionId), "Module install failed with " + state.errorCode(), true); if (channel != null) { channel.completeInstallError( - sessionIdToName.get(sessionId), "Android Dynamic Feature failed to install."); + sessionIdToName.get(sessionId), + "Android Deferred Component failed to install."); } sessionIdToName.delete(sessionId); sessionIdToLoadingUnitId.delete(sessionId); @@ -87,7 +88,7 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager { sessionIdToName.get(sessionId), sessionId)); loadAssets(sessionIdToLoadingUnitId.get(sessionId), sessionIdToName.get(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 + // (if present) in the deferred component module are not loaded, but can be loaded by // calling again with their loading unit id. If no valid loadingUnitId was included in // the installation request such as for an asset only feature, then we can skip this. if (sessionIdToLoadingUnitId.get(sessionId) > 0) { @@ -112,7 +113,7 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager { if (channel != null) { channel.completeInstallError( sessionIdToName.get(sessionId), - "Android Dynamic Feature installation canceled."); + "Android Deferred Component installation canceled."); } sessionIdToName.delete(sessionId); sessionIdToLoadingUnitId.delete(sessionId); @@ -146,7 +147,7 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager { String.format( "Module \"%s\" (sessionId %d) install requires user confirmation.", sessionIdToName.get(sessionId), sessionId)); - sessionIdToState.put(sessionId, "requires_user_confirmation"); + sessionIdToState.put(sessionId, "requiresUserConfirmation"); break; } case SplitInstallSessionStatus.DOWNLOADING: @@ -186,7 +187,8 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager { } } - public PlayStoreDynamicFeatureManager(@NonNull Context context, @Nullable FlutterJNI flutterJNI) { + public PlayStoreDeferredComponentManager( + @NonNull Context context, @Nullable FlutterJNI flutterJNI) { this.context = context; this.flutterJNI = flutterJNI; splitInstallManager = SplitInstallManagerFactory.create(context); @@ -206,13 +208,13 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager { if (flutterJNI == null) { Log.e( TAG, - "No FlutterJNI provided. `setJNI` must be called on the DynamicFeatureManager before attempting to load dart libraries or invoking with platform channels."); + "No FlutterJNI provided. `setJNI` must be called on the DeferredComponentManager before attempting to load dart libraries or invoking with platform channels."); return false; } return true; } - public void setDynamicFeatureChannel(DynamicFeatureChannel channel) { + public void setDeferredComponentChannel(DeferredComponentChannel channel) { this.channel = channel; } @@ -226,13 +228,13 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager { return context.getResources().getString(moduleNameIdentifier); } - public void installDynamicFeature(int loadingUnitId, String moduleName) { + public void installDeferredComponent(int loadingUnitId, String moduleName) { String resolvedModuleName = moduleName != null ? moduleName : loadingUnitIdToModuleName(loadingUnitId); if (resolvedModuleName == null) { Log.e( TAG, - "Dynamic feature module name was null and could not be resolved from loading unit id."); + "Deferred component module name was null and could not be resolved from loading unit id."); return; } @@ -260,26 +262,26 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager { exception -> { switch (((SplitInstallException) exception).getErrorCode()) { case SplitInstallErrorCode.NETWORK_ERROR: - flutterJNI.dynamicFeatureInstallFailure( + flutterJNI.deferredComponentInstallFailure( loadingUnitId, String.format( - "Install of dynamic feature module \"%s\" failed with a network error", + "Install of deferred component module \"%s\" failed with a network error", moduleName), true); break; case SplitInstallErrorCode.MODULE_UNAVAILABLE: - flutterJNI.dynamicFeatureInstallFailure( + flutterJNI.deferredComponentInstallFailure( loadingUnitId, String.format( - "Install of dynamic feature module \"%s\" failed as it is unavailable", + "Install of deferred component module \"%s\" failed as it is unavailable", moduleName), false); break; default: - flutterJNI.dynamicFeatureInstallFailure( + flutterJNI.deferredComponentInstallFailure( loadingUnitId, String.format( - "Install of dynamic feature module \"%s\" failed with error %d: %s", + "Install of deferred component module \"%s\" failed with error %d: %s", moduleName, ((SplitInstallException) exception).getErrorCode(), ((SplitInstallException) exception).getMessage()), @@ -289,17 +291,20 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager { }); } - public String getDynamicFeatureInstallState(int loadingUnitId, String moduleName) { + public String getDeferredComponentInstallState(int loadingUnitId, String moduleName) { String resolvedModuleName = moduleName != null ? moduleName : loadingUnitIdToModuleName(loadingUnitId); if (resolvedModuleName == null) { Log.e( TAG, - "Dynamic feature module name was null and could not be resolved from loading unit id."); - return null; + "Deferred component module name was null and could not be resolved from loading unit id."); + return "unknown"; } if (!nameToSessionId.containsKey(resolvedModuleName)) { - return null; + if (splitInstallManager.getInstalledModules().contains(resolvedModuleName)) { + return "installedPendingLoad"; + } + return "unknown"; } int sessionId = nameToSessionId.get(resolvedModuleName); return sessionIdToState.get(sessionId); @@ -309,7 +314,7 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager { if (!verifyJNI()) { return; } - // Since android dynamic feature asset manager is handled through + // Since android deferred component asset manager is handled through // context, neither parameter is used here. Assets are stored in // the apk's `assets` directory allowing them to be accessed by // Android's AssetManager directly. @@ -386,8 +391,20 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager { loadingUnitId, searchPaths.toArray(new String[apkPaths.size()])); } - public void uninstallFeature(int loadingUnitId, String moduleName) { - // TODO(garyq): support uninstalling. + public boolean uninstallDeferredComponent(int loadingUnitId, String moduleName) { + String resolvedModuleName = + moduleName != null ? moduleName : loadingUnitIdToModuleName(loadingUnitId); + if (resolvedModuleName == null) { + Log.e( + TAG, + "Deferred component module name was null and could not be resolved from loading unit id."); + return false; + } + List modulesToUninstall = new ArrayList<>(); + modulesToUninstall.add(resolvedModuleName); + splitInstallManager.deferredUninstall(modulesToUninstall); + sessionIdToState.delete(nameToSessionId.get(resolvedModuleName)); + return true; } public void destroy() { diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/DynamicFeatureChannel.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/DeferredComponentChannel.java similarity index 60% rename from engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/DynamicFeatureChannel.java rename to engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/DeferredComponentChannel.java index 4535fff8685..c1b7764db13 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/DynamicFeatureChannel.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/DeferredComponentChannel.java @@ -10,7 +10,7 @@ import androidx.annotation.VisibleForTesting; 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.deferredcomponents.DeferredComponentManager; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.StandardMethodCodec; @@ -21,15 +21,15 @@ import java.util.Map; /** * Method channel that handles manual installation requests and queries for installation state for - * dynamic feature modules. + * deferred component modules. * *

This channel is able to handle multiple simultaneous installation requests */ -public class DynamicFeatureChannel { - private static final String TAG = "DynamicFeatureChannel"; +public class DeferredComponentChannel { + private static final String TAG = "DeferredComponentChannel"; @NonNull private final MethodChannel channel; - @Nullable private DynamicFeatureManager dynamicFeatureManager; + @Nullable private DeferredComponentManager deferredComponentManager; // Track the Result objects to be able to handle multiple install requests of // the same module at a time. When installation enters a terminal state, either // completeInstallSuccess or completeInstallError can be called. @@ -40,8 +40,8 @@ public class DynamicFeatureChannel { new MethodChannel.MethodCallHandler() { @Override public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - if (dynamicFeatureManager == null) { - // If no DynamicFeatureManager has been injected, then this channel is a no-op. + if (deferredComponentManager == null) { + // If no DeferredComponentManager has been injected, then this channel is a no-op. return; } String method = call.method; @@ -50,16 +50,21 @@ public class DynamicFeatureChannel { final int loadingUnitId = (int) args.get("loadingUnitId"); final String moduleName = (String) args.get("moduleName"); switch (method) { - case "installDynamicFeature": - dynamicFeatureManager.installDynamicFeature(loadingUnitId, moduleName); + case "installDeferredComponent": + deferredComponentManager.installDeferredComponent(loadingUnitId, moduleName); if (!moduleNameToResults.containsKey(moduleName)) { moduleNameToResults.put(moduleName, new ArrayList<>()); } moduleNameToResults.get(moduleName).add(result); break; - case "getDynamicFeatureInstallState": + case "getDeferredComponentInstallState": result.success( - dynamicFeatureManager.getDynamicFeatureInstallState(loadingUnitId, moduleName)); + deferredComponentManager.getDeferredComponentInstallState( + loadingUnitId, moduleName)); + break; + case "uninstallDeferredComponent": + deferredComponentManager.uninstallDeferredComponent(loadingUnitId, moduleName); + result.success(null); break; default: result.notImplemented(); @@ -69,36 +74,38 @@ public class DynamicFeatureChannel { }; /** - * Constructs a {@code DynamicFeatureChannel} that connects Android to the Dart code running in + * Constructs a {@code DeferredComponentChannel} that connects Android to the Dart code running in * {@code dartExecutor}. * *

The given {@code dartExecutor} is permitted to be idle or executing code. * *

See {@link DartExecutor}. */ - public DynamicFeatureChannel(@NonNull DartExecutor dartExecutor) { + public DeferredComponentChannel(@NonNull DartExecutor dartExecutor) { this.channel = - new MethodChannel(dartExecutor, "flutter/dynamicfeature", StandardMethodCodec.INSTANCE); + new MethodChannel(dartExecutor, "flutter/deferredcomponent", StandardMethodCodec.INSTANCE); channel.setMethodCallHandler(parsingMethodHandler); - dynamicFeatureManager = FlutterInjector.instance().dynamicFeatureManager(); + deferredComponentManager = FlutterInjector.instance().deferredComponentManager(); moduleNameToResults = new HashMap<>(); } /** - * Sets the DynamicFeatureManager to exectue method channel calls with. + * Sets the DeferredComponentManager to exectue method channel calls with. * - * @param dynamicFeatureManager the DynamicFeatureManager to use. + * @param deferredComponentManager the DeferredComponentManager to use. */ @VisibleForTesting - public void setDynamicFeatureManager(@Nullable DynamicFeatureManager dynamicFeatureManager) { - this.dynamicFeatureManager = dynamicFeatureManager; + public void setDeferredComponentManager( + @Nullable DeferredComponentManager deferredComponentManager) { + this.deferredComponentManager = deferredComponentManager; } /** - * Finishes the `installDynamicFeature` method channel call for the specified moduleName with a + * Finishes the `installDeferredComponent` method channel call for the specified moduleName with a * success. * - * @param moduleName The name of the android dynamic feature module install request to complete. + * @param moduleName The name of the android deferred component module install request to + * complete. */ public void completeInstallSuccess(String moduleName) { if (moduleNameToResults.containsKey(moduleName)) { @@ -111,16 +118,17 @@ public class DynamicFeatureChannel { } /** - * Finishes the `installDynamicFeature` method channel call for the specified moduleName with an - * error/failure. + * Finishes the `installDeferredComponent` method channel call for the specified moduleName with + * an error/failure. * - * @param moduleName The name of the android dynamic feature module install request to complete. + * @param moduleName The name of the android deferred component module install request to + * complete. * @param errorMessage The error message to display to complete the future with. */ public void completeInstallError(String moduleName, String errorMessage) { if (moduleNameToResults.containsKey(moduleName)) { for (MethodChannel.Result result : moduleNameToResults.get(moduleName)) { - result.error("DynamicFeature Install failure", errorMessage, null); + result.error("DeferredComponent Install failure", errorMessage, null); } moduleNameToResults.get(moduleName).clear(); } diff --git a/engine/src/flutter/shell/platform/android/platform_view_android_jni_impl.cc b/engine/src/flutter/shell/platform/android/platform_view_android_jni_impl.cc index efe994f52d9..1b1efa0d2ef 100644 --- a/engine/src/flutter/shell/platform/android/platform_view_android_jni_impl.cc +++ b/engine/src/flutter/shell/platform/android/platform_view_android_jni_impl.cc @@ -522,11 +522,11 @@ static void LoadLoadingUnitFailure(intptr_t loading_unit_id, // TODO(garyq): Implement } -static void DynamicFeatureInstallFailure(JNIEnv* env, - jobject obj, - jint jLoadingUnitId, - jstring jError, - jboolean jTransient) { +static void DeferredComponentInstallFailure(JNIEnv* env, + jobject obj, + jint jLoadingUnitId, + jstring jError, + jboolean jTransient) { LoadLoadingUnitFailure(static_cast(jLoadingUnitId), fml::jni::JavaStringToString(env, jError), static_cast(jTransient)); @@ -754,9 +754,9 @@ bool RegisterApi(JNIEnv* env) { .fnPtr = reinterpret_cast(&UpdateJavaAssetManager), }, { - .name = "nativeDynamicFeatureInstallFailure", + .name = "nativeDeferredComponentInstallFailure", .signature = "(ILjava/lang/String;Z)V", - .fnPtr = reinterpret_cast(&DynamicFeatureInstallFailure), + .fnPtr = reinterpret_cast(&DeferredComponentInstallFailure), }, }; diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/FlutterInjectorTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/FlutterInjectorTest.java index 66384fcb7c2..65c958b1010 100644 --- a/engine/src/flutter/shell/platform/android/test/io/flutter/FlutterInjectorTest.java +++ b/engine/src/flutter/shell/platform/android/test/io/flutter/FlutterInjectorTest.java @@ -9,7 +9,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; -import io.flutter.embedding.engine.dynamicfeatures.PlayStoreDynamicFeatureManager; +import io.flutter.embedding.engine.deferredcomponents.PlayStoreDeferredComponentManager; import io.flutter.embedding.engine.loader.FlutterLoader; import org.junit.Before; import org.junit.Test; @@ -23,7 +23,7 @@ import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) public class FlutterInjectorTest { @Mock FlutterLoader mockFlutterLoader; - @Mock PlayStoreDynamicFeatureManager mockDynamicFeatureManager; + @Mock PlayStoreDeferredComponentManager mockDeferredComponentManager; @Before public void setUp() { @@ -37,7 +37,7 @@ public class FlutterInjectorTest { // Implicitly builds when first accessed. FlutterInjector injector = FlutterInjector.instance(); assertNotNull(injector.flutterLoader()); - assertNull(injector.dynamicFeatureManager()); + assertNull(injector.deferredComponentManager()); } @Test @@ -49,11 +49,13 @@ public class FlutterInjectorTest { } @Test - public void canInjectDynamicFeatureManager() { + public void canInjectDeferredComponentManager() { FlutterInjector.setInstance( - new FlutterInjector.Builder().setDynamicFeatureManager(mockDynamicFeatureManager).build()); + new FlutterInjector.Builder() + .setDeferredComponentManager(mockDeferredComponentManager) + .build()); FlutterInjector injector = FlutterInjector.instance(); - assertEquals(injector.dynamicFeatureManager(), mockDynamicFeatureManager); + assertEquals(injector.deferredComponentManager(), mockDeferredComponentManager); } @Test() diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/FlutterTestSuite.java b/engine/src/flutter/shell/platform/android/test/io/flutter/FlutterTestSuite.java index 8a7332d9a0a..5ea424c1413 100644 --- a/engine/src/flutter/shell/platform/android/test/io/flutter/FlutterTestSuite.java +++ b/engine/src/flutter/shell/platform/android/test/io/flutter/FlutterTestSuite.java @@ -18,12 +18,13 @@ import io.flutter.embedding.engine.LocalizationPluginTest; import io.flutter.embedding.engine.RenderingComponentTest; import io.flutter.embedding.engine.dart.DartExecutorTest; import io.flutter.embedding.engine.dart.DartMessengerTest; -import io.flutter.embedding.engine.dynamicfeatures.PlayStoreDynamicFeatureManagerTest; +import io.flutter.embedding.engine.deferredcomponents.PlayStoreDeferredComponentManagerTest; import io.flutter.embedding.engine.loader.ApplicationInfoLoaderTest; import io.flutter.embedding.engine.loader.FlutterLoaderTest; import io.flutter.embedding.engine.mutatorsstack.FlutterMutatorViewTest; import io.flutter.embedding.engine.plugins.shim.ShimPluginRegistryTest; import io.flutter.embedding.engine.renderer.FlutterRendererTest; +import io.flutter.embedding.engine.systemchannels.DeferredComponentChannelTest; import io.flutter.embedding.engine.systemchannels.KeyEventChannelTest; import io.flutter.embedding.engine.systemchannels.PlatformChannelTest; import io.flutter.embedding.engine.systemchannels.RestorationChannelTest; @@ -71,6 +72,7 @@ import test.io.flutter.embedding.engine.PluginComponentTest; FlutterShellArgsTest.class, FlutterViewTest.class, InputConnectionAdaptorTest.class, + DeferredComponentChannelTest.class, KeyEventChannelTest.class, ListenableEditingStateTest.class, LocalizationPluginTest.class, @@ -78,7 +80,7 @@ import test.io.flutter.embedding.engine.PluginComponentTest; PlatformChannelTest.class, PlatformPluginTest.class, PlatformViewsControllerTest.class, - PlayStoreDynamicFeatureManagerTest.class, + PlayStoreDeferredComponentManagerTest.class, PluginComponentTest.class, PreconditionsTest.class, RenderingComponentTest.class, diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManagerTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManagerTest.java similarity index 68% rename from engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManagerTest.java rename to engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManagerTest.java index a6d11abfeae..3c80596684d 100644 --- a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManagerTest.java +++ b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManagerTest.java @@ -2,7 +2,7 @@ // 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; +package io.flutter.embedding.engine.deferredcomponents; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertTrue; @@ -25,11 +25,11 @@ import org.robolectric.annotation.Config; @Config(manifest = Config.NONE) @RunWith(RobolectricTestRunner.class) -public class PlayStoreDynamicFeatureManagerTest { +public class PlayStoreDeferredComponentManagerTest { private class TestFlutterJNI extends FlutterJNI { public int loadDartDeferredLibraryCalled = 0; public int updateAssetManagerCalled = 0; - public int dynamicFeatureInstallFailureCalled = 0; + public int deferredComponentInstallFailureCalled = 0; public String[] searchPaths; public int loadingUnitId; public AssetManager assetManager; @@ -52,20 +52,20 @@ public class PlayStoreDynamicFeatureManagerTest { } @Override - public void dynamicFeatureInstallFailure( + public void deferredComponentInstallFailure( int loadingUnitId, @NonNull String error, boolean isTransient) { - dynamicFeatureInstallFailureCalled++; + deferredComponentInstallFailureCalled++; } } // Skips the download process to directly call the loadAssets and loadDartLibrary methods. - private class TestPlayStoreDynamicFeatureManager extends PlayStoreDynamicFeatureManager { - public TestPlayStoreDynamicFeatureManager(Context context, FlutterJNI jni) { + private class TestPlayStoreDeferredComponentManager extends PlayStoreDeferredComponentManager { + public TestPlayStoreDeferredComponentManager(Context context, FlutterJNI jni) { super(context, jni); } @Override - public void installDynamicFeature(int loadingUnitId, String moduleName) { + public void installDeferredComponent(int loadingUnitId, String moduleName) { // Override this to skip the online SplitInstallManager portion. loadAssets(loadingUnitId, moduleName); loadDartLibrary(loadingUnitId, moduleName); @@ -80,15 +80,15 @@ public class PlayStoreDynamicFeatureManagerTest { doReturn(null).when(spyContext).getAssets(); String soTestPath = "test/path/app.so-123.part.so"; doReturn(new File(soTestPath)).when(spyContext).getFilesDir(); - TestPlayStoreDynamicFeatureManager playStoreManager = - new TestPlayStoreDynamicFeatureManager(spyContext, jni); - jni.setDynamicFeatureManager(playStoreManager); + TestPlayStoreDeferredComponentManager playStoreManager = + new TestPlayStoreDeferredComponentManager(spyContext, jni); + jni.setDeferredComponentManager(playStoreManager); assertEquals(jni.loadingUnitId, 0); - playStoreManager.installDynamicFeature(123, "TestModuleName"); + playStoreManager.installDeferredComponent(123, "TestModuleName"); assertEquals(jni.loadDartDeferredLibraryCalled, 1); assertEquals(jni.updateAssetManagerCalled, 1); - assertEquals(jni.dynamicFeatureInstallFailureCalled, 0); + assertEquals(jni.deferredComponentInstallFailureCalled, 0); assertTrue(jni.searchPaths[0].endsWith(soTestPath)); assertEquals(jni.searchPaths.length, 1); @@ -103,16 +103,16 @@ public class PlayStoreDynamicFeatureManagerTest { doReturn(null).when(spyContext).getAssets(); String apkTestPath = "test/path/TestModuleName_armeabi_v7a.apk"; doReturn(new File(apkTestPath)).when(spyContext).getFilesDir(); - TestPlayStoreDynamicFeatureManager playStoreManager = - new TestPlayStoreDynamicFeatureManager(spyContext, jni); - jni.setDynamicFeatureManager(playStoreManager); + TestPlayStoreDeferredComponentManager playStoreManager = + new TestPlayStoreDeferredComponentManager(spyContext, jni); + jni.setDeferredComponentManager(playStoreManager); assertEquals(jni.loadingUnitId, 0); - playStoreManager.installDynamicFeature(123, "TestModuleName"); + playStoreManager.installDeferredComponent(123, "TestModuleName"); assertEquals(jni.loadDartDeferredLibraryCalled, 1); assertEquals(jni.updateAssetManagerCalled, 1); - assertEquals(jni.dynamicFeatureInstallFailureCalled, 0); + assertEquals(jni.deferredComponentInstallFailureCalled, 0); assertTrue(jni.searchPaths[0].endsWith(apkTestPath + "!lib/armeabi-v7a/app.so-123.part.so")); assertEquals(jni.searchPaths.length, 1); @@ -127,16 +127,16 @@ public class PlayStoreDynamicFeatureManagerTest { doReturn(null).when(spyContext).getAssets(); String apkTestPath = "test/path/invalidpath.apk"; doReturn(new File(apkTestPath)).when(spyContext).getFilesDir(); - TestPlayStoreDynamicFeatureManager playStoreManager = - new TestPlayStoreDynamicFeatureManager(spyContext, jni); - jni.setDynamicFeatureManager(playStoreManager); + TestPlayStoreDeferredComponentManager playStoreManager = + new TestPlayStoreDeferredComponentManager(spyContext, jni); + jni.setDeferredComponentManager(playStoreManager); assertEquals(jni.loadingUnitId, 0); - playStoreManager.installDynamicFeature(123, "TestModuleName"); + playStoreManager.installDeferredComponent(123, "TestModuleName"); assertEquals(jni.loadDartDeferredLibraryCalled, 1); assertEquals(jni.updateAssetManagerCalled, 1); - assertEquals(jni.dynamicFeatureInstallFailureCalled, 0); + assertEquals(jni.deferredComponentInstallFailureCalled, 0); assertEquals(jni.searchPaths.length, 0); assertEquals(jni.loadingUnitId, 123); @@ -150,17 +150,27 @@ public class PlayStoreDynamicFeatureManagerTest { AssetManager assetManager = spyContext.getAssets(); String apkTestPath = "blah doesn't matter here"; doReturn(new File(apkTestPath)).when(spyContext).getFilesDir(); - TestPlayStoreDynamicFeatureManager playStoreManager = - new TestPlayStoreDynamicFeatureManager(spyContext, jni); - jni.setDynamicFeatureManager(playStoreManager); + TestPlayStoreDeferredComponentManager playStoreManager = + new TestPlayStoreDeferredComponentManager(spyContext, jni); + jni.setDeferredComponentManager(playStoreManager); assertEquals(jni.loadingUnitId, 0); - playStoreManager.installDynamicFeature(123, "TestModuleName"); + playStoreManager.installDeferredComponent(123, "TestModuleName"); assertEquals(jni.loadDartDeferredLibraryCalled, 1); assertEquals(jni.updateAssetManagerCalled, 1); - assertEquals(jni.dynamicFeatureInstallFailureCalled, 0); + assertEquals(jni.deferredComponentInstallFailureCalled, 0); assertEquals(jni.assetManager, assetManager); } + + @Test + public void stateGetterReturnsUnknowByDefault() throws NameNotFoundException { + TestFlutterJNI jni = new TestFlutterJNI(); + Context spyContext = spy(RuntimeEnvironment.systemContext); + doReturn(spyContext).when(spyContext).createPackageContext(any(), anyInt()); + TestPlayStoreDeferredComponentManager playStoreManager = + new TestPlayStoreDeferredComponentManager(spyContext, jni); + assertEquals(playStoreManager.getDeferredComponentInstallState(-1, "invalidName"), "unknown"); + } } diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/systemchannels/DeferredComponentChannelTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/systemchannels/DeferredComponentChannelTest.java new file mode 100644 index 00000000000..8aec57c0b2c --- /dev/null +++ b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/systemchannels/DeferredComponentChannelTest.java @@ -0,0 +1,123 @@ +package io.flutter.embedding.engine.systemchannels; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.content.res.AssetManager; +import io.flutter.embedding.engine.FlutterJNI; +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import java.util.HashMap; +import java.util.Map; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +class TestDeferredComponentManager implements DeferredComponentManager { + DeferredComponentChannel channel; + String moduleName; + + public void setJNI(FlutterJNI flutterJNI) {} + + public void setDeferredComponentChannel(DeferredComponentChannel channel) { + this.channel = channel; + } + + public void installDeferredComponent(int loadingUnitId, String moduleName) { + this.moduleName = moduleName; + } + + public void completeInstall() { + channel.completeInstallSuccess(moduleName); + } + + public String getDeferredComponentInstallState(int loadingUnitId, String moduleName) { + return "installed"; + } + + public void loadAssets(int loadingUnitId, String moduleName) {} + + public void loadDartLibrary(int loadingUnitId, String moduleName) {} + + public boolean uninstallDeferredComponent(int loadingUnitId, String moduleName) { + return true; + } + + public void destroy() {} +} + +@Config(manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class DeferredComponentChannelTest { + @Test + public void deferredComponentChannel_installCompletesResults() { + MethodChannel rawChannel = mock(MethodChannel.class); + FlutterJNI mockFlutterJNI = mock(FlutterJNI.class); + DartExecutor dartExecutor = new DartExecutor(mockFlutterJNI, mock(AssetManager.class)); + TestDeferredComponentManager testDeferredComponentManager = new TestDeferredComponentManager(); + DeferredComponentChannel fakeDeferredComponentChannel = + new DeferredComponentChannel(dartExecutor); + fakeDeferredComponentChannel.setDeferredComponentManager(testDeferredComponentManager); + testDeferredComponentManager.setDeferredComponentChannel(fakeDeferredComponentChannel); + + Map args = new HashMap<>(); + args.put("loadingUnitId", -1); + args.put("moduleName", "hello"); + MethodCall methodCall = new MethodCall("installDeferredComponent", args); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + fakeDeferredComponentChannel.parsingMethodHandler.onMethodCall(methodCall, mockResult); + + testDeferredComponentManager.completeInstall(); + verify(mockResult).success(null); + } + + @Test + public void deferredComponentChannel_installCompletesMultipleResults() { + MethodChannel rawChannel = mock(MethodChannel.class); + FlutterJNI mockFlutterJNI = mock(FlutterJNI.class); + DartExecutor dartExecutor = new DartExecutor(mockFlutterJNI, mock(AssetManager.class)); + TestDeferredComponentManager testDeferredComponentManager = new TestDeferredComponentManager(); + DeferredComponentChannel fakeDeferredComponentChannel = + new DeferredComponentChannel(dartExecutor); + fakeDeferredComponentChannel.setDeferredComponentManager(testDeferredComponentManager); + testDeferredComponentManager.setDeferredComponentChannel(fakeDeferredComponentChannel); + + Map args = new HashMap<>(); + args.put("loadingUnitId", -1); + args.put("moduleName", "hello"); + MethodCall methodCall = new MethodCall("installDeferredComponent", args); + MethodChannel.Result mockResult1 = mock(MethodChannel.Result.class); + MethodChannel.Result mockResult2 = mock(MethodChannel.Result.class); + fakeDeferredComponentChannel.parsingMethodHandler.onMethodCall(methodCall, mockResult1); + fakeDeferredComponentChannel.parsingMethodHandler.onMethodCall(methodCall, mockResult2); + + testDeferredComponentManager.completeInstall(); + verify(mockResult1).success(null); + verify(mockResult2).success(null); + } + + @Test + public void deferredComponentChannel_getInstallState() { + MethodChannel rawChannel = mock(MethodChannel.class); + FlutterJNI mockFlutterJNI = mock(FlutterJNI.class); + DartExecutor dartExecutor = new DartExecutor(mockFlutterJNI, mock(AssetManager.class)); + TestDeferredComponentManager testDeferredComponentManager = new TestDeferredComponentManager(); + DeferredComponentChannel fakeDeferredComponentChannel = + new DeferredComponentChannel(dartExecutor); + fakeDeferredComponentChannel.setDeferredComponentManager(testDeferredComponentManager); + testDeferredComponentManager.setDeferredComponentChannel(fakeDeferredComponentChannel); + + Map args = new HashMap<>(); + args.put("loadingUnitId", -1); + args.put("moduleName", "hello"); + MethodCall methodCall = new MethodCall("getDeferredComponentInstallState", args); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + fakeDeferredComponentChannel.parsingMethodHandler.onMethodCall(methodCall, mockResult); + + testDeferredComponentManager.completeInstall(); + verify(mockResult).success("installed"); + } +} diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/systemchannels/DynamicFeatureChannelTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/systemchannels/DynamicFeatureChannelTest.java deleted file mode 100644 index 591d063b2a8..00000000000 --- a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/systemchannels/DynamicFeatureChannelTest.java +++ /dev/null @@ -1,115 +0,0 @@ -package io.flutter.embedding.engine.systemchannels; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import android.content.res.AssetManager; -import io.flutter.embedding.engine.FlutterJNI; -import io.flutter.embedding.engine.dart.DartExecutor; -import io.flutter.embedding.engine.dynamicfeatures.DynamicFeatureManager; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; -import java.util.HashMap; -import java.util.Map; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -class TestDynamicFeatureManager implements DynamicFeatureManager { - DynamicFeatureChannel channel; - String moduleName; - - public void setJNI(FlutterJNI flutterJNI) {} - - public void setDynamicFeatureChannel(DynamicFeatureChannel channel) { - this.channel = channel; - } - - public void installDynamicFeature(int loadingUnitId, String moduleName) { - this.moduleName = moduleName; - } - - public void completeInstall() { - channel.completeInstallSuccess(moduleName); - } - - public String getDynamicFeatureInstallState(int loadingUnitId, String moduleName) { - return "installed"; - } - - public void loadAssets(int loadingUnitId, String moduleName) {} - - public void loadDartLibrary(int loadingUnitId, String moduleName) {} - - public void uninstallFeature(int loadingUnitId, String moduleName) {} - - public void destroy() {} -} - -@Config(manifest = Config.NONE) -@RunWith(RobolectricTestRunner.class) -public class DynamicFeatureChannelTest { - @Test - public void dynamicFeatureChannel_installCompletesResults() { - MethodChannel rawChannel = mock(MethodChannel.class); - FlutterJNI mockFlutterJNI = mock(FlutterJNI.class); - DartExecutor dartExecutor = new DartExecutor(mockFlutterJNI, mock(AssetManager.class)); - TestDynamicFeatureManager testDynamicFeatureManager = new TestDynamicFeatureManager(); - DynamicFeatureChannel fakeDynamicFeatureChannel = new DynamicFeatureChannel(dartExecutor); - fakeDynamicFeatureChannel.setDynamicFeatureManager(testDynamicFeatureManager); - - Map args = new HashMap<>(); - args.put("loadingUnitId", -1); - args.put("moduleName", "hello"); - MethodCall methodCall = new MethodCall("installDynamicFeature", args); - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - fakeDynamicFeatureChannel.parsingMethodHandler.onMethodCall(methodCall, mockResult); - - testDynamicFeatureManager.completeInstall(); - verify(mockResult).success(null); - } - - @Test - public void dynamicFeatureChannel_installCompletesMultipleResults() { - MethodChannel rawChannel = mock(MethodChannel.class); - FlutterJNI mockFlutterJNI = mock(FlutterJNI.class); - DartExecutor dartExecutor = new DartExecutor(mockFlutterJNI, mock(AssetManager.class)); - TestDynamicFeatureManager testDynamicFeatureManager = new TestDynamicFeatureManager(); - DynamicFeatureChannel fakeDynamicFeatureChannel = new DynamicFeatureChannel(dartExecutor); - fakeDynamicFeatureChannel.setDynamicFeatureManager(testDynamicFeatureManager); - - Map args = new HashMap<>(); - args.put("loadingUnitId", -1); - args.put("moduleName", "hello"); - MethodCall methodCall = new MethodCall("installDynamicFeature", args); - MethodChannel.Result mockResult1 = mock(MethodChannel.Result.class); - MethodChannel.Result mockResult2 = mock(MethodChannel.Result.class); - fakeDynamicFeatureChannel.parsingMethodHandler.onMethodCall(methodCall, mockResult1); - fakeDynamicFeatureChannel.parsingMethodHandler.onMethodCall(methodCall, mockResult2); - - testDynamicFeatureManager.completeInstall(); - verify(mockResult1).success(null); - verify(mockResult2).success(null); - } - - @Test - public void dynamicFeatureChannel_getInstallState() { - MethodChannel rawChannel = mock(MethodChannel.class); - FlutterJNI mockFlutterJNI = mock(FlutterJNI.class); - DartExecutor dartExecutor = new DartExecutor(mockFlutterJNI, mock(AssetManager.class)); - TestDynamicFeatureManager testDynamicFeatureManager = new TestDynamicFeatureManager(); - DynamicFeatureChannel fakeDynamicFeatureChannel = new DynamicFeatureChannel(dartExecutor); - fakeDynamicFeatureChannel.setDynamicFeatureManager(testDynamicFeatureManager); - - Map args = new HashMap<>(); - args.put("loadingUnitId", -1); - args.put("moduleName", "hello"); - MethodCall methodCall = new MethodCall("getDynamicFeatureInstallState", args); - MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - fakeDynamicFeatureChannel.parsingMethodHandler.onMethodCall(methodCall, mockResult); - - testDynamicFeatureManager.completeInstall(); - verify(mockResult).success("installed"); - } -}