Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1110,21 +1110,19 @@ public void requestDartDeferredLibrary(int loadingUnitId) {
* @param loadingUnitId The loadingUnitId is assigned during compile time by gen_snapshot and is
* automatically retrieved when loadLibrary() is called on a dart deferred library. This is
* used to identify which Dart deferred library the resolved correspond to.
* @param searchPaths An array of paths in which to look for valid dart shared libraries. This
* supports paths within zipped apks as long as the apks are not compressed using the
* `path/to/apk.apk!path/inside/apk/lib.so` format. Paths will be tried first to last and ends
* when a library is sucessfully found. When the found library is invalid, no additional paths
* will be attempted.
* @param sharedLibraryName File name of the .so file to be loaded, or if the file is not already
* in LD_LIBRARY_PATH, the full path to the file. The .so files in the lib/[abi] directory are
* already in LD_LIBRARY_PATH and in this case you only need to pass the file name.
*/
@UiThread
public void loadDartDeferredLibrary(int loadingUnitId, @NonNull String[] searchPaths) {
public void loadDartDeferredLibrary(int loadingUnitId, @NonNull String sharedLibraryName) {
ensureRunningOnMainThread();
ensureAttachedToNative();
nativeLoadDartDeferredLibrary(nativeShellHolderId, loadingUnitId, searchPaths);
nativeLoadDartDeferredLibrary(nativeShellHolderId, loadingUnitId, sharedLibraryName);
}

private native void nativeLoadDartDeferredLibrary(
long nativeShellHolderId, int loadingUnitId, @NonNull String[] searchPaths);
long nativeShellHolderId, int loadingUnitId, @NonNull String sharedLibraryName);

/**
* Adds the specified AssetManager as an APKAssetResolver in the Flutter Engine's AssetManager.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@
* 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 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.
* loadDartLibrary should be invoked. loadDartLibrary should pass the file name of the shared
* library .so file to FlutterJNI.loadDartDeferredLibrary for the engine to dlopen, or if the file
* is not in LD_LIBRARY_PATH, it should find the shared library .so file and pass the full path.
* 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.
*
* <p>The loadAssets and loadDartLibrary methods are separated out because they may also be called
* manually via platform channel messages. A full installDeferredComponent implementation should
Expand Down Expand Up @@ -182,14 +183,10 @@ public interface DeferredComponentManager {
* Load the .so shared library file into the Dart VM.
*
* <p>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.
*
* <p>Specifically, APKs distributed by Android's app bundle format may vary by device and API
* number, so FlutterJNI's loadDartDeferredLibrary accepts a list of search paths with can include
* paths within APKs that have not been unpacked using the
* `path/to/apk.apk!path/inside/apk/lib.so` format. Each search path will be attempted in order
* until a shared library is found. This allows for the developer to avoid unpacking the apk zip.
* find the .so library file. The filenames, or path if it's not in LD_LIBRARY_PATH, should then
* be passed to FlutterJNI.loadDartDeferredLibrary to be dlopen-ed and loaded into the Dart VM.
* The .so files in the lib/[abi] directory are already in LD_LIBRARY_PATH and in this case you
* only need to pass the file name.
*
* <p>Upon successful load of the Dart library, the Dart future from the originating loadLibary()
* call completes and developers are able to use symbols and assets from the feature module.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.AssetManager;
import android.os.Build;
import android.util.SparseArray;
import android.util.SparseIntArray;
import androidx.annotation.NonNull;
Expand All @@ -23,14 +22,13 @@
import com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus;
import io.flutter.Log;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.loader.ApplicationInfoLoader;
import io.flutter.embedding.engine.loader.FlutterApplicationInfo;
import io.flutter.embedding.engine.systemchannels.DeferredComponentChannel;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;

/**
* Flutter default implementation of DeferredComponentManager that downloads deferred component
Expand All @@ -43,6 +41,7 @@ public class PlayStoreDeferredComponentManager implements DeferredComponentManag
private @Nullable FlutterJNI flutterJNI;
private @Nullable DeferredComponentChannel channel;
private @NonNull Context context;
private @NonNull FlutterApplicationInfo flutterApplicationInfo;
// 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.
private @NonNull SparseArray<String> sessionIdToName;
Expand Down Expand Up @@ -191,6 +190,7 @@ public PlayStoreDeferredComponentManager(
@NonNull Context context, @Nullable FlutterJNI flutterJNI) {
this.context = context;
this.flutterJNI = flutterJNI;
this.flutterApplicationInfo = ApplicationInfoLoader.load(context);
splitInstallManager = SplitInstallManagerFactory.create(context);
listener = new FeatureInstallStateUpdatedListener();
splitInstallManager.registerListener(listener);
Expand Down Expand Up @@ -322,10 +322,7 @@ public void loadAssets(int loadingUnitId, String moduleName) {
context = context.createPackageContext(context.getPackageName(), 0);

AssetManager assetManager = context.getAssets();
flutterJNI.updateJavaAssetManager(
assetManager,
// TODO(garyq): Made the "flutter_assets" directory dynamic based off of DartEntryPoint.
"flutter_assets");
flutterJNI.updateJavaAssetManager(assetManager, flutterApplicationInfo.flutterAssetsDir);
} catch (NameNotFoundException e) {
throw new RuntimeException(e);
}
Expand All @@ -341,54 +338,10 @@ public void loadDartLibrary(int loadingUnitId, String moduleName) {
}

// This matches/depends on dart's loading unit naming convention, which we use unchanged.
String aotSharedLibraryName = "app.so-" + loadingUnitId + ".part.so";
String aotSharedLibraryName =
flutterApplicationInfo.aotSharedLibraryName + "-" + loadingUnitId + ".part.so";

// Possible values: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips, mips64
String abi;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
abi = Build.SUPPORTED_ABIS[0];
} else {
abi = Build.CPU_ABI;
}
String pathAbi = abi.replace("-", "_"); // abis are represented with underscores in paths.

// TODO(garyq): Optimize this apk/file discovery process to use less i/o and be more
// performant and robust.

// Search directly in APKs first
List<String> apkPaths = new ArrayList<>();
// If not found in APKs, we check in extracted native libs for the lib directly.
List<String> soPaths = new ArrayList<>();
Queue<File> searchFiles = new LinkedList<>();
searchFiles.add(context.getFilesDir());
while (!searchFiles.isEmpty()) {
File file = searchFiles.remove();
if (file != null && file.isDirectory()) {
for (File f : file.listFiles()) {
searchFiles.add(f);
}
continue;
}
String name = file.getName();
if (name.endsWith(".apk") && name.startsWith(moduleName) && name.contains(pathAbi)) {
apkPaths.add(file.getAbsolutePath());
continue;
}
if (name.equals(aotSharedLibraryName)) {
soPaths.add(file.getAbsolutePath());
}
}

List<String> searchPaths = new ArrayList<>();
for (String path : apkPaths) {
searchPaths.add(path + "!lib/" + abi + "/" + aotSharedLibraryName);
}
for (String path : soPaths) {
searchPaths.add(path);
}

flutterJNI.loadDartDeferredLibrary(
loadingUnitId, searchPaths.toArray(new String[apkPaths.size()]));
flutterJNI.loadDartDeferredLibrary(loadingUnitId, aotSharedLibraryName);
}

public boolean uninstallDeferredComponent(int loadingUnitId, String moduleName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@
import org.xmlpull.v1.XmlPullParserException;

/** Loads application information given a Context. */
final class ApplicationInfoLoader {
public final class ApplicationInfoLoader {
// XML Attribute keys supported in AndroidManifest.xml
static final String PUBLIC_AOT_SHARED_LIBRARY_NAME =
public static final String PUBLIC_AOT_SHARED_LIBRARY_NAME =
FlutterLoader.class.getName() + '.' + FlutterLoader.AOT_SHARED_LIBRARY_NAME;
static final String PUBLIC_VM_SNAPSHOT_DATA_KEY =
public static final String PUBLIC_VM_SNAPSHOT_DATA_KEY =
FlutterLoader.class.getName() + '.' + FlutterLoader.VM_SNAPSHOT_DATA_KEY;
static final String PUBLIC_ISOLATE_SNAPSHOT_DATA_KEY =
public static final String PUBLIC_ISOLATE_SNAPSHOT_DATA_KEY =
FlutterLoader.class.getName() + '.' + FlutterLoader.ISOLATE_SNAPSHOT_DATA_KEY;
static final String PUBLIC_FLUTTER_ASSETS_DIR_KEY =
public static final String PUBLIC_FLUTTER_ASSETS_DIR_KEY =
FlutterLoader.class.getName() + '.' + FlutterLoader.FLUTTER_ASSETS_DIR_KEY;
static final String NETWORK_POLICY_METADATA_KEY = "io.flutter.network-policy";
public static final String NETWORK_POLICY_METADATA_KEY = "io.flutter.network-policy";

@NonNull
private static ApplicationInfo getApplicationInfo(@NonNull Context applicationContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ public final class FlutterApplicationInfo {
private static final String DEFAULT_ISOLATE_SNAPSHOT_DATA = "isolate_snapshot_data";
private static final String DEFAULT_FLUTTER_ASSETS_DIR = "flutter_assets";

final String aotSharedLibraryName;
final String vmSnapshotData;
final String isolateSnapshotData;
final String flutterAssetsDir;
final String domainNetworkPolicy;
final String nativeLibraryDir;
final boolean clearTextPermitted;
public final String aotSharedLibraryName;
public final String vmSnapshotData;
public final String isolateSnapshotData;
public final String flutterAssetsDir;
public final String domainNetworkPolicy;
public final String nativeLibraryDir;
public final boolean clearTextPermitted;

public FlutterApplicationInfo(
String aotSharedLibraryName,
Expand Down
18 changes: 7 additions & 11 deletions shell/platform/android/platform_view_android_jni_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -567,23 +567,19 @@ static void LoadDartDeferredLibrary(JNIEnv* env,
jobject obj,
jlong shell_holder,
jint jLoadingUnitId,
jobjectArray jSearchPaths) {
jstring jSharedLibraryName) {
// Convert java->c++
intptr_t loading_unit_id = static_cast<intptr_t>(jLoadingUnitId);
std::vector<std::string> search_paths =
fml::jni::StringArrayToVector(env, jSearchPaths);
std::string sharedLibraryName =
fml::jni::JavaStringToString(env, jSharedLibraryName);

// Use dlopen here to directly check if handle is nullptr before creating a
// NativeLibrary.
void* handle = nullptr;
while (handle == nullptr && !search_paths.empty()) {
std::string path = search_paths.back();
handle = ::dlopen(path.c_str(), RTLD_NOW);
search_paths.pop_back();
}
void* handle = ::dlopen(sharedLibraryName.c_str(), RTLD_NOW);
if (handle == nullptr) {
LoadLoadingUnitFailure(loading_unit_id,
"No lib .so found for provided search paths.", true);
"Shared library not found for the provided name.",
true);
return;
}
fml::RefPtr<fml::NativeLibrary> native_lib =
Expand Down Expand Up @@ -781,7 +777,7 @@ bool RegisterApi(JNIEnv* env) {
},
{
.name = "nativeLoadDartDeferredLibrary",
.signature = "(JI[Ljava/lang/String;)V",
.signature = "(JILjava/lang/String;)V",
.fnPtr = reinterpret_cast<void*>(&LoadDartDeferredLibrary),
},
{
Expand Down
Loading