diff --git a/shell/common/switches.cc b/shell/common/switches.cc index 7094f98cbf71f..a048b62bc4d28 100644 --- a/shell/common/switches.cc +++ b/shell/common/switches.cc @@ -235,42 +235,42 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { command_line.GetOptionValue(FlagForSwitch(Switch::FlutterAssetsDir), &settings.assets_path); - std::string aot_shared_library_name; - command_line.GetOptionValue(FlagForSwitch(Switch::AotSharedLibraryName), - &aot_shared_library_name); + std::string aot_shared_library_path; + command_line.GetOptionValue(FlagForSwitch(Switch::AotSharedLibraryPath), + &aot_shared_library_path); - std::string snapshot_asset_path; - command_line.GetOptionValue(FlagForSwitch(Switch::SnapshotAssetPath), - &snapshot_asset_path); + std::string aot_snapshot_path; + command_line.GetOptionValue(FlagForSwitch(Switch::AotSnapshotPath), + &aot_snapshot_path); - std::string vm_snapshot_data_filename; - command_line.GetOptionValue(FlagForSwitch(Switch::VmSnapshotData), - &vm_snapshot_data_filename); + std::string aot_vm_snapshot_data_filename; + command_line.GetOptionValue(FlagForSwitch(Switch::AotVmSnapshotData), + &aot_vm_snapshot_data_filename); - std::string vm_snapshot_instr_filename; - command_line.GetOptionValue(FlagForSwitch(Switch::VmSnapshotInstructions), - &vm_snapshot_instr_filename); + std::string aot_vm_snapshot_instr_filename; + command_line.GetOptionValue(FlagForSwitch(Switch::AotVmSnapshotInstructions), + &aot_vm_snapshot_instr_filename); - std::string isolate_snapshot_data_filename; - command_line.GetOptionValue(FlagForSwitch(Switch::IsolateSnapshotData), - &isolate_snapshot_data_filename); + std::string aot_isolate_snapshot_data_filename; + command_line.GetOptionValue(FlagForSwitch(Switch::AotIsolateSnapshotData), + &aot_isolate_snapshot_data_filename); - std::string isolate_snapshot_instr_filename; + std::string aot_isolate_snapshot_instr_filename; command_line.GetOptionValue( - FlagForSwitch(Switch::IsolateSnapshotInstructions), - &isolate_snapshot_instr_filename); - - if (aot_shared_library_name.size() > 0) { - settings.application_library_path = aot_shared_library_name; - } else if (snapshot_asset_path.size() > 0) { - settings.vm_snapshot_data_path = - fml::paths::JoinPaths({snapshot_asset_path, vm_snapshot_data_filename}); + FlagForSwitch(Switch::AotIsolateSnapshotInstructions), + &aot_isolate_snapshot_instr_filename); + + if (aot_shared_library_path.size() > 0) { + settings.application_library_path = aot_shared_library_path; + } else if (aot_snapshot_path.size() > 0) { + settings.vm_snapshot_data_path = fml::paths::JoinPaths( + {aot_snapshot_path, aot_vm_snapshot_data_filename}); settings.vm_snapshot_instr_path = fml::paths::JoinPaths( - {snapshot_asset_path, vm_snapshot_instr_filename}); + {aot_snapshot_path, aot_vm_snapshot_instr_filename}); settings.isolate_snapshot_data_path = fml::paths::JoinPaths( - {snapshot_asset_path, isolate_snapshot_data_filename}); + {aot_snapshot_path, aot_isolate_snapshot_data_filename}); settings.isolate_snapshot_instr_path = fml::paths::JoinPaths( - {snapshot_asset_path, isolate_snapshot_instr_filename}); + {aot_snapshot_path, aot_isolate_snapshot_instr_filename}); } command_line.GetOptionValue(FlagForSwitch(Switch::CacheDirPath), diff --git a/shell/common/switches.h b/shell/common/switches.h index 7afc7668228ed..73c00d97a0953 100644 --- a/shell/common/switches.h +++ b/shell/common/switches.h @@ -24,30 +24,28 @@ namespace flutter { // clang-format on DEF_SWITCHES_START -DEF_SWITCH(AotSharedLibraryName, - "aot-shared-library-name", - "Name of the *.so containing AOT compiled Dart assets.") -DEF_SWITCH(SnapshotAssetPath, - "snapshot-asset-path", +DEF_SWITCH(AotSharedLibraryPath, "aot-shared-library-path", "Path to the *.so.") +DEF_SWITCH(AotSnapshotPath, + "aot-snapshot-path", "Path to the directory containing the four files specified by " - "VmSnapshotData, VmSnapshotInstructions, " - "VmSnapshotInstructions and IsolateSnapshotInstructions.") -DEF_SWITCH(VmSnapshotData, + "AotVmSnapshotData, AotVmSnapshotInstructions, " + "AotVmSnapshotInstructions and AotIsolateSnapshotInstructions.") +DEF_SWITCH(AotVmSnapshotData, "vm-snapshot-data", "The VM snapshot data that will be memory mapped as read-only. " - "SnapshotAssetPath must be present.") -DEF_SWITCH(VmSnapshotInstructions, + "AotSnapshotPath must be present.") +DEF_SWITCH(AotVmSnapshotInstructions, "vm-snapshot-instr", "The VM instructions snapshot that will be memory mapped as read " - "and executable. SnapshotAssetPath must be present.") -DEF_SWITCH(IsolateSnapshotData, + "and executable. AotSnapshotPath must be present.") +DEF_SWITCH(AotIsolateSnapshotData, "isolate-snapshot-data", "The isolate snapshot data that will be memory mapped as read-only. " - "SnapshotAssetPath must be present.") -DEF_SWITCH(IsolateSnapshotInstructions, + "AotSnapshotPath must be present.") +DEF_SWITCH(AotIsolateSnapshotInstructions, "isolate-snapshot-instr", "The isolate instructions snapshot that will be memory mapped as " - "read and executable. SnapshotAssetPath must be present.") + "read and executable. AotSnapshotPath must be present.") DEF_SWITCH(CacheDirPath, "cache-dir-path", "Path to the cache directory.") DEF_SWITCH(ICUDataFilePath, "icu-data-file-path", "Path to the ICU data file.") DEF_SWITCH(ICUSymbolPrefix, diff --git a/shell/platform/android/io/flutter/view/FlutterMain.java b/shell/platform/android/io/flutter/view/FlutterMain.java index 01c4aca79104a..cb965fb40bc4c 100644 --- a/shell/platform/android/io/flutter/view/FlutterMain.java +++ b/shell/platform/android/io/flutter/view/FlutterMain.java @@ -15,7 +15,6 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; -import io.flutter.BuildConfig; import io.flutter.util.PathUtils; import java.io.File; @@ -29,26 +28,34 @@ public class FlutterMain { private static final String TAG = "FlutterMain"; // Must match values in sky::switches - private static final String AOT_SHARED_LIBRARY_NAME = "aot-shared-library-name"; - private static final String SNAPSHOT_ASSET_PATH_KEY = "snapshot-asset-path"; - private static final String VM_SNAPSHOT_DATA_KEY = "vm-snapshot-data"; - private static final String ISOLATE_SNAPSHOT_DATA_KEY = "isolate-snapshot-data"; + private static final String AOT_SHARED_LIBRARY_PATH = "aot-shared-library-path"; + private static final String AOT_SNAPSHOT_PATH_KEY = "aot-snapshot-path"; + private static final String AOT_VM_SNAPSHOT_DATA_KEY = "vm-snapshot-data"; + private static final String AOT_VM_SNAPSHOT_INSTR_KEY = "vm-snapshot-instr"; + private static final String AOT_ISOLATE_SNAPSHOT_DATA_KEY = "isolate-snapshot-data"; + private static final String AOT_ISOLATE_SNAPSHOT_INSTR_KEY = "isolate-snapshot-instr"; private static final String FLUTTER_ASSETS_DIR_KEY = "flutter-assets-dir"; // XML Attribute keys supported in AndroidManifest.xml - public static final String PUBLIC_AOT_SHARED_LIBRARY_NAME = - FlutterMain.class.getName() + '.' + AOT_SHARED_LIBRARY_NAME; - public static final String PUBLIC_VM_SNAPSHOT_DATA_KEY = - FlutterMain.class.getName() + '.' + VM_SNAPSHOT_DATA_KEY; - public static final String PUBLIC_ISOLATE_SNAPSHOT_DATA_KEY = - FlutterMain.class.getName() + '.' + ISOLATE_SNAPSHOT_DATA_KEY; + public static final String PUBLIC_AOT_AOT_SHARED_LIBRARY_PATH = + FlutterMain.class.getName() + '.' + AOT_SHARED_LIBRARY_PATH; + public static final String PUBLIC_AOT_VM_SNAPSHOT_DATA_KEY = + FlutterMain.class.getName() + '.' + AOT_VM_SNAPSHOT_DATA_KEY; + public static final String PUBLIC_AOT_VM_SNAPSHOT_INSTR_KEY = + FlutterMain.class.getName() + '.' + AOT_VM_SNAPSHOT_INSTR_KEY; + public static final String PUBLIC_AOT_ISOLATE_SNAPSHOT_DATA_KEY = + FlutterMain.class.getName() + '.' + AOT_ISOLATE_SNAPSHOT_DATA_KEY; + public static final String PUBLIC_AOT_ISOLATE_SNAPSHOT_INSTR_KEY = + FlutterMain.class.getName() + '.' + AOT_ISOLATE_SNAPSHOT_INSTR_KEY; public static final String PUBLIC_FLUTTER_ASSETS_DIR_KEY = FlutterMain.class.getName() + '.' + FLUTTER_ASSETS_DIR_KEY; // Resource names used for components of the precompiled snapshot. - private static final String DEFAULT_AOT_SHARED_LIBRARY_NAME = "libapp.so"; - private static final String DEFAULT_VM_SNAPSHOT_DATA = "vm_snapshot_data"; - private static final String DEFAULT_ISOLATE_SNAPSHOT_DATA = "isolate_snapshot_data"; + private static final String DEFAULT_AOT_SHARED_LIBRARY_PATH= "app.so"; + private static final String DEFAULT_AOT_VM_SNAPSHOT_DATA = "vm_snapshot_data"; + private static final String DEFAULT_AOT_VM_SNAPSHOT_INSTR = "vm_snapshot_instr"; + private static final String DEFAULT_AOT_ISOLATE_SNAPSHOT_DATA = "isolate_snapshot_data"; + private static final String DEFAULT_AOT_ISOLATE_SNAPSHOT_INSTR = "isolate_snapshot_instr"; private static final String DEFAULT_LIBRARY = "libflutter.so"; private static final String DEFAULT_KERNEL_BLOB = "kernel_blob.bin"; private static final String DEFAULT_FLUTTER_ASSETS_DIR = "flutter_assets"; @@ -59,17 +66,54 @@ private static String fromFlutterAssets(@NonNull String filePath) { } // Mutable because default values can be overridden via config properties - private static String sAotSharedLibraryName = DEFAULT_AOT_SHARED_LIBRARY_NAME; - private static String sVmSnapshotData = DEFAULT_VM_SNAPSHOT_DATA; - private static String sIsolateSnapshotData = DEFAULT_ISOLATE_SNAPSHOT_DATA; + private static String sAotSharedLibraryPath = DEFAULT_AOT_SHARED_LIBRARY_PATH; + private static String sAotVmSnapshotData = DEFAULT_AOT_VM_SNAPSHOT_DATA; + private static String sAotVmSnapshotInstr = DEFAULT_AOT_VM_SNAPSHOT_INSTR; + private static String sAotIsolateSnapshotData = DEFAULT_AOT_ISOLATE_SNAPSHOT_DATA; + private static String sAotIsolateSnapshotInstr = DEFAULT_AOT_ISOLATE_SNAPSHOT_INSTR; private static String sFlutterAssetsDir = DEFAULT_FLUTTER_ASSETS_DIR; private static boolean sInitialized = false; + private static boolean sIsPrecompiledAsBlobs = false; + private static boolean sIsPrecompiledAsSharedLibrary = false; @Nullable private static ResourceExtractor sResourceExtractor; @Nullable private static Settings sSettings; + @NonNull + private static String sSnapshotPath; + + + private static final class ImmutableSetBuilder { + static ImmutableSetBuilder newInstance() { + return new ImmutableSetBuilder<>(); + } + + HashSet set = new HashSet<>(); + + private ImmutableSetBuilder() {} + + @NonNull + ImmutableSetBuilder add(@NonNull T element) { + set.add(element); + return this; + } + + @SafeVarargs + @NonNull + final ImmutableSetBuilder add(@NonNull T... elements) { + for (T element : elements) { + set.add(element); + } + return this; + } + + @NonNull + Set build() { + return Collections.unmodifiableSet(set); + } + } public static class Settings { private String logTag; @@ -114,6 +158,7 @@ public static void startInitialization(@NonNull Context applicationContext, @Non long initStartTimestampMillis = SystemClock.uptimeMillis(); initConfig(applicationContext); + initAot(applicationContext); initResources(applicationContext); System.loadLibrary("flutter"); @@ -154,15 +199,22 @@ public static void ensureInitializationComplete(@NonNull Context applicationCont if (args != null) { Collections.addAll(shellArgs, args); } - if (BuildConfig.DEBUG) { - shellArgs.add("--" + SNAPSHOT_ASSET_PATH_KEY + "=" + PathUtils.getDataDirectory(applicationContext) + "/" + sFlutterAssetsDir); - shellArgs.add("--" + VM_SNAPSHOT_DATA_KEY + "=" + sVmSnapshotData); - shellArgs.add("--" + ISOLATE_SNAPSHOT_DATA_KEY + "=" + sIsolateSnapshotData); + if (sIsPrecompiledAsSharedLibrary) { + shellArgs.add("--" + AOT_SHARED_LIBRARY_PATH + "=" + + new File(sSnapshotPath, sAotSharedLibraryPath)); } else { - shellArgs.add("--" + AOT_SHARED_LIBRARY_NAME + "=" + sAotSharedLibraryName); + if (sIsPrecompiledAsBlobs) { + shellArgs.add("--" + AOT_SNAPSHOT_PATH_KEY + "=" + sSnapshotPath); + } else { + shellArgs.add("--cache-dir-path=" + PathUtils.getCacheDirectory(applicationContext)); + shellArgs.add("--" + AOT_SNAPSHOT_PATH_KEY + "=" + PathUtils.getDataDirectory(applicationContext) + "/" + sFlutterAssetsDir); + } + shellArgs.add("--" + AOT_VM_SNAPSHOT_DATA_KEY + "=" + sAotVmSnapshotData); + shellArgs.add("--" + AOT_VM_SNAPSHOT_INSTR_KEY + "=" + sAotVmSnapshotInstr); + shellArgs.add("--" + AOT_ISOLATE_SNAPSHOT_DATA_KEY + "=" + sAotIsolateSnapshotData); + shellArgs.add("--" + AOT_ISOLATE_SNAPSHOT_INSTR_KEY + "=" + sAotIsolateSnapshotInstr); } - shellArgs.add("--cache-dir-path=" + PathUtils.getCacheDirectory(applicationContext)); if (sSettings.getLogTag() != null) { shellArgs.add("--log-tag=" + sSettings.getLogTag()); } @@ -243,16 +295,18 @@ private static void initConfig(@NonNull Context applicationContext) { return; } - sAotSharedLibraryName = metadata.getString(PUBLIC_AOT_SHARED_LIBRARY_NAME, DEFAULT_AOT_SHARED_LIBRARY_NAME); + sAotSharedLibraryPath = metadata.getString(PUBLIC_AOT_AOT_SHARED_LIBRARY_PATH, DEFAULT_AOT_SHARED_LIBRARY_PATH); sFlutterAssetsDir = metadata.getString(PUBLIC_FLUTTER_ASSETS_DIR_KEY, DEFAULT_FLUTTER_ASSETS_DIR); - sVmSnapshotData = metadata.getString(PUBLIC_VM_SNAPSHOT_DATA_KEY, DEFAULT_VM_SNAPSHOT_DATA); - sIsolateSnapshotData = metadata.getString(PUBLIC_ISOLATE_SNAPSHOT_DATA_KEY, DEFAULT_ISOLATE_SNAPSHOT_DATA); + sAotVmSnapshotData = metadata.getString(PUBLIC_AOT_VM_SNAPSHOT_DATA_KEY, DEFAULT_AOT_VM_SNAPSHOT_DATA); + sAotVmSnapshotInstr = metadata.getString(PUBLIC_AOT_VM_SNAPSHOT_INSTR_KEY, DEFAULT_AOT_VM_SNAPSHOT_INSTR); + sAotIsolateSnapshotData = metadata.getString(PUBLIC_AOT_ISOLATE_SNAPSHOT_DATA_KEY, DEFAULT_AOT_ISOLATE_SNAPSHOT_DATA); + sAotIsolateSnapshotInstr = metadata.getString(PUBLIC_AOT_ISOLATE_SNAPSHOT_INSTR_KEY, DEFAULT_AOT_ISOLATE_SNAPSHOT_INSTR); } /** - * Extract assets out of the APK that need to be cached as uncompressed - * files on disk. + * Extract the AOT blobs from the app's asset directory. + * This is required by the Dart runtime, so it can read the blobs. */ private static void initResources(@NonNull Context applicationContext) { new ResourceCleaner(applicationContext).start(); @@ -263,20 +317,145 @@ private static void initResources(@NonNull Context applicationContext) { final AssetManager assetManager = applicationContext.getResources().getAssets(); sResourceExtractor = new ResourceExtractor(dataDirPath, packageName, packageManager, assetManager); - // In debug/JIT mode these assets will be written to disk and then - // mapped into memory so they can be provided to the Dart VM. - // AOT modes obtain compiled Dart assets from a ELF library that does - // not need to be extracted out of the APK. - if (BuildConfig.DEBUG) { - sResourceExtractor - .addResource(fromFlutterAssets(sVmSnapshotData)) - .addResource(fromFlutterAssets(sIsolateSnapshotData)) - .addResource(fromFlutterAssets(DEFAULT_KERNEL_BLOB)); + sResourceExtractor + .addResource(fromFlutterAssets(sAotVmSnapshotData)) + .addResource(fromFlutterAssets(sAotVmSnapshotInstr)) + .addResource(fromFlutterAssets(sAotIsolateSnapshotData)) + .addResource(fromFlutterAssets(sAotIsolateSnapshotInstr)) + .addResource(fromFlutterAssets(DEFAULT_KERNEL_BLOB)); + + if (sIsPrecompiledAsSharedLibrary) { + sResourceExtractor + .addResource(sAotSharedLibraryPath); + } else { + sResourceExtractor + .addResource(sAotVmSnapshotData) + .addResource(sAotVmSnapshotInstr) + .addResource(sAotIsolateSnapshotData) + .addResource(sAotIsolateSnapshotInstr); } - sResourceExtractor.start(); } + /** + * Returns a list of the file names at the root of the application's asset + * path. + */ + @NonNull + private static Set listAssets(@NonNull Context applicationContext, @NonNull String path) { + AssetManager manager = applicationContext.getResources().getAssets(); + try { + return ImmutableSetBuilder.newInstance() + .add(manager.list(path)) + .build(); + } catch (IOException e) { + Log.e(TAG, "Unable to list assets", e); + throw new RuntimeException(e); + } + } + + /** + * Returns a list of the file names at the root of the application's + * native library directory. + */ + @NonNull + private static Set listLibs(@NonNull Context applicationContext) { + ApplicationInfo applicationInfo = getApplicationInfo(applicationContext); + File[] files = new File(applicationInfo.nativeLibraryDir).listFiles(); + if (files == null) { + files = new File[0]; + } + + ImmutableSetBuilder builder = ImmutableSetBuilder.newInstance(); + for (File file : files) { + builder.add(file.getName()); + } + return builder.build(); + } + + /** + * Determines if the APK contains a shared library or AOT snapshots, + * the file name of the snapshots and the directory where they are contained. + * + *

The snapshots can be contained in the app's assets or in the native library + * directory. The default names are: + * + *

    + *
  • `vm_snapshot_data`
  • + *
  • `vm_snapshot_instr`
  • + *
  • `isolate_snapshot_data`
  • + *
  • `isolate_snapshot_instr`
  • + *
  • Shared library: `app.so`
  • + *
+ * + *

When the blobs are contained in the native library directory, + * this method looks for blobs named `lib_%s.so`. + * + * The shared library should have the `lib` prefix only. e.g. `libapp.so`. + * + *

Note: The name of the files can be customized in the app's metadata, but the + * format is preserved. + * + *

The AOT snapshots and the shared library cannot exist at the same time in the APK. + */ + private static void initAot(@NonNull Context applicationContext) { + Set assets = listAssets(applicationContext, ""); + Set libs = listLibs(applicationContext); + + String aotVmSnapshotDataLib = "lib_" + sAotVmSnapshotData + ".so"; + String aotVmSnapshotInstrLib = "lib_" + sAotVmSnapshotInstr + ".so"; + String aotIsolateSnapshotDataLib = "lib_" + sAotIsolateSnapshotData + ".so"; + String aotIsolateSnapshotInstrLib = "lib_" + sAotIsolateSnapshotInstr + ".so"; + String aotSharedLibraryLib = "lib" + sAotSharedLibraryPath; + + boolean isPrecompiledBlobInLib = libs + .containsAll(Arrays.asList( + aotVmSnapshotDataLib, + aotVmSnapshotInstrLib, + aotIsolateSnapshotDataLib, + aotIsolateSnapshotInstrLib + )); + + if (isPrecompiledBlobInLib) { + sIsPrecompiledAsBlobs = true; + sAotVmSnapshotData = aotVmSnapshotDataLib; + sAotVmSnapshotInstr = aotVmSnapshotInstrLib; + sAotIsolateSnapshotData = aotIsolateSnapshotDataLib; + sAotIsolateSnapshotInstr = aotIsolateSnapshotInstrLib; + } else { + sIsPrecompiledAsBlobs = assets.containsAll(Arrays.asList( + sAotVmSnapshotData, + sAotVmSnapshotInstr, + sAotIsolateSnapshotData, + sAotIsolateSnapshotInstr + )); + } + boolean isSharedLibraryInLib = libs.contains(aotSharedLibraryLib); + boolean isSharedLibraryInAssets = assets.contains(sAotSharedLibraryPath); + + if (isSharedLibraryInLib) { + sAotSharedLibraryPath = aotSharedLibraryLib; + sIsPrecompiledAsSharedLibrary = true; + } else if (isSharedLibraryInAssets) { + sIsPrecompiledAsSharedLibrary = true; + } + + if (isSharedLibraryInLib || isPrecompiledBlobInLib) { + sSnapshotPath = getApplicationInfo(applicationContext).nativeLibraryDir; + } else { + sSnapshotPath = PathUtils.getDataDirectory(applicationContext); + } + + if (sIsPrecompiledAsBlobs && sIsPrecompiledAsSharedLibrary) { + throw new RuntimeException( + "Found precompiled app as shared library and as Dart VM snapshots."); + } + } + + public static boolean isRunningPrecompiledCode() { + return sIsPrecompiledAsBlobs || sIsPrecompiledAsSharedLibrary; + } + @Nullable public static String findAppBundlePath(@NonNull Context applicationContext) { String dataDirectory = PathUtils.getDataDirectory(applicationContext);