diff --git a/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java b/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java index 6a3f2147afa09..c5c7939930aa4 100644 --- a/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java +++ b/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java @@ -248,12 +248,14 @@ private ApplicationInfo getApplicationInfo() { // Obtain and parses the metadata string. An example encoded string is: // - // "2:component2,3:component3,4:component1:libcomponent4.so" + // "2:component2,3:component3,4:component1:libcomponent4.so,5:" // // Where loading unit 2 is included in component2, loading unit 3 is // included in component3, and loading unit 4 is included in component1. // An optional third parameter can be added to indicate the name of - // the shared library of the loading unit. + // the shared library of the loading unit. Loading unit 5 maps to an empty + // string, indicating it is included in the base module and no dynamic + // feature modules need to be downloaded. private void initLoadingUnitMappingToComponentNames() { String mappingKey = DeferredComponentManager.class.getName() + ".loadingUnitMapping"; ApplicationInfo applicationInfo = getApplicationInfo(); @@ -269,7 +271,8 @@ private void initLoadingUnitMappingToComponentNames() { + "' is defined in the base module's AndroidManifest."); } else { for (String entry : rawMappingString.split(",")) { - String[] splitEntry = entry.split(":"); + // Split with -1 param to include empty string following trailing ":" + String[] splitEntry = entry.split(":", -1); int loadingUnitId = Integer.parseInt(splitEntry[0]); loadingUnitIdToComponentNames.put(loadingUnitId, splitEntry[1]); if (splitEntry.length > 2) { @@ -290,6 +293,13 @@ public void installDeferredComponent(int loadingUnitId, String componentName) { return; } + // Handle a loading unit that is included in the base module that does not need download. + if (resolvedComponentName.equals("") && loadingUnitId > 0) { + // No need to load assets as base assets are already loaded. + loadDartLibrary(loadingUnitId, resolvedComponentName); + return; + } + SplitInstallRequest request = SplitInstallRequest.newBuilder().addModule(resolvedComponentName).build(); diff --git a/shell/platform/android/test/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManagerTest.java b/shell/platform/android/test/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManagerTest.java index f6347dad1c698..10355120919f9 100644 --- a/shell/platform/android/test/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManagerTest.java +++ b/shell/platform/android/test/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManagerTest.java @@ -184,6 +184,37 @@ public void downloadCallsJNIFunctionsWithSharedLibraryNameFromManifest() assertEquals(jni.assetBundlePath, "custom_assets"); } + @Test + public void manifestMappingHandlesBaseModuleEmptyString() throws NameNotFoundException { + TestFlutterJNI jni = new TestFlutterJNI(); + + Bundle bundle = new Bundle(); + bundle.putString( + PlayStoreDeferredComponentManager.MAPPING_KEY, "123:module:custom_name.so,3:,4:"); + bundle.putString(ApplicationInfoLoader.PUBLIC_FLUTTER_ASSETS_DIR_KEY, "custom_assets"); + + Context spyContext = createSpyContext(bundle); + doReturn(null).when(spyContext).getAssets(); + + String soTestFilename = "libapp.so-3.part.so"; + String soTestPath = "test/path/" + soTestFilename; + doReturn(new File(soTestPath)).when(spyContext).getFilesDir(); + PlayStoreDeferredComponentManager playStoreManager = + new PlayStoreDeferredComponentManager(spyContext, jni); + jni.setDeferredComponentManager(playStoreManager); + assertEquals(jni.loadingUnitId, 0); + + playStoreManager.installDeferredComponent(3, null); + assertEquals(jni.loadDartDeferredLibraryCalled, 1); + assertEquals(jni.updateAssetManagerCalled, 0); // no assets to load for base + assertEquals(jni.deferredComponentInstallFailureCalled, 0); + + assertEquals(jni.searchPaths[0], soTestFilename); + assertTrue(jni.searchPaths[1].endsWith(soTestPath)); + assertEquals(jni.searchPaths.length, 2); + assertEquals(jni.loadingUnitId, 3); + } + @Test public void searchPathsAddsApks() throws NameNotFoundException { TestFlutterJNI jni = new TestFlutterJNI();