diff --git a/engine/src/flutter/shell/common/shell.cc b/engine/src/flutter/shell/common/shell.cc index b6c9f859553..1a98703fe99 100644 --- a/engine/src/flutter/shell/common/shell.cc +++ b/engine/src/flutter/shell/common/shell.cc @@ -1313,8 +1313,15 @@ void Shell::LoadDartDeferredLibrary( intptr_t loading_unit_id, std::unique_ptr snapshot_data, std::unique_ptr snapshot_instructions) { - engine_->LoadDartDeferredLibrary(loading_unit_id, std::move(snapshot_data), - std::move(snapshot_instructions)); + task_runners_.GetUITaskRunner()->PostTask(fml::MakeCopyable( + [engine = engine_->GetWeakPtr(), loading_unit_id, + data = std::move(snapshot_data), + instructions = std::move(snapshot_instructions)]() mutable { + if (engine) { + engine->LoadDartDeferredLibrary(loading_unit_id, std::move(data), + std::move(instructions)); + } + })); } void Shell::LoadDartDeferredLibraryError(intptr_t loading_unit_id, diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java index 062956f673e..156531eb011 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java @@ -425,18 +425,34 @@ public class PlayStoreDeferredComponentManager implements DeferredComponentManag List apkPaths = new ArrayList<>(); // If not found in APKs, we check in extracted native libs for the lib directly. List soPaths = new ArrayList<>(); + Queue searchFiles = new LinkedList<>(); + // Downloaded modules are stored here searchFiles.add(context.getFilesDir()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // The initial installed apks are provided by `sourceDirs` in ApplicationInfo. + // The jniLibs we want are in the splits not the baseDir. These + // APKs are only searched as a fallback, as base libs generally do not need + // to be fully path referenced. + for (String path : context.getApplicationInfo().splitSourceDirs) { + searchFiles.add(new File(path)); + } + } + while (!searchFiles.isEmpty()) { File file = searchFiles.remove(); - if (file != null && file.isDirectory()) { + if (file != null && file.isDirectory() && file.listFiles() != null) { for (File f : file.listFiles()) { searchFiles.add(f); } continue; } String name = file.getName(); - if (name.endsWith(".apk") && name.startsWith(componentName) && name.contains(pathAbi)) { + // Special case for "split_config" since android base module non-master apks are + // initially installed with the "split_config" prefix/name. + if (name.endsWith(".apk") + && (name.startsWith(componentName) || name.startsWith("split_config")) + && name.contains(pathAbi)) { apkPaths.add(file.getAbsolutePath()); continue; } @@ -459,7 +475,7 @@ public class PlayStoreDeferredComponentManager implements DeferredComponentManag } flutterJNI.loadDartDeferredLibrary( - loadingUnitId, searchPaths.toArray(new String[apkPaths.size()])); + loadingUnitId, searchPaths.toArray(new String[searchPaths.size()])); } public boolean uninstallDeferredComponent(int loadingUnitId, String componentName) { diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManagerTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManagerTest.java index 2a2a8b31215..71d4ddb38d3 100644 --- a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManagerTest.java +++ b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManagerTest.java @@ -14,6 +14,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import android.annotation.TargetApi; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -32,6 +33,7 @@ import org.robolectric.annotation.Config; @Config(manifest = Config.NONE) @RunWith(RobolectricTestRunner.class) +@TargetApi(21) public class PlayStoreDeferredComponentManagerTest { private class TestFlutterJNI extends FlutterJNI { public int loadDartDeferredLibraryCalled = 0; @@ -90,9 +92,12 @@ public class PlayStoreDeferredComponentManagerTest { PackageManager packageManager = mock(PackageManager.class); ApplicationInfo applicationInfo = mock(ApplicationInfo.class); applicationInfo.metaData = metadata; + applicationInfo.splitSourceDirs = new String[1]; + applicationInfo.splitSourceDirs[0] = "some.invalid.apk"; when(packageManager.getApplicationInfo(any(String.class), any(int.class))) .thenReturn(applicationInfo); doReturn(packageManager).when(spyContext).getPackageManager(); + doReturn(applicationInfo).when(spyContext).getApplicationInfo(); return spyContext; } @@ -239,6 +244,30 @@ public class PlayStoreDeferredComponentManagerTest { assertEquals(jni.loadingUnitId, 123); } + @Test + public void searchPathsSearchesSplitConfig() throws NameNotFoundException { + TestFlutterJNI jni = new TestFlutterJNI(); + Context spyContext = createSpyContext(null); + doReturn(null).when(spyContext).getAssets(); + String apkTestPath = "test/path/split_config.armeabi_v7a.apk"; + doReturn(new File(apkTestPath)).when(spyContext).getFilesDir(); + TestPlayStoreDeferredComponentManager playStoreManager = + new TestPlayStoreDeferredComponentManager(spyContext, jni); + jni.setDeferredComponentManager(playStoreManager); + + assertEquals(jni.loadingUnitId, 0); + + playStoreManager.installDeferredComponent(123, "TestModuleName"); + assertEquals(jni.loadDartDeferredLibraryCalled, 1); + assertEquals(jni.updateAssetManagerCalled, 1); + assertEquals(jni.deferredComponentInstallFailureCalled, 0); + + assertEquals(jni.searchPaths[0], "libapp.so-123.part.so"); + assertTrue(jni.searchPaths[1].endsWith(apkTestPath + "!lib/armeabi-v7a/libapp.so-123.part.so")); + assertEquals(jni.searchPaths.length, 2); + assertEquals(jni.loadingUnitId, 123); + } + @Test public void invalidSearchPathsAreIgnored() throws NameNotFoundException { TestFlutterJNI jni = new TestFlutterJNI();