args = new ArrayList<>();
- /** Represents a Flutter shell flag that can be set via manifest metadata or command line. */
- public static class Flag {
- /** The command line argument used to specify the flag. */
- public final String commandLineArgument;
-
- /**
- * The metadata key name used to specify the flag in AndroidManifest.xml.
- *
- * To specify a flag in a manifest, it should be prefixed with {@code
- * io.flutter.embedding.android.}. This is enforced to avoid potential naming collisions with
- * other metadata keys. The only exception are flags that have already been deprecated.
- */
- public final String metadataKey;
-
- /** Whether this flag is allowed to be set in release mode. */
- public final boolean allowedInRelease;
-
- /**
- * Creates a new Flutter shell flag that is not allowed in release mode with the default flag
- * prefix.
- */
- private Flag(String commandLineArgument, String metaDataName) {
- this(commandLineArgument, metaDataName, "io.flutter.embedding.android.", false);
+ if (intent.getBooleanExtra(ARG_KEY_TRACE_STARTUP, false)) {
+ args.add(ARG_TRACE_STARTUP);
}
-
- /** Creates a new Flutter shell flag with the default flag prefix. */
- private Flag(String commandLineArgument, String metaDataName, boolean allowedInRelease) {
- this(commandLineArgument, metaDataName, "io.flutter.embedding.android.", allowedInRelease);
+ if (intent.getBooleanExtra(ARG_KEY_START_PAUSED, false)) {
+ args.add(ARG_START_PAUSED);
}
-
- /**
- * Creates a new Flutter shell flag.
- *
- *
{@param allowedInRelease} determines whether or not this flag is allowed in release mode.
- * Whenever possible, it is recommended to NOT allow this flag in release mode. Many flags are
- * designed for debugging purposes and if enabled in production, could expose sensitive
- * application data or make the app vulnerable to malicious actors.
- *
- *
If creating a flag that will be allowed in release, please leave a comment in the Javadoc
- * explaining why it should be allowed in release.
- */
- private Flag(
- String commandLineArgument,
- String metaDataName,
- String flagPrefix,
- boolean allowedInRelease) {
- this.commandLineArgument = commandLineArgument;
- this.metadataKey = flagPrefix + metaDataName;
- this.allowedInRelease = allowedInRelease;
+ int vmServicePort = intent.getIntExtra(ARG_KEY_VM_SERVICE_PORT, 0);
+ if (vmServicePort > 0) {
+ args.add(ARG_VM_SERVICE_PORT + vmServicePort);
}
-
- /** Returns true if this flag requires a value to be specified. */
- public boolean hasValue() {
- return commandLineArgument.endsWith("=");
+ if (intent.getBooleanExtra(ARG_KEY_DISABLE_SERVICE_AUTH_CODES, false)) {
+ args.add(ARG_DISABLE_SERVICE_AUTH_CODES);
}
- }
-
- // Manifest flags allowed in release mode:
-
- /**
- * Specifies the path to the AOT shared library containing compiled Dart code.
- *
- *
The AOT shared library that the engine uses will default to the library set by this flag,
- * but will fall back to the libraries set internally by the embedding if the path specified by
- * this argument is invalid.
- *
- *
This is allowed in release to support the same AOT configuration regardless of build mode.
- */
- public static final Flag AOT_SHARED_LIBRARY_NAME =
- new Flag("--aot-shared-library-name=", "AOTSharedLibraryName", true);
-
- /**
- * Deprecated flag that specifies the path to the AOT shared library containing compiled Dart
- * code.
- *
- *
Please use {@link AOT_SHARED_LIBRARY_NAME} instead.
- */
- @Deprecated
- public static final Flag DEPRECATED_AOT_SHARED_LIBRARY_NAME =
- new Flag(
- "--aot-shared-library-name=",
- "aot-shared-library-name",
- "io.flutter.embedding.engine.loader.FlutterLoader.",
- true);
-
- /**
- * Sets the directory containing Flutter assets.
- *
- *
This is allowed in release to specify custom asset locations in production.
- */
- public static final Flag FLUTTER_ASSETS_DIR =
- new Flag("--flutter-assets-dir=", "FlutterAssetsDir", true);
-
- /**
- * The deprecated flag that sets the directory containing Flutter assets.
- *
- *
Please use {@link DEPRECATED_FLUTTER_ASSETS_DIR} instead.
- */
- @Deprecated
- public static final Flag DEPRECATED_FLUTTER_ASSETS_DIR =
- new Flag(
- "--flutter-assets-dir=",
- "flutter-assets-dir",
- "io.flutter.embedding.engine.loader.FlutterLoader.",
- true);
-
- /**
- * Sets the old generation heap size for the Dart VM in megabytes.
- *
- *
This is allowed in release for performance tuning.
- */
- public static final Flag OLD_GEN_HEAP_SIZE =
- new Flag("--old-gen-heap-size=", "OldGenHeapSize", true);
-
- /**
- * Enables or disables the Impeller renderer.
- *
- *
This is allowed in release to control which rendering backend is used in production.
- */
- private static final Flag ENABLE_IMPELLER =
- new Flag("--enable-impeller=", "EnableImpeller", true);
-
- /**
- * Specifies the backend to use for Impeller rendering.
- *
- *
This is allowed in release to select a specific graphics backend for Impeller in production.
- */
- private static final Flag IMPELLER_BACKEND =
- new Flag("--impeller-backend=", "ImpellerBackend", true);
-
- /**
- * Enables Android SurfaceControl for rendering.
- *
- *
This is allowed in release to opt-in to this rendering feature in production.
- */
- private static final Flag ENABLE_SURFACE_CONTROL =
- new Flag("--enable-surface-control", "EnableSurfaceControl", true);
-
- /**
- * Enables the Flutter GPU backend.
- *
- *
This is allowed in release for developers to use the Flutter GPU backend in production.
- */
- private static final Flag ENABLE_FLUTTER_GPU =
- new Flag("--enable-flutter-gpu", "EnableFlutterGPU", true);
-
- /**
- * Enables lazy initialization of Impeller shaders.
- *
- *
This is allowed in release for performance tuning of the Impeller backend.
- */
- private static final Flag IMPELLER_LAZY_SHADER_MODE =
- new Flag("--impeller-lazy-shader-mode=", "ImpellerLazyShaderInitialization", true);
-
- /**
- * Enables antialiasing for lines in Impeller.
- *
- *
This is allowed in release to control rendering quality in production.
- */
- private static final Flag IMPELLER_ANTIALIAS_LINES =
- new Flag("--impeller-antialias-lines", "ImpellerAntialiasLines", true);
-
- /**
- * Specifies the path to the VM snapshot data file.
- *
- *
This is allowed in release to support different snapshot configurations.
- */
- public static final Flag VM_SNAPSHOT_DATA =
- new Flag("--vm-snapshot-data=", "VmSnapshotData", true);
-
- /**
- * Specifies the path to the isolate snapshot data file.
- *
- *
This is allowed in release to support different snapshot configurations.
- */
- public static final Flag ISOLATE_SNAPSHOT_DATA =
- new Flag("--isolate-snapshot-data=", "IsolateSnapshotData", true);
-
- // Manifest flags NOT allowed in release mode:
-
- /** Ensures deterministic Skia rendering by skipping CPU feature swaps. */
- private static final Flag SKIA_DETERMINISTIC_RENDERING =
- new Flag("--skia-deterministic-rendering", "SkiaDeterministicRendering");
-
- /** Use Skia software backend for rendering. */
- public static final Flag ENABLE_SOFTWARE_RENDERING =
- new Flag("--enable-software-rendering", "EnableSoftwareRendering");
-
- /** Use the Ahem test font for font resolution. */
- private static final Flag USE_TEST_FONTS = new Flag("--use-test-fonts", "UseTestFonts");
-
- /** Sets the port for the Dart VM Service. */
- private static final Flag VM_SERVICE_PORT = new Flag("--vm-service-port=", "VMServicePort");
-
- /** Enables Vulkan validation layers if available. */
- private static final Flag ENABLE_VULKAN_VALIDATION =
- new Flag("--enable-vulkan-validation", "EnableVulkanValidation");
-
- /** Enables GPU tracing for OpenGL. */
- private static final Flag ENABLE_OPENGL_GPU_TRACING =
- new Flag("--enable-opengl-gpu-tracing", "EnableOpenGLGPUTracing");
-
- /** Enables GPU tracing for Vulkan. */
- private static final Flag ENABLE_VULKAN_GPU_TRACING =
- new Flag("--enable-vulkan-gpu-tracing", "EnableVulkanGPUTracing");
-
- /**
- * Set whether leave or clean up the VM after the last shell shuts down. It can be set from app's
- * metadata in the application block in AndroidManifest.xml. Set it to true in to leave the Dart
- * VM, set it to false to destroy VM.
- *
- *
If your want to let your app destroy the last shell and re-create shells more quickly, set
- * it to true, otherwise if you want to clean up the memory of the leak VM, set it to false.
- *
- *
TODO(eggfly): Should it be set to false by default?
- * https://github.com/flutter/flutter/issues/96843
- */
- public static final Flag LEAK_VM = new Flag("--leak-vm=", "LeakVM");
-
- /** Measures startup time and switches to an endless trace buffer. */
- private static final Flag TRACE_STARTUP = new Flag("--trace-startup", "TraceStartup");
-
- /** Pauses Dart code execution at launch until a debugger is attached. */
- private static final Flag START_PAUSED = new Flag("--start-paused", "StartPaused");
-
- /** Disables authentication codes for VM service communication. */
- private static final Flag DISABLE_SERVICE_AUTH_CODES =
- new Flag("--disable-service-auth-codes", "DisableServiceAuthCodes");
-
- /** Enables an endless trace buffer for timeline events. */
- private static final Flag ENDLESS_TRACE_BUFFER =
- new Flag("--endless-trace-buffer", "EndlessTraceBuffer");
-
- /** Enables Dart profiling for use with DevTools. */
- private static final Flag ENABLE_DART_PROFILING =
- new Flag("--enable-dart-profiling", "EnableDartProfiling");
-
- /** Discards new profiler samples once the buffer is full. */
- private static final Flag PROFILE_STARTUP = new Flag("--profile-startup", "ProfileStartup");
-
- /** Enables tracing of Skia GPU calls. */
- private static final Flag TRACE_SKIA = new Flag("--trace-skia", "TraceSkia");
-
- /** Only traces specified Skia event categories. */
- private static final Flag TRACE_SKIA_ALLOWLIST =
- new Flag("--trace-skia-allowlist=", "TraceSkiaAllowList");
-
- /** Traces to the system tracer on supported platforms. */
- private static final Flag TRACE_SYSTRACE = new Flag("--trace-systrace", "TraceSystrace");
-
- /** Writes timeline trace to a file in Perfetto format. */
- private static final Flag TRACE_TO_FILE = new Flag("--trace-to-file=", "TraceToFile");
-
- /** Collects and logs information about microtasks. */
- private static final Flag PROFILE_MICROTASKS =
- new Flag("--profile-microtasks", "ProfileMicrotasks");
-
- /** Dumps SKP files that trigger shader compilations. */
- private static final Flag DUMP_SKP_ON_SHADER_COMPILATION =
- new Flag("--dump-skp-on-shader-compilation", "DumpSkpOnShaderCompilation");
-
- /** Removes all persistent cache files for debugging. */
- private static final Flag PURGE_PERSISTENT_CACHE =
- new Flag("--purge-persistent-cache", "PurgePersistentCache");
-
- /** Enables logging at all severity levels. */
- private static final Flag VERBOSE_LOGGING = new Flag("--verbose-logging", "VerboseLogging");
-
- /**
- * Passes additional flags to the Dart VM.
- *
- *
All flags provided with this argument are subject to filtering based on a list of allowed
- * flags in shell/common/switches.cc. If any flag provided is not allowed, the process will
- * immediately terminate.
- *
- *
Flags should be separated by a space, e.g. "--dart-flags=--flag-1 --flag-2=2".
- */
- private static final Flag DART_FLAGS = new Flag("--dart-flags=", "DartFlags");
-
- // Deprecated flags:
-
- /** Disables the merging of the UI and platform threads. */
- @VisibleForTesting
- public static final Flag DISABLE_MERGED_PLATFORM_UI_THREAD =
- new Flag("--no-enable-merged-platform-ui-thread", "DisableMergedPlatformUIThread");
-
- @VisibleForTesting
- public static final List ALL_FLAGS =
- Collections.unmodifiableList(
- Arrays.asList(
- VM_SERVICE_PORT,
- USE_TEST_FONTS,
- ENABLE_SOFTWARE_RENDERING,
- SKIA_DETERMINISTIC_RENDERING,
- AOT_SHARED_LIBRARY_NAME,
- FLUTTER_ASSETS_DIR,
- OLD_GEN_HEAP_SIZE,
- ENABLE_IMPELLER,
- IMPELLER_BACKEND,
- ENABLE_SURFACE_CONTROL,
- ENABLE_FLUTTER_GPU,
- IMPELLER_LAZY_SHADER_MODE,
- IMPELLER_ANTIALIAS_LINES,
- VM_SNAPSHOT_DATA,
- ISOLATE_SNAPSHOT_DATA,
- ENABLE_VULKAN_VALIDATION,
- ENABLE_OPENGL_GPU_TRACING,
- ENABLE_VULKAN_GPU_TRACING,
- LEAK_VM,
- TRACE_STARTUP,
- START_PAUSED,
- DISABLE_SERVICE_AUTH_CODES,
- ENDLESS_TRACE_BUFFER,
- ENABLE_DART_PROFILING,
- PROFILE_STARTUP,
- TRACE_SKIA,
- TRACE_SKIA_ALLOWLIST,
- TRACE_SYSTRACE,
- TRACE_TO_FILE,
- PROFILE_MICROTASKS,
- DUMP_SKP_ON_SHADER_COMPILATION,
- PURGE_PERSISTENT_CACHE,
- VERBOSE_LOGGING,
- DART_FLAGS,
- DISABLE_MERGED_PLATFORM_UI_THREAD,
- DEPRECATED_AOT_SHARED_LIBRARY_NAME,
- DEPRECATED_FLUTTER_ASSETS_DIR));
-
- // Flags that have been turned off.
- private static final List DISABLED_FLAGS =
- Collections.unmodifiableList(Arrays.asList(DISABLE_MERGED_PLATFORM_UI_THREAD));
-
- // Lookup map for current flags that replace deprecated ones.
- private static final Map DEPRECATED_FLAGS_BY_REPLACEMENT =
- new HashMap() {
- {
- put(DEPRECATED_AOT_SHARED_LIBRARY_NAME, AOT_SHARED_LIBRARY_NAME);
- put(DEPRECATED_FLUTTER_ASSETS_DIR, FLUTTER_ASSETS_DIR);
- }
- };
-
- // Lookup map for retrieving the Flag corresponding to a specific command line argument.
- private static final Map FLAG_BY_COMMAND_LINE_ARG;
-
- // Lookup map for retrieving the Flag corresponding to a specific metadata key.
- private static final Map FLAG_BY_META_DATA_KEY;
-
- static {
- Map map = new HashMap(ALL_FLAGS.size());
- Map metaMap = new HashMap(ALL_FLAGS.size());
- for (Flag flag : ALL_FLAGS) {
- map.put(flag.commandLineArgument, flag);
- metaMap.put(flag.metadataKey, flag);
+ if (intent.getBooleanExtra(ARG_KEY_ENDLESS_TRACE_BUFFER, false)) {
+ args.add(ARG_ENDLESS_TRACE_BUFFER);
}
- FLAG_BY_COMMAND_LINE_ARG = Collections.unmodifiableMap(map);
- FLAG_BY_META_DATA_KEY = Collections.unmodifiableMap(metaMap);
- }
-
- /** Looks up a {@link Flag} by its metadataKey. */
- public static Flag getFlagByMetadataKey(String key) {
- Flag flag = FLAG_BY_META_DATA_KEY.get(key);
- Flag replacementFlag = getReplacementFlagIfDeprecated(flag);
- return replacementFlag != null ? replacementFlag : flag;
- }
-
- /** Looks up a {@link Flag} by its commandLineArgument. */
- public static Flag getFlagByCommandLineArgument(String arg) {
- int equalsIndex = arg.indexOf('=');
- Flag flag =
- FLAG_BY_COMMAND_LINE_ARG.get(equalsIndex == -1 ? arg : arg.substring(0, equalsIndex + 1));
- Flag replacementFlag = getReplacementFlagIfDeprecated(flag);
- return replacementFlag != null ? replacementFlag : flag;
- }
-
- /**
- * Looks up a {@link Flag} by its Intent key.
- *
- * Previously, the Intent keys were used to set Flutter shell arguments via Intent. The Intent
- * keys match the command line argument without the "--" prefix and "=" suffix if the argument
- * takes a value.
- */
- public static Flag getFlagFromIntentKey(String intentKey) {
- for (Flag flag : ALL_FLAGS) {
- String commandLineArg = flag.commandLineArgument;
- String key = commandLineArg.startsWith("--") ? commandLineArg.substring(2) : commandLineArg;
- if (key.endsWith("=")) {
- key = key.substring(0, key.length() - 1);
- }
- if (key.equals(intentKey)) {
- return flag;
+ if (intent.getBooleanExtra(ARG_KEY_USE_TEST_FONTS, false)) {
+ args.add(ARG_USE_TEST_FONTS);
+ }
+ if (intent.getBooleanExtra(ARG_KEY_ENABLE_DART_PROFILING, false)) {
+ args.add(ARG_ENABLE_DART_PROFILING);
+ }
+ if (intent.getBooleanExtra(ARG_KEY_PROFILE_STARTUP, false)) {
+ args.add(ARG_PROFILE_STARTUP);
+ }
+ if (intent.getBooleanExtra(ARG_KEY_ENABLE_SOFTWARE_RENDERING, false)) {
+ args.add(ARG_ENABLE_SOFTWARE_RENDERING);
+ }
+ if (intent.getBooleanExtra(ARG_KEY_SKIA_DETERMINISTIC_RENDERING, false)) {
+ args.add(ARG_SKIA_DETERMINISTIC_RENDERING);
+ }
+ if (intent.getBooleanExtra(ARG_KEY_TRACE_SKIA, false)) {
+ args.add(ARG_TRACE_SKIA);
+ }
+ String traceSkiaAllowlist = intent.getStringExtra(ARG_KEY_TRACE_SKIA_ALLOWLIST);
+ if (traceSkiaAllowlist != null) {
+ args.add(ARG_TRACE_SKIA_ALLOWLIST + traceSkiaAllowlist);
+ }
+ if (intent.getBooleanExtra(ARG_KEY_TRACE_SYSTRACE, false)) {
+ args.add(ARG_TRACE_SYSTRACE);
+ }
+ if (intent.hasExtra(ARG_KEY_TRACE_TO_FILE)) {
+ args.add(ARG_TRACE_TO_FILE + "=" + intent.getStringExtra(ARG_KEY_TRACE_TO_FILE));
+ }
+ if (intent.hasExtra(ARG_KEY_PROFILE_MICROTASKS)) {
+ args.add(ARG_PROFILE_MICROTASKS);
+ }
+ if (intent.hasExtra(ARG_KEY_TOGGLE_IMPELLER)) {
+ if (intent.getBooleanExtra(ARG_KEY_TOGGLE_IMPELLER, false)) {
+ args.add(ARG_ENABLE_IMPELLER);
+ } else {
+ args.add(ARG_DISABLE_IMPELLER);
}
}
- return null;
+ if (intent.getBooleanExtra(ARG_KEY_ENABLE_VULKAN_VALIDATION, false)) {
+ args.add(ARG_ENABLE_VULKAN_VALIDATION);
+ }
+ if (intent.getBooleanExtra(ARG_KEY_DUMP_SHADER_SKP_ON_SHADER_COMPILATION, false)) {
+ args.add(ARG_DUMP_SHADER_SKP_ON_SHADER_COMPILATION);
+ }
+ if (intent.getBooleanExtra(ARG_KEY_CACHE_SKSL, false)) {
+ args.add(ARG_CACHE_SKSL);
+ }
+ if (intent.getBooleanExtra(ARG_KEY_PURGE_PERSISTENT_CACHE, false)) {
+ args.add(ARG_PURGE_PERSISTENT_CACHE);
+ }
+ if (intent.getBooleanExtra(ARG_KEY_VERBOSE_LOGGING, false)) {
+ args.add(ARG_VERBOSE_LOGGING);
+ }
+
+ // NOTE: all flags provided with this argument are subject to filtering
+ // based on a list of allowed flags in shell/common/switches.cc. If any
+ // flag provided is not allowed, the process will immediately terminate.
+ if (intent.hasExtra(ARG_KEY_DART_FLAGS)) {
+ args.add(ARG_DART_FLAGS + "=" + intent.getStringExtra(ARG_KEY_DART_FLAGS));
+ }
+
+ return new FlutterShellArgs(args);
}
- /** Returns whether or not a flag is disabled and should raise an exception if used. */
- public static boolean isDisabled(Flag flag) {
- return DISABLED_FLAGS.contains(flag);
+ @NonNull private Set args;
+
+ /**
+ * Creates a set of Flutter shell arguments from a given {@code String[]} array. The given
+ * arguments are automatically de-duplicated.
+ */
+ public FlutterShellArgs(@NonNull String[] args) {
+ this.args = new HashSet<>(Arrays.asList(args));
}
- /** Returns the replacement flag of that given if it is deprecated. */
- public static Flag getReplacementFlagIfDeprecated(Flag flag) {
- return DEPRECATED_FLAGS_BY_REPLACEMENT.get(flag);
+ /**
+ * Creates a set of Flutter shell arguments from a given {@code List}. The given arguments
+ * are automatically de-duplicated.
+ */
+ public FlutterShellArgs(@NonNull List args) {
+ this.args = new HashSet<>(args);
+ }
+
+ /** Creates a set of Flutter shell arguments from a given {@code Set}. */
+ public FlutterShellArgs(@NonNull Set args) {
+ this.args = new HashSet<>(args);
+ }
+
+ /**
+ * Adds the given {@code arg} to this set of arguments.
+ *
+ * @param arg argument to add
+ */
+ public void add(@NonNull String arg) {
+ args.add(arg);
+ }
+
+ /**
+ * Removes the given {@code arg} from this set of arguments.
+ *
+ * @param arg argument to remove
+ */
+ public void remove(@NonNull String arg) {
+ args.remove(arg);
+ }
+
+ /**
+ * Returns a new {@code String[]} array which contains each of the arguments within this {@code
+ * FlutterShellArgs}.
+ *
+ * @return array of arguments
+ */
+ @NonNull
+ public String[] toArray() {
+ String[] argsArray = new String[args.size()];
+ return args.toArray(argsArray);
}
}
diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgsIntentUtils.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgsIntentUtils.java
deleted file mode 100644
index 81ffc72fb16..00000000000
--- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgsIntentUtils.java
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// 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;
-
-import android.content.Intent;
-import androidx.annotation.NonNull;
-import java.util.*;
-
-/**
- * Arguments that can be delivered to the Flutter shell on Android as Intent extras.
- *
- * The term "shell" refers to the native code that adapts Flutter to different platforms.
- * Flutter's Android Java code initializes a native "shell" and passes these arguments to that
- * native "shell" when it is initialized. See {@link
- * io.flutter.embedding.engine.loader.FlutterLoader#ensureInitializationComplete(Context, String[])}
- * for more information.
- *
- *
All of these flags map to a flag listed in shell/common/switches.cc, which contains the full
- * list of flags that can be set across all platforms.
- *
- *
These flags are preferably set via the manifest metadata in a Flutter component's
- * AndroidManifest.xml or via the command line for security purposes as Intent extras may expose
- * sensitive information to malicious actors. See {@link FlutterShellArgs} for the specification of
- * how to set each flag via the command line and manifest metadata.
- */
-// TODO(camsim99): Delete this class when support for setting engine shell arguments via Intent
-// is no longer supported. See https://github.com/flutter/flutter/issues/180686.
-public final class FlutterShellArgsIntentUtils {
-
- private FlutterShellArgsIntentUtils() {}
-
- public static final String ARG_KEY_TRACE_STARTUP = "trace-startup";
- public static final String ARG_TRACE_STARTUP = "--trace-startup";
- public static final String ARG_KEY_START_PAUSED = "start-paused";
- public static final String ARG_START_PAUSED = "--start-paused";
- public static final String ARG_KEY_DISABLE_SERVICE_AUTH_CODES = "disable-service-auth-codes";
- public static final String ARG_DISABLE_SERVICE_AUTH_CODES = "--disable-service-auth-codes";
- public static final String ARG_KEY_ENDLESS_TRACE_BUFFER = "endless-trace-buffer";
- public static final String ARG_ENDLESS_TRACE_BUFFER = "--endless-trace-buffer";
- public static final String ARG_KEY_USE_TEST_FONTS = "use-test-fonts";
- public static final String ARG_USE_TEST_FONTS = "--use-test-fonts";
- public static final String ARG_KEY_ENABLE_DART_PROFILING = "enable-dart-profiling";
- public static final String ARG_ENABLE_DART_PROFILING = "--enable-dart-profiling";
- public static final String ARG_KEY_PROFILE_STARTUP = "profile-startup";
- public static final String ARG_PROFILE_STARTUP = "--profile-startup";
- public static final String ARG_KEY_ENABLE_SOFTWARE_RENDERING = "enable-software-rendering";
- public static final String ARG_ENABLE_SOFTWARE_RENDERING = "--enable-software-rendering";
- public static final String ARG_KEY_SKIA_DETERMINISTIC_RENDERING = "skia-deterministic-rendering";
- public static final String ARG_SKIA_DETERMINISTIC_RENDERING = "--skia-deterministic-rendering";
- public static final String ARG_KEY_TRACE_SKIA = "trace-skia";
- public static final String ARG_TRACE_SKIA = "--trace-skia";
- public static final String ARG_KEY_TRACE_SKIA_ALLOWLIST = "trace-skia-allowlist";
- public static final String ARG_TRACE_SKIA_ALLOWLIST = "--trace-skia-allowlist=";
- public static final String ARG_KEY_TRACE_SYSTRACE = "trace-systrace";
- public static final String ARG_TRACE_SYSTRACE = "--trace-systrace";
- public static final String ARG_KEY_TRACE_TO_FILE = "trace-to-file";
- public static final String ARG_TRACE_TO_FILE = "--trace-to-file";
- public static final String ARG_KEY_PROFILE_MICROTASKS = "profile-microtasks";
- public static final String ARG_PROFILE_MICROTASKS = "--profile-microtasks";
- public static final String ARG_KEY_TOGGLE_IMPELLER = "enable-impeller";
- public static final String ARG_ENABLE_IMPELLER = "--enable-impeller=true";
- public static final String ARG_DISABLE_IMPELLER = "--enable-impeller=false";
- public static final String ARG_KEY_ENABLE_VULKAN_VALIDATION = "enable-vulkan-validation";
- public static final String ARG_ENABLE_VULKAN_VALIDATION = "--enable-vulkan-validation";
- public static final String ARG_KEY_DUMP_SHADER_SKP_ON_SHADER_COMPILATION =
- "dump-skp-on-shader-compilation";
- public static final String ARG_DUMP_SHADER_SKP_ON_SHADER_COMPILATION =
- "--dump-skp-on-shader-compilation";
- public static final String ARG_KEY_CACHE_SKSL = "cache-sksl";
- public static final String ARG_CACHE_SKSL = "--cache-sksl";
- public static final String ARG_KEY_PURGE_PERSISTENT_CACHE = "purge-persistent-cache";
- public static final String ARG_PURGE_PERSISTENT_CACHE = "--purge-persistent-cache";
- public static final String ARG_KEY_VERBOSE_LOGGING = "verbose-logging";
- public static final String ARG_VERBOSE_LOGGING = "--verbose-logging";
- public static final String ARG_KEY_VM_SERVICE_PORT = "vm-service-port";
- public static final String ARG_VM_SERVICE_PORT = "--vm-service-port=";
- public static final String ARG_KEY_DART_FLAGS = "dart-flags";
- public static final String ARG_DART_FLAGS = "--dart-flags";
-
- @NonNull
- public static String[] getFlutterShellCommandLineArgs(@NonNull Intent intent) {
- // Before adding more entries to this list, consider that arbitrary
- // Android applications can generate intents with extra data and that
- // there are many security-sensitive args in the binary.
- ArrayList args = new ArrayList<>();
-
- if (intent.getBooleanExtra(ARG_KEY_TRACE_STARTUP, false)) {
- args.add(ARG_TRACE_STARTUP);
- }
- if (intent.getBooleanExtra(ARG_KEY_START_PAUSED, false)) {
- args.add(ARG_START_PAUSED);
- }
- int vmServicePort = intent.getIntExtra(ARG_KEY_VM_SERVICE_PORT, 0);
- if (vmServicePort > 0) {
- args.add(ARG_VM_SERVICE_PORT + vmServicePort);
- }
- if (intent.getBooleanExtra(ARG_KEY_DISABLE_SERVICE_AUTH_CODES, false)) {
- args.add(ARG_DISABLE_SERVICE_AUTH_CODES);
- }
- if (intent.getBooleanExtra(ARG_KEY_ENDLESS_TRACE_BUFFER, false)) {
- args.add(ARG_ENDLESS_TRACE_BUFFER);
- }
- if (intent.getBooleanExtra(ARG_KEY_USE_TEST_FONTS, false)) {
- args.add(ARG_USE_TEST_FONTS);
- }
- if (intent.getBooleanExtra(ARG_KEY_ENABLE_DART_PROFILING, false)) {
- args.add(ARG_ENABLE_DART_PROFILING);
- }
- if (intent.getBooleanExtra(ARG_KEY_PROFILE_STARTUP, false)) {
- args.add(ARG_PROFILE_STARTUP);
- }
- if (intent.getBooleanExtra(ARG_KEY_ENABLE_SOFTWARE_RENDERING, false)) {
- args.add(ARG_ENABLE_SOFTWARE_RENDERING);
- }
- if (intent.getBooleanExtra(ARG_KEY_SKIA_DETERMINISTIC_RENDERING, false)) {
- args.add(ARG_SKIA_DETERMINISTIC_RENDERING);
- }
- if (intent.getBooleanExtra(ARG_KEY_TRACE_SKIA, false)) {
- args.add(ARG_TRACE_SKIA);
- }
- String traceSkiaAllowlist = intent.getStringExtra(ARG_KEY_TRACE_SKIA_ALLOWLIST);
- if (traceSkiaAllowlist != null) {
- args.add(ARG_TRACE_SKIA_ALLOWLIST + traceSkiaAllowlist);
- }
- if (intent.getBooleanExtra(ARG_KEY_TRACE_SYSTRACE, false)) {
- args.add(ARG_TRACE_SYSTRACE);
- }
- if (intent.hasExtra(ARG_KEY_TRACE_TO_FILE)) {
- args.add(ARG_TRACE_TO_FILE + "=" + intent.getStringExtra(ARG_KEY_TRACE_TO_FILE));
- }
- if (intent.hasExtra(ARG_KEY_PROFILE_MICROTASKS)) {
- args.add(ARG_PROFILE_MICROTASKS);
- }
- if (intent.hasExtra(ARG_KEY_TOGGLE_IMPELLER)) {
- if (intent.getBooleanExtra(ARG_KEY_TOGGLE_IMPELLER, false)) {
- args.add(ARG_ENABLE_IMPELLER);
- } else {
- args.add(ARG_DISABLE_IMPELLER);
- }
- }
- if (intent.getBooleanExtra(ARG_KEY_ENABLE_VULKAN_VALIDATION, false)) {
- args.add(ARG_ENABLE_VULKAN_VALIDATION);
- }
- if (intent.getBooleanExtra(ARG_KEY_DUMP_SHADER_SKP_ON_SHADER_COMPILATION, false)) {
- args.add(ARG_DUMP_SHADER_SKP_ON_SHADER_COMPILATION);
- }
- if (intent.getBooleanExtra(ARG_KEY_CACHE_SKSL, false)) {
- args.add(ARG_CACHE_SKSL);
- }
- if (intent.getBooleanExtra(ARG_KEY_PURGE_PERSISTENT_CACHE, false)) {
- args.add(ARG_PURGE_PERSISTENT_CACHE);
- }
- if (intent.getBooleanExtra(ARG_KEY_VERBOSE_LOGGING, false)) {
- args.add(ARG_VERBOSE_LOGGING);
- }
-
- // All flags provided with this argument are subject to filtering
- // based on a list of allowed flags in shell/common/switches.cc. If any
- // flag provided is not allowed, the process will immediately terminate.
- if (intent.hasExtra(ARG_KEY_DART_FLAGS)) {
- args.add(ARG_DART_FLAGS + "=" + intent.getStringExtra(ARG_KEY_DART_FLAGS));
- }
-
- String[] argsArray = new String[args.size()];
- return args.toArray(argsArray);
- }
-}
diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/ApplicationInfoLoader.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/ApplicationInfoLoader.java
index fb682cdbfcf..24c50298358 100644
--- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/ApplicationInfoLoader.java
+++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/ApplicationInfoLoader.java
@@ -10,19 +10,24 @@ import android.content.pm.PackageManager;
import android.content.res.XmlResourceParser;
import android.os.Bundle;
import androidx.annotation.NonNull;
-import io.flutter.embedding.engine.FlutterShellArgs;
import java.io.IOException;
import org.json.JSONArray;
import org.xmlpull.v1.XmlPullParserException;
/** Loads application information given a Context. */
public final class ApplicationInfoLoader {
- // TODO(camsim99): Remove support for these flags:
- // https://github.com/flutter/flutter/issues/179276.
- // AndroidManifest.xml metadata keys for setting internal respective Flutter configuration values.
+ // XML Attribute keys supported in AndroidManifest.xml
+ public static final String PUBLIC_AOT_SHARED_LIBRARY_NAME =
+ FlutterLoader.class.getName() + '.' + FlutterLoader.AOT_SHARED_LIBRARY_NAME;
+ public static final String PUBLIC_VM_SNAPSHOT_DATA_KEY =
+ FlutterLoader.class.getName() + '.' + FlutterLoader.VM_SNAPSHOT_DATA_KEY;
+ public static final String PUBLIC_ISOLATE_SNAPSHOT_DATA_KEY =
+ FlutterLoader.class.getName() + '.' + FlutterLoader.ISOLATE_SNAPSHOT_DATA_KEY;
+ public static final String PUBLIC_FLUTTER_ASSETS_DIR_KEY =
+ FlutterLoader.class.getName() + '.' + FlutterLoader.FLUTTER_ASSETS_DIR_KEY;
public static final String NETWORK_POLICY_METADATA_KEY = "io.flutter.network-policy";
public static final String PUBLIC_AUTOMATICALLY_REGISTER_PLUGINS_METADATA_KEY =
- "io.flutter.automatically-register-plugins";
+ "io.flutter." + FlutterLoader.AUTOMATICALLY_REGISTER_PLUGINS_KEY;
@NonNull
private static ApplicationInfo getApplicationInfo(@NonNull Context applicationContext) {
@@ -42,20 +47,6 @@ public final class ApplicationInfoLoader {
return metadata.getString(key, null);
}
- private static String getStringWithFallback(Bundle metadata, String key, String fallbackKey) {
- if (metadata == null) {
- return null;
- }
-
- String metadataString = metadata.getString(key, null);
-
- if (metadataString == null) {
- metadataString = metadata.getString(fallbackKey);
- }
-
- return metadataString;
- }
-
private static boolean getBoolean(Bundle metadata, String key, boolean defaultValue) {
if (metadata == null) {
return defaultValue;
@@ -155,21 +146,11 @@ public final class ApplicationInfoLoader {
@NonNull
public static FlutterApplicationInfo load(@NonNull Context applicationContext) {
ApplicationInfo appInfo = getApplicationInfo(applicationContext);
-
- // TODO(camsim99): Remove support for DEPRECATED_AOT_SHARED_LIBRARY_NAME and
- // DEPRECATED_FLUTTER_ASSETS_DIR
- // when all usage of the deprecated names has been removed.
return new FlutterApplicationInfo(
- getStringWithFallback(
- appInfo.metaData,
- FlutterShellArgs.DEPRECATED_AOT_SHARED_LIBRARY_NAME.metadataKey,
- FlutterShellArgs.AOT_SHARED_LIBRARY_NAME.metadataKey),
- getString(appInfo.metaData, FlutterShellArgs.VM_SNAPSHOT_DATA.metadataKey),
- getString(appInfo.metaData, FlutterShellArgs.ISOLATE_SNAPSHOT_DATA.metadataKey),
- getStringWithFallback(
- appInfo.metaData,
- FlutterShellArgs.DEPRECATED_FLUTTER_ASSETS_DIR.metadataKey,
- FlutterShellArgs.FLUTTER_ASSETS_DIR.metadataKey),
+ getString(appInfo.metaData, PUBLIC_AOT_SHARED_LIBRARY_NAME),
+ getString(appInfo.metaData, PUBLIC_VM_SNAPSHOT_DATA_KEY),
+ getString(appInfo.metaData, PUBLIC_ISOLATE_SNAPSHOT_DATA_KEY),
+ getString(appInfo.metaData, PUBLIC_FLUTTER_ASSETS_DIR_KEY),
getNetworkPolicy(appInfo, applicationContext),
appInfo.nativeLibraryDir,
getBoolean(appInfo.metaData, PUBLIC_AUTOMATICALLY_REGISTER_PLUGINS_METADATA_KEY, true));
diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java
index c4897f83a9b..07df17b5d85 100644
--- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java
+++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java
@@ -23,7 +23,6 @@ import io.flutter.BuildConfig;
import io.flutter.FlutterInjector;
import io.flutter.Log;
import io.flutter.embedding.engine.FlutterJNI;
-import io.flutter.embedding.engine.FlutterShellArgs;
import io.flutter.util.HandlerCompat;
import io.flutter.util.PathUtils;
import io.flutter.util.TraceSection;
@@ -34,19 +33,54 @@ import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
-import java.util.concurrent.atomic.AtomicBoolean;
/** Finds Flutter resources in an application APK and also loads Flutter's native library. */
public class FlutterLoader {
private static final String TAG = "FlutterLoader";
- // Flags to only be set internally by default. Match values in flutter::switches.
- private static final String SNAPSHOT_ASSET_PATH_KEY = "snapshot-asset-path";
- private static final String AOT_VMSERVICE_SHARED_LIBRARY_NAME =
- "aot-vmservice-shared-library-name";
+ private static final String OLD_GEN_HEAP_SIZE_META_DATA_KEY =
+ "io.flutter.embedding.android.OldGenHeapSize";
+ private static final String ENABLE_IMPELLER_META_DATA_KEY =
+ "io.flutter.embedding.android.EnableImpeller";
+ private static final String ENABLE_VULKAN_VALIDATION_META_DATA_KEY =
+ "io.flutter.embedding.android.EnableVulkanValidation";
+ private static final String IMPELLER_BACKEND_META_DATA_KEY =
+ "io.flutter.embedding.android.ImpellerBackend";
+ private static final String IMPELLER_OPENGL_GPU_TRACING_DATA_KEY =
+ "io.flutter.embedding.android.EnableOpenGLGPUTracing";
+ private static final String IMPELLER_VULKAN_GPU_TRACING_DATA_KEY =
+ "io.flutter.embedding.android.EnableVulkanGPUTracing";
+ private static final String DISABLE_MERGED_PLATFORM_UI_THREAD_KEY =
+ "io.flutter.embedding.android.DisableMergedPlatformUIThread";
+ private static final String ENABLE_SURFACE_CONTROL =
+ "io.flutter.embedding.android.EnableSurfaceControl";
+ private static final String ENABLE_FLUTTER_GPU = "io.flutter.embedding.android.EnableFlutterGPU";
+ private static final String IMPELLER_LAZY_SHADER_MODE =
+ "io.flutter.embedding.android.ImpellerLazyShaderInitialization";
+ private static final String IMPELLER_ANTIALIAS_LINES =
+ "io.flutter.embedding.android.ImpellerAntialiasLines";
- // Flag set for generating GeneratedPluginRegistrant.java.
- private static final String FLUTTER_EMBEDDING_KEY = "flutterEmbedding";
+ /**
+ * Set whether leave or clean up the VM after the last shell shuts down. It can be set from app's
+ * meta-data in in AndroidManifest.xml. Set it to true in to leave the Dart VM,
+ * set it to false to destroy VM.
+ *
+ * If your want to let your app destroy the last shell and re-create shells more quickly, set
+ * it to true, otherwise if you want to clean up the memory of the leak VM, set it to false.
+ *
+ *
TODO(eggfly): Should it be set to false by default?
+ * https://github.com/flutter/flutter/issues/96843
+ */
+ private static final String LEAK_VM_META_DATA_KEY = "io.flutter.embedding.android.LeakVM";
+
+ // Must match values in flutter::switches
+ static final String AOT_SHARED_LIBRARY_NAME = "aot-shared-library-name";
+ static final String AOT_VMSERVICE_SHARED_LIBRARY_NAME = "aot-vmservice-shared-library-name";
+ static final String SNAPSHOT_ASSET_PATH_KEY = "snapshot-asset-path";
+ static final String VM_SNAPSHOT_DATA_KEY = "vm-snapshot-data";
+ static final String ISOLATE_SNAPSHOT_DATA_KEY = "isolate-snapshot-data";
+ static final String FLUTTER_ASSETS_DIR_KEY = "flutter-assets-dir";
+ static final String AUTOMATICALLY_REGISTER_PLUGINS_KEY = "automatically-register-plugins";
// Resource names used for components of the precompiled snapshot.
private static final String DEFAULT_LIBRARY = "libflutter.so";
@@ -55,7 +89,8 @@ public class FlutterLoader {
private static FlutterLoader instance;
- private boolean enableSoftwareRendering = false;
+ @VisibleForTesting
+ static final String aotSharedLibraryNameFlag = "--" + AOT_SHARED_LIBRARY_NAME + "=";
/**
* Creates a {@code FlutterLoader} that uses a default constructed {@link FlutterJNI} and {@link
@@ -260,21 +295,6 @@ public class FlutterLoader {
*/
public void ensureInitializationComplete(
@NonNull Context applicationContext, @Nullable String[] args) {
- ensureInitializationComplete(applicationContext, args, BuildConfig.RELEASE);
- }
-
- /**
- * Blocks until initialization of the native system has completed.
- *
- *
Calling this method multiple times has no effect.
- *
- * @param applicationContext The Android application context.
- * @param args Flags sent to the Flutter runtime.
- * @param isRelease Whether or not the Flutter component is running in release mode.
- */
- @VisibleForTesting
- void ensureInitializationComplete(
- @NonNull Context applicationContext, @Nullable String[] args, @NonNull boolean isRelease) {
if (initialized) {
return;
}
@@ -291,183 +311,59 @@ public class FlutterLoader {
InitResult result = initResultFuture.get();
List shellArgs = new ArrayList<>();
-
- // Add engine flags for which defaults set internally take precedent.
shellArgs.add("--icu-symbol-prefix=_binary_icudtl_dat");
+
shellArgs.add(
"--icu-native-lib-path="
+ flutterApplicationInfo.nativeLibraryDir
+ File.separator
+ DEFAULT_LIBRARY);
- // Add AOT shared library name flag if set via the command line. This flag,
- // unlike others, gives precedence to the first occurrence found in shellArgs,
- // so we must add it here before adding flags from the manifest.
if (args != null) {
for (String arg : args) {
- if (arg.startsWith(FlutterShellArgs.AOT_SHARED_LIBRARY_NAME.commandLineArgument)) {
- // Perform security check for path containing application's compiled Dart
- // code and potentially user-provided compiled native code.
- String aotSharedLibraryPath =
- arg.substring(
- FlutterShellArgs.AOT_SHARED_LIBRARY_NAME.commandLineArgument.length());
- maybeAddAotSharedLibraryNameArg(applicationContext, aotSharedLibraryPath, shellArgs);
- break;
- }
- }
- }
-
- // Add engine flags provided by metadata in the application manifest. These settings will take
- // precedent over any defaults set below, but will be overridden if additionally set by the
- // command line.
- ApplicationInfo applicationInfo =
- applicationContext
- .getPackageManager()
- .getApplicationInfo(
- applicationContext.getPackageName(), PackageManager.GET_META_DATA);
- Bundle applicationMetaData = applicationInfo.metaData;
- final AtomicBoolean oldGenHeapSizeSet = new AtomicBoolean(false);
- final AtomicBoolean isLeakVMSet = new AtomicBoolean(false);
-
- if (applicationMetaData != null) {
- applicationMetaData.keySet().stream()
- .filter(metadataKey -> !metadataKey.equals(FLUTTER_EMBEDDING_KEY))
- .forEach(
- metadataKey -> {
- FlutterShellArgs.Flag flag = FlutterShellArgs.getFlagByMetadataKey(metadataKey);
- if (flag == null) {
- // Manifest flag was not recognized.
- Log.w(
- TAG,
- "Flag with metadata key "
- + metadataKey
- + " is not recognized. Please ensure that the flag is defined in the FlutterShellArgs.");
- return;
- } else if (FlutterShellArgs.isDisabled(flag)) {
- // Do not allow disabled flags.
- throw new IllegalArgumentException(
- metadataKey
- + " is disabled and no longer allowed. Please remove this flag from your application manifest.");
- } else if (FlutterShellArgs.getReplacementFlagIfDeprecated(flag) != null) {
- Log.w(
- TAG,
- "If you are trying to specify "
- + flag.metadataKey
- + " in your application manifest, please make sure to use the new metadata key name: "
- + FlutterShellArgs.getReplacementFlagIfDeprecated(flag).metadataKey);
- } else if (!flag.allowedInRelease && isRelease) {
- // Manifest flag is not allowed in release builds.
- Log.w(
- TAG,
- "Flag with metadata key "
- + metadataKey
- + " is not allowed in release builds and will be ignored if specified in the application manifest or via the command line.");
- return;
- }
-
- // Handle special cases for specific flags.
- if (flag == FlutterShellArgs.OLD_GEN_HEAP_SIZE) {
- // Mark if old gen heap size is set to track whether or not to set default
- // internally.
- oldGenHeapSizeSet.set(true);
- } else if (flag == FlutterShellArgs.LEAK_VM) {
- // Mark if leak VM is set to track whether or not to set default internally.
- isLeakVMSet.set(true);
- } else if (flag == FlutterShellArgs.ENABLE_SOFTWARE_RENDERING) {
- // Enabling software rendering impacts platform views, so save this value
- // so that the PlatformViewsController can be properly configured.
- enableSoftwareRendering =
- applicationMetaData.getBoolean(
- FlutterShellArgs.ENABLE_SOFTWARE_RENDERING.metadataKey, false);
- } else if (flag == FlutterShellArgs.AOT_SHARED_LIBRARY_NAME
- || flag == FlutterShellArgs.DEPRECATED_AOT_SHARED_LIBRARY_NAME) {
- // Perform security check for path containing application's compiled Dart
- // code and potentially user-provided compiled native code.
- String aotSharedLibraryPath = applicationMetaData.getString(metadataKey);
- maybeAddAotSharedLibraryNameArg(
- applicationContext, aotSharedLibraryPath, shellArgs);
- return;
- }
-
- // Add flag to shell args.
- String arg = flag.commandLineArgument;
- if (flag.hasValue()) {
- Object valueObj = applicationMetaData.get(metadataKey);
- String value = valueObj != null ? valueObj.toString() : null;
- if (value == null) {
- Log.w(
- TAG,
- "Flag with metadata key "
- + metadataKey
- + " requires a value, but no value was found. Please ensure that the value is a string.");
- return;
- }
- arg += value;
- }
-
- shellArgs.add(arg);
- });
- }
-
- // Add any remaining engine flags provided by the command line. These settings will take
- // precedent over any flag settings specified by application manifest
- // metadata and any defaults set below.
- if (args != null) {
- for (String arg : args) {
- FlutterShellArgs.Flag flag = FlutterShellArgs.getFlagByCommandLineArgument(arg);
- if (flag == null) {
- // Command line flag was not recognized.
- Log.w(
- TAG,
- "Command line argument "
- + arg
- + "is not recognized. Please ensure that the flag is defined in the FlutterShellArgs.");
- continue;
- } else if (flag.equals(FlutterShellArgs.AOT_SHARED_LIBRARY_NAME)) {
- // This flag has already been handled.
- continue;
- } else if (!flag.allowedInRelease && isRelease) {
- // Flag is not allowed in release builds.
- Log.w(
- TAG,
- "Command line argument "
- + arg
- + " is not allowed in release builds and will be ignored if specified in the application manifest or via the command line.");
- continue;
+ // Perform security check for path containing application's compiled Dart code and
+ // potentially user-provided compiled native code.
+ if (arg.startsWith(aotSharedLibraryNameFlag)) {
+ String safeAotSharedLibraryNameFlag =
+ getSafeAotSharedLibraryNameFlag(applicationContext, arg);
+ if (safeAotSharedLibraryNameFlag != null) {
+ arg = safeAotSharedLibraryNameFlag;
+ } else {
+ // If the library path is not safe, we will skip adding this argument.
+ Log.w(
+ TAG,
+ "Skipping unsafe AOT shared library name flag: "
+ + arg
+ + ". Please ensure that the library is vetted and placed in your application's internal storage.");
+ continue;
+ }
}
+ // TODO(camsim99): This is a dangerous pattern that blindly allows potentially malicious
+ // arguments to be used for engine initialization and should be fixed. See
+ // https://github.com/flutter/flutter/issues/172553.
shellArgs.add(arg);
}
}
- // Add engine flags set by default internally. Some of these settings can be overridden
- // by command line args or application manifest metadata.
-
String kernelPath = null;
if (BuildConfig.DEBUG || BuildConfig.JIT_RELEASE) {
String snapshotAssetPath =
result.dataDirPath + File.separator + flutterApplicationInfo.flutterAssetsDir;
kernelPath = snapshotAssetPath + File.separator + DEFAULT_KERNEL_BLOB;
shellArgs.add("--" + SNAPSHOT_ASSET_PATH_KEY + "=" + snapshotAssetPath);
+ shellArgs.add("--" + VM_SNAPSHOT_DATA_KEY + "=" + flutterApplicationInfo.vmSnapshotData);
shellArgs.add(
- FlutterShellArgs.VM_SNAPSHOT_DATA.commandLineArgument
- + flutterApplicationInfo.vmSnapshotData);
- shellArgs.add(
- FlutterShellArgs.ISOLATE_SNAPSHOT_DATA.commandLineArgument
- + flutterApplicationInfo.isolateSnapshotData);
+ "--" + ISOLATE_SNAPSHOT_DATA_KEY + "=" + flutterApplicationInfo.isolateSnapshotData);
} else {
- // Add default AOT shared library name arg. Note that if a different library
- // is set in the manifest, that value will take precendence and the default
- // libraries will be used as fallbacks in the order that they are added.
- shellArgs.add(
- FlutterShellArgs.AOT_SHARED_LIBRARY_NAME.commandLineArgument
- + flutterApplicationInfo.aotSharedLibraryName);
+ // Add default AOT shared library name arg.
+ shellArgs.add(aotSharedLibraryNameFlag + flutterApplicationInfo.aotSharedLibraryName);
// Some devices cannot load the an AOT shared library based on the library name
// with no directory path. So, we provide a fully qualified path to the default library
// as a workaround for devices where that fails.
shellArgs.add(
- FlutterShellArgs.AOT_SHARED_LIBRARY_NAME.commandLineArgument
+ aotSharedLibraryNameFlag
+ flutterApplicationInfo.nativeLibraryDir
+ File.separator
+ flutterApplicationInfo.aotSharedLibraryName);
@@ -488,17 +384,23 @@ public class FlutterLoader {
shellArgs.add("--log-tag=" + settings.getLogTag());
}
- if (!oldGenHeapSizeSet.get()) {
- // Default to half of total memory.
+ ApplicationInfo applicationInfo =
+ applicationContext
+ .getPackageManager()
+ .getApplicationInfo(
+ applicationContext.getPackageName(), PackageManager.GET_META_DATA);
+ Bundle metaData = applicationInfo.metaData;
+ int oldGenHeapSizeMegaBytes =
+ metaData != null ? metaData.getInt(OLD_GEN_HEAP_SIZE_META_DATA_KEY) : 0;
+ if (oldGenHeapSizeMegaBytes == 0) {
+ // default to half of total memory.
ActivityManager activityManager =
(ActivityManager) applicationContext.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memInfo);
- int oldGenHeapSizeMegaBytes = (int) (memInfo.totalMem / 1e6 / 2);
- shellArgs.add(
- FlutterShellArgs.OLD_GEN_HEAP_SIZE.commandLineArgument
- + String.valueOf(oldGenHeapSizeMegaBytes));
+ oldGenHeapSizeMegaBytes = (int) (memInfo.totalMem / 1e6 / 2);
}
+ shellArgs.add("--old-gen-heap-size=" + oldGenHeapSizeMegaBytes);
DisplayMetrics displayMetrics = applicationContext.getResources().getDisplayMetrics();
int screenWidth = displayMetrics.widthPixels;
@@ -510,10 +412,49 @@ public class FlutterLoader {
shellArgs.add("--prefetched-default-font-manager");
- if (!isLeakVMSet.get()) {
- shellArgs.add(FlutterShellArgs.LEAK_VM.commandLineArgument + "true");
+ if (metaData != null) {
+ if (metaData.containsKey(ENABLE_IMPELLER_META_DATA_KEY)) {
+ if (metaData.getBoolean(ENABLE_IMPELLER_META_DATA_KEY)) {
+ shellArgs.add("--enable-impeller=true");
+ } else {
+ shellArgs.add("--enable-impeller=false");
+ }
+ }
+ if (metaData.getBoolean(ENABLE_VULKAN_VALIDATION_META_DATA_KEY, false)) {
+ shellArgs.add("--enable-vulkan-validation");
+ }
+ if (metaData.getBoolean(IMPELLER_OPENGL_GPU_TRACING_DATA_KEY, false)) {
+ shellArgs.add("--enable-opengl-gpu-tracing");
+ }
+ if (metaData.getBoolean(IMPELLER_VULKAN_GPU_TRACING_DATA_KEY, false)) {
+ shellArgs.add("--enable-vulkan-gpu-tracing");
+ }
+ if (metaData.getBoolean(DISABLE_MERGED_PLATFORM_UI_THREAD_KEY, false)) {
+ throw new IllegalArgumentException(
+ DISABLE_MERGED_PLATFORM_UI_THREAD_KEY + " is no longer allowed.");
+ }
+ if (metaData.getBoolean(ENABLE_FLUTTER_GPU, false)) {
+ shellArgs.add("--enable-flutter-gpu");
+ }
+ if (metaData.getBoolean(ENABLE_SURFACE_CONTROL, false)) {
+ shellArgs.add("--enable-surface-control");
+ }
+
+ String backend = metaData.getString(IMPELLER_BACKEND_META_DATA_KEY);
+ if (backend != null) {
+ shellArgs.add("--impeller-backend=" + backend);
+ }
+ if (metaData.getBoolean(IMPELLER_LAZY_SHADER_MODE)) {
+ shellArgs.add("--impeller-lazy-shader-mode");
+ }
+ if (metaData.getBoolean(IMPELLER_ANTIALIAS_LINES)) {
+ shellArgs.add("--impeller-antialias-lines");
+ }
}
+ final String leakVM = isLeakVM(metaData) ? "true" : "false";
+ shellArgs.add("--leak-vm=" + leakVM);
+
long initTimeMillis = SystemClock.uptimeMillis() - initStartTimestampMillis;
flutterJNI.init(
@@ -532,49 +473,10 @@ public class FlutterLoader {
}
}
- /** Adds the AOT shared library name argument to the shell args if the provided path is safe. */
- private void maybeAddAotSharedLibraryNameArg(
- @NonNull Context applicationContext,
- @NonNull String aotSharedLibraryPath,
- @NonNull List shellArgs) {
- String safeAotSharedLibraryName = null;
- try {
- safeAotSharedLibraryName =
- getSafeAotSharedLibraryName(applicationContext, aotSharedLibraryPath);
- } catch (IOException exception) {
- Log.w(
- TAG,
- "Error while validating AOT shared library name flag: " + aotSharedLibraryPath,
- exception);
- }
-
- if (safeAotSharedLibraryName != null) {
- shellArgs.add(
- FlutterShellArgs.AOT_SHARED_LIBRARY_NAME.commandLineArgument + safeAotSharedLibraryName);
- } else {
- // If the library path is not safe, we will skip adding this argument.
- Log.w(
- TAG,
- "Skipping unsafe AOT shared library name flag: "
- + aotSharedLibraryPath
- + ". Please ensure that the library is vetted and placed in your application's internal storage.");
- }
- }
-
/**
- * Returns whether software rendering is enabled.
- *
- * {@link #ensureInitializationComplete} must be called first in order to retrieve this value.
- * Otherwise, this will return false.
- */
- public boolean getSofwareRenderingEnabledViaManifest() {
- return enableSoftwareRendering;
- }
-
- /**
- * Returns the canonical path to the AOT shared library that the engine will use to load the
- * application's Dart code if it lives within a path we consider safe, which is a path within the
- * application's internal storage. Otherwise, returns null.
+ * Returns the AOT shared library name flag with the canonical path to the library that the engine
+ * will use to load application's Dart code if it lives within a path we consider safe, which is a
+ * path within the application's internal storage. Otherwise, returns null.
*
*
If the library lives within the application's internal storage, this means that the
* application developer either explicitly placed the library there or set the Android Gradle
@@ -582,9 +484,17 @@ public class FlutterLoader {
* https://developer.android.com/build/releases/past-releases/agp-4-2-0-release-notes#compress-native-libs-dsl
* for more information.
*/
- private String getSafeAotSharedLibraryName(
- @NonNull Context applicationContext, @NonNull String aotSharedLibraryPath)
+ private String getSafeAotSharedLibraryNameFlag(
+ @NonNull Context applicationContext, @NonNull String aotSharedLibraryNameArg)
throws IOException {
+ // Isolate AOT shared library path.
+ if (!aotSharedLibraryNameArg.startsWith(aotSharedLibraryNameFlag)) {
+ throw new IllegalArgumentException(
+ "AOT shared library name flag was not specified correctly; please use --aot-shared-library-name=.");
+ }
+ String aotSharedLibraryPath =
+ aotSharedLibraryNameArg.substring(aotSharedLibraryNameFlag.length());
+
// Canocalize path for safety analysis.
File aotSharedLibraryFile = getFileFromPath(aotSharedLibraryPath);
@@ -609,7 +519,7 @@ public class FlutterLoader {
boolean isSoFile = aotSharedLibraryPathCanonicalPath.endsWith(".so");
if (livesWithinInternalStorage && isSoFile) {
- return aotSharedLibraryPathCanonicalPath;
+ return aotSharedLibraryNameFlag + aotSharedLibraryPathCanonicalPath;
}
// If the library does not live within the application's internal storage, we will not use it.
Log.e(
@@ -625,6 +535,14 @@ public class FlutterLoader {
return new File(path);
}
+ private static boolean isLeakVM(@Nullable Bundle metaData) {
+ final boolean leakVMDefaultValue = true;
+ if (metaData == null) {
+ return leakVMDefaultValue;
+ }
+ return metaData.getBoolean(LEAK_VM_META_DATA_KEY, leakVMDefaultValue);
+ }
+
/**
* Same as {@link #ensureInitializationComplete(Context, String[])} but waiting on a background
* thread, then invoking {@code callback} on the {@code callbackHandler}.
diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java
index 253ecf5005f..ca2e40d1b96 100644
--- a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java
+++ b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java
@@ -40,6 +40,7 @@ import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterEngineCache;
import io.flutter.embedding.engine.FlutterEngineGroup;
import io.flutter.embedding.engine.FlutterEngineGroupCache;
+import io.flutter.embedding.engine.FlutterShellArgs;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.loader.FlutterLoader;
import io.flutter.embedding.engine.plugins.activity.ActivityControlSurface;
@@ -88,6 +89,7 @@ public class FlutterActivityAndFragmentDelegateTest {
mockHost = mock(FlutterActivityAndFragmentDelegate.Host.class);
when(mockHost.getContext()).thenReturn(ctx);
when(mockHost.getLifecycle()).thenReturn(mock(Lifecycle.class));
+ when(mockHost.getFlutterShellArgs()).thenReturn(new FlutterShellArgs(new String[] {}));
when(mockHost.getDartEntrypointFunctionName()).thenReturn("main");
when(mockHost.getDartEntrypointArgs()).thenReturn(null);
when(mockHost.getAppBundlePath()).thenReturn("/fake/path");
@@ -104,6 +106,7 @@ public class FlutterActivityAndFragmentDelegateTest {
mockHost2 = mock(FlutterActivityAndFragmentDelegate.Host.class);
when(mockHost2.getContext()).thenReturn(ctx);
when(mockHost2.getLifecycle()).thenReturn(mock(Lifecycle.class));
+ when(mockHost2.getFlutterShellArgs()).thenReturn(new FlutterShellArgs(new String[] {}));
when(mockHost2.getDartEntrypointFunctionName()).thenReturn("main");
when(mockHost2.getDartEntrypointArgs()).thenReturn(null);
when(mockHost2.getAppBundlePath()).thenReturn("/fake/path");
@@ -468,6 +471,8 @@ public class FlutterActivityAndFragmentDelegateTest {
activity -> {
when(customMockHost.getActivity()).thenReturn(activity);
when(customMockHost.getLifecycle()).thenReturn(mock(Lifecycle.class));
+ when(customMockHost.getFlutterShellArgs())
+ .thenReturn(new FlutterShellArgs(new String[] {}));
when(customMockHost.getDartEntrypointFunctionName()).thenReturn("main");
when(customMockHost.getAppBundlePath()).thenReturn("/fake/path");
when(customMockHost.getInitialRoute()).thenReturn("/");
diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java
index ce711b86c3a..6875f8b756b 100644
--- a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java
+++ b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java
@@ -250,7 +250,7 @@ public class FlutterActivityTest {
assertNull(flutterActivity.getDartEntrypointLibraryUri());
assertNull(flutterActivity.getDartEntrypointArgs());
assertEquals("/", flutterActivity.getInitialRoute());
- assertArrayEquals(new String[] {}, flutterActivity.getFlutterShellArgs());
+ assertArrayEquals(new String[] {}, flutterActivity.getFlutterShellArgs().toArray());
assertTrue(flutterActivity.shouldAttachEngineToActivity());
assertNull(flutterActivity.getCachedEngineId());
assertTrue(flutterActivity.shouldDestroyEngineWithHost());
@@ -303,7 +303,7 @@ public class FlutterActivityTest {
assertEquals("/custom/route", flutterActivity.getInitialRoute());
assertArrayEquals(
new String[] {"foo", "bar"}, flutterActivity.getDartEntrypointArgs().toArray());
- assertArrayEquals(new String[] {}, flutterActivity.getFlutterShellArgs());
+ assertArrayEquals(new String[] {}, flutterActivity.getFlutterShellArgs().toArray());
assertTrue(flutterActivity.shouldAttachEngineToActivity());
assertNull(flutterActivity.getCachedEngineId());
assertTrue(flutterActivity.shouldDestroyEngineWithHost());
@@ -328,7 +328,7 @@ public class FlutterActivityTest {
assertEquals("my_cached_engine_group", flutterActivity.getCachedEngineGroupId());
assertEquals("custom_entrypoint", flutterActivity.getDartEntrypointFunctionName());
assertEquals("/custom/route", flutterActivity.getInitialRoute());
- assertArrayEquals(new String[] {}, flutterActivity.getFlutterShellArgs());
+ assertArrayEquals(new String[] {}, flutterActivity.getFlutterShellArgs().toArray());
assertTrue(flutterActivity.shouldAttachEngineToActivity());
assertTrue(flutterActivity.shouldDestroyEngineWithHost());
assertNull(flutterActivity.getCachedEngineId());
@@ -393,7 +393,7 @@ public class FlutterActivityTest {
Robolectric.buildActivity(FlutterActivity.class, intent);
FlutterActivity flutterActivity = activityController.get();
- assertArrayEquals(new String[] {}, flutterActivity.getFlutterShellArgs());
+ assertArrayEquals(new String[] {}, flutterActivity.getFlutterShellArgs().toArray());
assertTrue(flutterActivity.shouldAttachEngineToActivity());
assertEquals("my_cached_engine", flutterActivity.getCachedEngineId());
assertFalse(flutterActivity.shouldDestroyEngineWithHost());
@@ -409,7 +409,7 @@ public class FlutterActivityTest {
Robolectric.buildActivity(FlutterActivity.class, intent);
FlutterActivity flutterActivity = activityController.get();
- assertArrayEquals(new String[] {}, flutterActivity.getFlutterShellArgs());
+ assertArrayEquals(new String[] {}, flutterActivity.getFlutterShellArgs().toArray());
assertTrue(flutterActivity.shouldAttachEngineToActivity());
assertEquals("my_cached_engine", flutterActivity.getCachedEngineId());
assertTrue(flutterActivity.shouldDestroyEngineWithHost());
diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java
index 2e733635fcd..a9ac78f4fec 100644
--- a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java
+++ b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java
@@ -31,6 +31,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterEngineCache;
import io.flutter.embedding.engine.FlutterJNI;
+import io.flutter.embedding.engine.FlutterShellArgs;
import io.flutter.embedding.engine.loader.FlutterLoader;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
@@ -301,8 +302,8 @@ public class FlutterAndroidComponentTest {
@NonNull
@Override
- public String[] getFlutterShellArgs() {
- return new String[0];
+ public FlutterShellArgs getFlutterShellArgs() {
+ return new FlutterShellArgs(new String[] {});
}
@Nullable
diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java
index 048b72e7506..581154ed66e 100644
--- a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java
+++ b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java
@@ -69,7 +69,7 @@ public class FlutterFragmentTest {
assertNull(fragment.getDartEntrypointLibraryUri());
assertNull(fragment.getDartEntrypointArgs());
assertEquals("/", fragment.getInitialRoute());
- assertArrayEquals(new String[] {}, fragment.getFlutterShellArgs());
+ assertArrayEquals(new String[] {}, fragment.getFlutterShellArgs().toArray());
assertTrue(fragment.shouldAttachEngineToActivity());
assertFalse(fragment.shouldHandleDeeplinking());
assertNull(fragment.getCachedEngineId());
@@ -100,7 +100,7 @@ public class FlutterFragmentTest {
assertEquals("package:foo/bar.dart", fragment.getDartEntrypointLibraryUri());
assertEquals("/custom/route", fragment.getInitialRoute());
assertArrayEquals(new String[] {"foo", "bar"}, fragment.getDartEntrypointArgs().toArray());
- assertArrayEquals(new String[] {}, fragment.getFlutterShellArgs());
+ assertArrayEquals(new String[] {}, fragment.getFlutterShellArgs().toArray());
assertFalse(fragment.shouldAttachEngineToActivity());
assertTrue(fragment.shouldHandleDeeplinking());
assertNull(fragment.getCachedEngineId());
@@ -129,7 +129,7 @@ public class FlutterFragmentTest {
assertEquals("my_cached_engine_group", fragment.getCachedEngineGroupId());
assertEquals("custom_entrypoint", fragment.getDartEntrypointFunctionName());
assertEquals("/custom/route", fragment.getInitialRoute());
- assertArrayEquals(new String[] {}, fragment.getFlutterShellArgs());
+ assertArrayEquals(new String[] {}, fragment.getFlutterShellArgs().toArray());
assertFalse(fragment.shouldAttachEngineToActivity());
assertTrue(fragment.shouldHandleDeeplinking());
assertNull(fragment.getCachedEngineId());
diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/FlutterEngineConnectionRegistryTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/FlutterEngineConnectionRegistryTest.java
index 311d66beba1..091ca42931f 100644
--- a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/FlutterEngineConnectionRegistryTest.java
+++ b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/FlutterEngineConnectionRegistryTest.java
@@ -11,19 +11,14 @@ import static org.mockito.Mockito.*;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import io.flutter.embedding.android.ExclusiveAppComponent;
-import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.loader.FlutterLoader;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
-import io.flutter.embedding.engine.renderer.FlutterRenderer;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.platform.PlatformViewsController;
import io.flutter.plugin.platform.PlatformViewsController2;
@@ -90,19 +85,6 @@ public class FlutterEngineConnectionRegistryTest {
when(flutterEngine.getPlatformViewsControllerDelegator())
.thenReturn(platformViewsControllerDelegator);
- PackageManager packageManager = mock(PackageManager.class);
- String packageName = "io.flutter.test";
- ApplicationInfo applicationInfo = new ApplicationInfo();
- applicationInfo.metaData = new Bundle();
- when(context.getPackageName()).thenReturn(packageName);
- when(context.getPackageManager()).thenReturn(packageManager);
- try {
- when(packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA))
- .thenReturn(applicationInfo);
- } catch (PackageManager.NameNotFoundException e) {
- fail("Mocking application info threw an exception");
- }
-
FlutterLoader flutterLoader = mock(FlutterLoader.class);
ExclusiveAppComponent appComponent = mock(ExclusiveAppComponent.class);
@@ -145,31 +127,41 @@ public class FlutterEngineConnectionRegistryTest {
}
@Test
- public void attachToActivityConfiguresSoftwareRendering() {
+ public void softwareRendering() {
Context context = mock(Context.class);
+
FlutterEngine flutterEngine = mock(FlutterEngine.class);
PlatformViewsController platformViewsController = mock(PlatformViewsController.class);
- FlutterLoader flutterLoader = mock(FlutterLoader.class);
- ExclusiveAppComponent appComponent = mock(ExclusiveAppComponent.class);
- Activity activity = mock(Activity.class);
- Lifecycle lifecycle = mock(Lifecycle.class);
-
- when(flutterEngine.getPlatformViewsController()).thenReturn(platformViewsController);
+ PlatformViewsController2 platformViewsController2 = mock(PlatformViewsController2.class);
PlatformViewsControllerDelegator platformViewsControllerDelegator =
mock(PlatformViewsControllerDelegator.class);
when(flutterEngine.getPlatformViewsControllerDelegator())
.thenReturn(platformViewsControllerDelegator);
- when(flutterEngine.getDartExecutor()).thenReturn(mock(DartExecutor.class));
- when(flutterEngine.getRenderer()).thenReturn(mock(FlutterRenderer.class));
+ when(flutterEngine.getPlatformViewsController()).thenReturn(platformViewsController);
+ when(flutterEngine.getPlatformViewsController2()).thenReturn(platformViewsController2);
+
+ FlutterLoader flutterLoader = mock(FlutterLoader.class);
+
+ ExclusiveAppComponent appComponent = mock(ExclusiveAppComponent.class);
+ Activity activity = mock(Activity.class);
+ when(appComponent.getAppComponent()).thenReturn(activity);
+
+ // Test attachToActivity with an Activity that has no Intent.
FlutterEngineConnectionRegistry registry =
new FlutterEngineConnectionRegistry(context, flutterEngine, flutterLoader, null);
+ registry.attachToActivity(appComponent, mock(Lifecycle.class));
+ verify(platformViewsController).setSoftwareRendering(false);
- when(flutterLoader.getSofwareRenderingEnabledViaManifest()).thenReturn(true);
- when(appComponent.getAppComponent()).thenReturn(activity);
- when(activity.getIntent()).thenReturn(mock(Intent.class));
+ Intent intent = mock(Intent.class);
+ when(intent.getBooleanExtra("enable-software-rendering", false)).thenReturn(false);
+ when(activity.getIntent()).thenReturn(intent);
- registry.attachToActivity(appComponent, lifecycle);
+ registry.attachToActivity(appComponent, mock(Lifecycle.class));
+ verify(platformViewsController, times(2)).setSoftwareRendering(false);
+ when(intent.getBooleanExtra("enable-software-rendering", false)).thenReturn(true);
+
+ registry.attachToActivity(appComponent, mock(Lifecycle.class));
verify(platformViewsController).setSoftwareRendering(true);
}
diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/FlutterShellArgsIntentUtilsTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/FlutterShellArgsIntentUtilsTest.java
deleted file mode 100644
index 4f5fbfdf96c..00000000000
--- a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/FlutterShellArgsIntentUtilsTest.java
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package test.io.flutter.embedding.engine;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.content.Intent;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import io.flutter.embedding.engine.FlutterShellArgsIntentUtils;
-import java.util.Arrays;
-import java.util.HashSet;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class FlutterShellArgsIntentUtilsTest {
- @Test
- public void itProcessesShellFlags() {
- // Setup the test.
- Intent intent = new Intent();
- intent.putExtra("dart-flags", "--observe --no-hot --no-pub");
- intent.putExtra("trace-skia-allowlist", "skia.a,skia.b");
-
- // Execute the behavior under test.
- String[] args = FlutterShellArgsIntentUtils.getFlutterShellCommandLineArgs(intent);
- HashSet argValues = new HashSet(Arrays.asList(args));
-
- // Verify results.
- assertEquals(2, argValues.size());
- assertTrue(argValues.contains("--dart-flags=--observe --no-hot --no-pub"));
- assertTrue(argValues.contains("--trace-skia-allowlist=skia.a,skia.b"));
- }
-}
diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/FlutterShellArgsTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/FlutterShellArgsTest.java
index ebd0085c619..0f05dc67f67 100644
--- a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/FlutterShellArgsTest.java
+++ b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/FlutterShellArgsTest.java
@@ -2,121 +2,35 @@
// 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;
+package test.io.flutter.embedding.engine;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
+import android.content.Intent;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import io.flutter.embedding.engine.FlutterShellArgs;
+import java.util.Arrays;
+import java.util.HashSet;
import org.junit.Test;
+import org.junit.runner.RunWith;
+@RunWith(AndroidJUnit4.class)
public class FlutterShellArgsTest {
-
@Test
- public void allFlags_containsAllFlags() {
- // Count the number of declared flags in FlutterShellArgs.
- int declaredFlagsCount = 0;
- for (Field field : FlutterShellArgs.class.getDeclaredFields()) {
- if (FlutterShellArgs.Flag.class.isAssignableFrom(field.getType())
- && Modifier.isStatic(field.getModifiers())
- && Modifier.isFinal(field.getModifiers())) {
- declaredFlagsCount++;
- }
- }
+ public void itProcessesShellFlags() {
+ // Setup the test.
+ Intent intent = new Intent();
+ intent.putExtra("dart-flags", "--observe --no-hot --no-pub");
+ intent.putExtra("trace-skia-allowlist", "skia.a,skia.b");
- // Check that the number of declared flags matches the size of ALL_FLAGS.
- assertEquals(
- "If you are adding a new Flag to FlutterShellArgs, please make sure it is added to ALL_FLAGS as well. Otherwise, the flag will be silently ignored when specified.",
- declaredFlagsCount,
- FlutterShellArgs.ALL_FLAGS.size());
- }
+ // Execute the behavior under test.
+ FlutterShellArgs args = FlutterShellArgs.fromIntent(intent);
+ HashSet argValues = new HashSet(Arrays.asList(args.toArray()));
- @SuppressWarnings("deprecation")
- @Test
- public void allFlags_haveExpectedMetaDataNamePrefix() {
- String defaultPrefix = "io.flutter.embedding.android.";
- for (FlutterShellArgs.Flag flag : FlutterShellArgs.ALL_FLAGS) {
- // Test all non-deprecated flags that should have the default prefix.
- if (!flag.equals(FlutterShellArgs.DEPRECATED_AOT_SHARED_LIBRARY_NAME)
- && !flag.equals(FlutterShellArgs.DEPRECATED_FLUTTER_ASSETS_DIR)) {
- assertTrue(
- "Flag " + flag.commandLineArgument + " does not have the correct metadata key prefix.",
- flag.metadataKey.startsWith(defaultPrefix));
- }
- }
- }
-
- @Test
- public void getFlagByMetadataKey_returnsExpectedFlagWhenValidKeySpecified() {
- FlutterShellArgs.Flag flag =
- FlutterShellArgs.getFlagByMetadataKey("io.flutter.embedding.android.AOTSharedLibraryName");
- assertEquals(FlutterShellArgs.AOT_SHARED_LIBRARY_NAME, flag);
- }
-
- @Test
- public void getFlagByMetadataKey_returnsNullWhenInvalidKeySpecified() {
- FlutterShellArgs.Flag flag =
- FlutterShellArgs.getFlagByMetadataKey("io.flutter.embedding.android.InvalidMetaDataKey");
- assertNull("Should return null for an invalid meta-data key", flag);
- }
-
- @Test
- public void getFlagByCommandLineArgument_returnsExpectedFlagWhenValidArgumentSpecified() {
- FlutterShellArgs.Flag flag =
- FlutterShellArgs.getFlagByCommandLineArgument("--flutter-assets-dir=");
- assertEquals(FlutterShellArgs.FLUTTER_ASSETS_DIR, flag);
- }
-
- @Test
- public void getFlagByCommandLineArgument_returnsNullWhenInvalidArgumentSpecified() {
- assertNull(FlutterShellArgs.getFlagFromIntentKey("--non-existent-flag"));
- }
-
- @Test
- public void getFlagFromIntentKey_returnsExpectedFlagWhenValidKeySpecified() {
- // Test flag without value.
- FlutterShellArgs.Flag flag = FlutterShellArgs.getFlagFromIntentKey("old-gen-heap-size");
- assertEquals(FlutterShellArgs.OLD_GEN_HEAP_SIZE, flag);
-
- // Test with flag.
- flag = FlutterShellArgs.getFlagFromIntentKey("vm-snapshot-data");
- assertEquals(FlutterShellArgs.VM_SNAPSHOT_DATA, flag);
- }
-
- @Test
- public void getFlagFromIntentKey_returnsNullWhenInvalidKeySpecified() {
- assertNull(FlutterShellArgs.getFlagFromIntentKey("non-existent-flag"));
- }
-
- @Test
- public void isDisabled_returnsTrueWhenFlagIsDisabled() {
- assertTrue(FlutterShellArgs.isDisabled(FlutterShellArgs.DISABLE_MERGED_PLATFORM_UI_THREAD));
- }
-
- @Test
- public void isDisabled_returnsFalseWhenFlagIsNotDisabled() {
- assertFalse(FlutterShellArgs.isDisabled(FlutterShellArgs.VM_SNAPSHOT_DATA));
- }
-
- // Deprecated flags are tested in this test.
- @SuppressWarnings("deprecation")
- @Test
- public void getReplacementFlagIfDeprecated_returnsExpectedFlag() {
- assertEquals(
- FlutterShellArgs.AOT_SHARED_LIBRARY_NAME,
- FlutterShellArgs.getReplacementFlagIfDeprecated(
- FlutterShellArgs.DEPRECATED_AOT_SHARED_LIBRARY_NAME));
- assertEquals(
- FlutterShellArgs.FLUTTER_ASSETS_DIR,
- FlutterShellArgs.getReplacementFlagIfDeprecated(
- FlutterShellArgs.DEPRECATED_FLUTTER_ASSETS_DIR));
- }
-
- @Test
- public void getReplacementFlagIfDeprecated_returnsNullWhenFlagIsNotDeprecated() {
- assertNull(FlutterShellArgs.getReplacementFlagIfDeprecated(FlutterShellArgs.VM_SNAPSHOT_DATA));
+ // Verify results.
+ assertEquals(2, argValues.size());
+ assertTrue(argValues.contains("--dart-flags=--observe --no-hot --no-pub"));
+ assertTrue(argValues.contains("--trace-skia-allowlist=skia.a,skia.b"));
}
}
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 089bbd3a030..e594ce80057 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
@@ -24,7 +24,7 @@ import androidx.annotation.NonNull;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import io.flutter.embedding.engine.FlutterJNI;
-import io.flutter.embedding.engine.FlutterShellArgs;
+import io.flutter.embedding.engine.loader.ApplicationInfoLoader;
import java.io.File;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -129,8 +129,8 @@ public class PlayStoreDeferredComponentManagerTest {
TestFlutterJNI jni = new TestFlutterJNI();
Bundle bundle = new Bundle();
- bundle.putString(FlutterShellArgs.AOT_SHARED_LIBRARY_NAME.metadataKey, "custom_name.so");
- bundle.putString(FlutterShellArgs.FLUTTER_ASSETS_DIR.metadataKey, "custom_assets");
+ bundle.putString(ApplicationInfoLoader.PUBLIC_AOT_SHARED_LIBRARY_NAME, "custom_name.so");
+ bundle.putString(ApplicationInfoLoader.PUBLIC_FLUTTER_ASSETS_DIR_KEY, "custom_assets");
Context spyContext = createSpyContext(bundle);
doReturn(null).when(spyContext).getAssets();
@@ -162,7 +162,7 @@ public class PlayStoreDeferredComponentManagerTest {
Bundle bundle = new Bundle();
bundle.putString(PlayStoreDeferredComponentManager.MAPPING_KEY, "123:module:custom_name.so");
- bundle.putString(FlutterShellArgs.FLUTTER_ASSETS_DIR.metadataKey, "custom_assets");
+ bundle.putString(ApplicationInfoLoader.PUBLIC_FLUTTER_ASSETS_DIR_KEY, "custom_assets");
Context spyContext = createSpyContext(bundle);
doReturn(null).when(spyContext).getAssets();
@@ -194,7 +194,7 @@ public class PlayStoreDeferredComponentManagerTest {
Bundle bundle = new Bundle();
bundle.putString(
PlayStoreDeferredComponentManager.MAPPING_KEY, "123:module:custom_name.so,3:,4:");
- bundle.putString(FlutterShellArgs.FLUTTER_ASSETS_DIR.metadataKey, "custom_assets");
+ bundle.putString(ApplicationInfoLoader.PUBLIC_FLUTTER_ASSETS_DIR_KEY, "custom_assets");
Context spyContext = createSpyContext(bundle);
doReturn(null).when(spyContext).getAssets();
diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/loader/ApplicationInfoLoaderTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/loader/ApplicationInfoLoaderTest.java
index 58a51c4f29e..4a5f6183305 100644
--- a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/loader/ApplicationInfoLoaderTest.java
+++ b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/loader/ApplicationInfoLoaderTest.java
@@ -23,7 +23,6 @@ import android.content.res.XmlResourceParser;
import android.os.Bundle;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import io.flutter.embedding.engine.FlutterShellArgs;
import java.io.StringReader;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -71,10 +70,10 @@ public class ApplicationInfoLoaderTest {
@Test
public void itGeneratesCorrectApplicationInfoWithCustomValues() throws Exception {
Bundle bundle = new Bundle();
- bundle.putString(FlutterShellArgs.AOT_SHARED_LIBRARY_NAME.metadataKey, "testaot");
- bundle.putString(FlutterShellArgs.VM_SNAPSHOT_DATA.metadataKey, "testvmsnapshot");
- bundle.putString(FlutterShellArgs.ISOLATE_SNAPSHOT_DATA.metadataKey, "testisolatesnapshot");
- bundle.putString(FlutterShellArgs.FLUTTER_ASSETS_DIR.metadataKey, "testassets");
+ bundle.putString(ApplicationInfoLoader.PUBLIC_AOT_SHARED_LIBRARY_NAME, "testaot");
+ bundle.putString(ApplicationInfoLoader.PUBLIC_VM_SNAPSHOT_DATA_KEY, "testvmsnapshot");
+ bundle.putString(ApplicationInfoLoader.PUBLIC_ISOLATE_SNAPSHOT_DATA_KEY, "testisolatesnapshot");
+ bundle.putString(ApplicationInfoLoader.PUBLIC_FLUTTER_ASSETS_DIR_KEY, "testassets");
Context context = generateMockContext(bundle, null);
FlutterApplicationInfo info = ApplicationInfoLoader.load(context);
assertNotNull(info);
diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/loader/FlutterLoaderTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/loader/FlutterLoaderTest.java
index a7c8f93cbf8..eff98068a1e 100644
--- a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/loader/FlutterLoaderTest.java
+++ b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/loader/FlutterLoaderTest.java
@@ -8,7 +8,6 @@ import static android.os.Looper.getMainLooper;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
@@ -204,6 +203,35 @@ public class FlutterLoaderTest {
assertTrue(arguments.contains(leakVMArg));
}
+ @Test
+ public void itSetsTheLeakVMFromMetaData() {
+ FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
+ FlutterLoader flutterLoader = new FlutterLoader(mockFlutterJNI);
+ Bundle metaData = new Bundle();
+ metaData.putBoolean("io.flutter.embedding.android.LeakVM", false);
+ ctx.getApplicationInfo().metaData = metaData;
+
+ FlutterLoader.Settings settings = new FlutterLoader.Settings();
+ assertFalse(flutterLoader.initialized());
+ flutterLoader.startInitialization(ctx, settings);
+ flutterLoader.ensureInitializationComplete(ctx, null);
+ shadowOf(getMainLooper()).idle();
+
+ final String leakVMArg = "--leak-vm=false";
+ ArgumentCaptor shellArgsCaptor = ArgumentCaptor.forClass(String[].class);
+ verify(mockFlutterJNI, times(1))
+ .init(
+ eq(ctx),
+ shellArgsCaptor.capture(),
+ anyString(),
+ anyString(),
+ anyString(),
+ anyLong(),
+ anyInt());
+ List arguments = Arrays.asList(shellArgsCaptor.getValue());
+ assertTrue(arguments.contains(leakVMArg));
+ }
+
@Test
public void itUsesCorrectExecutorService() {
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
@@ -266,32 +294,22 @@ public class FlutterLoaderTest {
}
@Test
- public void itSetsDeprecatedAotSharedLibraryNameIfPathIsInInternalStorage() throws IOException {
+ public void itSetsEnableImpellerFromMetaData() {
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
- FlutterLoader flutterLoader = spy(new FlutterLoader(mockFlutterJNI));
- Context mockApplicationContext = mock(Context.class);
- File internalStorageDir = ctx.getFilesDir();
- Path internalStorageDirAsPathObj = internalStorageDir.toPath();
+ FlutterLoader flutterLoader = new FlutterLoader(mockFlutterJNI);
+ Bundle metaData = new Bundle();
+ metaData.putBoolean("io.flutter.embedding.android.EnableImpeller", true);
+ ctx.getApplicationInfo().metaData = metaData;
- ctx.getApplicationInfo().nativeLibraryDir =
- Paths.get("some", "path", "doesnt", "matter").toString();
+ FlutterLoader.Settings settings = new FlutterLoader.Settings();
assertFalse(flutterLoader.initialized());
- flutterLoader.startInitialization(ctx);
-
- // Test paths for library living within internal storage.
- String librarySoFileName = "library.so";
- Path testPath = internalStorageDirAsPathObj.resolve(librarySoFileName);
-
- String path = testPath.toString();
- Bundle metadata = new Bundle();
- metadata.putString(
- "io.flutter.embedding.engine.loader.FlutterLoader.aot-shared-library-name", path);
- ctx.getApplicationInfo().metaData = metadata;
-
+ flutterLoader.startInitialization(ctx, settings);
flutterLoader.ensureInitializationComplete(ctx, null);
+ shadowOf(getMainLooper()).idle();
+ final String enableImpellerArg = "--enable-impeller=true";
ArgumentCaptor shellArgsCaptor = ArgumentCaptor.forClass(String[].class);
- verify(mockFlutterJNI)
+ verify(mockFlutterJNI, times(1))
.init(
eq(ctx),
shellArgsCaptor.capture(),
@@ -300,51 +318,27 @@ public class FlutterLoaderTest {
anyString(),
anyLong(),
anyInt());
-
- List actualArgs = Arrays.asList(shellArgsCaptor.getValue());
-
- // This check works because the tests run in debug mode. If run in release (or JIT release)
- // mode, actualArgs would contain the default arguments for AOT shared library name on top
- // of aotSharedLibraryNameArg.
- String canonicalTestPath = testPath.toFile().getCanonicalPath();
- String canonicalAotSharedLibraryNameArg = "--aot-shared-library-name=" + canonicalTestPath;
- assertTrue(
- "Args sent to FlutterJni.init incorrectly did not include path " + path,
- actualArgs.contains(canonicalAotSharedLibraryNameArg));
-
- // Reset FlutterLoader and mockFlutterJNI to make more calls to
- // FlutterLoader.ensureInitialized and mockFlutterJNI.init for testing.
- flutterLoader.initialized = false;
- clearInvocations(mockFlutterJNI);
+ List arguments = Arrays.asList(shellArgsCaptor.getValue());
+ assertTrue(arguments.contains(enableImpellerArg));
}
@Test
- public void itSetsAotSharedLibraryNameIfPathIsInInternalStorageInReleaseMode()
- throws IOException {
+ public void itSetsEnableFlutterGPUFromMetaData() {
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
- FlutterLoader flutterLoader = spy(new FlutterLoader(mockFlutterJNI));
- Context mockApplicationContext = mock(Context.class);
- File internalStorageDir = ctx.getFilesDir();
- Path internalStorageDirAsPathObj = internalStorageDir.toPath();
+ FlutterLoader flutterLoader = new FlutterLoader(mockFlutterJNI);
+ Bundle metaData = new Bundle();
+ metaData.putBoolean("io.flutter.embedding.android.EnableFlutterGPU", true);
+ ctx.getApplicationInfo().metaData = metaData;
- ctx.getApplicationInfo().nativeLibraryDir =
- Paths.get("some", "path", "doesnt", "matter").toString();
+ FlutterLoader.Settings settings = new FlutterLoader.Settings();
assertFalse(flutterLoader.initialized());
- flutterLoader.startInitialization(ctx);
-
- // Test paths for library living within internal storage.
- String librarySoFileName = "library.so";
- Path testPath = internalStorageDirAsPathObj.resolve(librarySoFileName);
-
- String path = testPath.toString();
- Bundle metadata = new Bundle();
- metadata.putString("io.flutter.embedding.android.AOTSharedLibraryName", path);
- ctx.getApplicationInfo().metaData = metadata;
-
- flutterLoader.ensureInitializationComplete(ctx, null, true);
+ flutterLoader.startInitialization(ctx, settings);
+ flutterLoader.ensureInitializationComplete(ctx, null);
+ shadowOf(getMainLooper()).idle();
+ final String enableImpellerArg = "--enable-flutter-gpu";
ArgumentCaptor shellArgsCaptor = ArgumentCaptor.forClass(String[].class);
- verify(mockFlutterJNI)
+ verify(mockFlutterJNI, times(1))
.init(
eq(ctx),
shellArgsCaptor.capture(),
@@ -353,22 +347,66 @@ public class FlutterLoaderTest {
anyString(),
anyLong(),
anyInt());
+ List arguments = Arrays.asList(shellArgsCaptor.getValue());
+ assertTrue(arguments.contains(enableImpellerArg));
+ }
- List actualArgs = Arrays.asList(shellArgsCaptor.getValue());
+ @Test
+ public void itSetsEnableSurfaceControlFromMetaData() {
+ FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
+ FlutterLoader flutterLoader = new FlutterLoader(mockFlutterJNI);
+ Bundle metaData = new Bundle();
+ metaData.putBoolean("io.flutter.embedding.android.EnableSurfaceControl", true);
+ ctx.getApplicationInfo().metaData = metaData;
- // This check works because the tests run in debug mode. If run in release (or JIT release)
- // mode, actualArgs would contain the default arguments for AOT shared library name on top
- // of aotSharedLibraryNameArg.
- String canonicalTestPath = testPath.toFile().getCanonicalPath();
- String canonicalAotSharedLibraryNameArg = "--aot-shared-library-name=" + canonicalTestPath;
- assertTrue(
- "Args sent to FlutterJni.init incorrectly did not include path " + path,
- actualArgs.contains(canonicalAotSharedLibraryNameArg));
+ FlutterLoader.Settings settings = new FlutterLoader.Settings();
+ assertFalse(flutterLoader.initialized());
+ flutterLoader.startInitialization(ctx, settings);
+ flutterLoader.ensureInitializationComplete(ctx, null);
+ shadowOf(getMainLooper()).idle();
- // Reset FlutterLoader and mockFlutterJNI to make more calls to
- // FlutterLoader.ensureInitialized and mockFlutterJNI.init for testing.
- flutterLoader.initialized = false;
- clearInvocations(mockFlutterJNI);
+ final String disabledControlArg = "--enable-surface-control";
+ ArgumentCaptor shellArgsCaptor = ArgumentCaptor.forClass(String[].class);
+ verify(mockFlutterJNI, times(1))
+ .init(
+ eq(ctx),
+ shellArgsCaptor.capture(),
+ anyString(),
+ anyString(),
+ anyString(),
+ anyLong(),
+ anyInt());
+ List arguments = Arrays.asList(shellArgsCaptor.getValue());
+ assertTrue(arguments.contains(disabledControlArg));
+ }
+
+ @Test
+ public void itSetsShaderInitModeFromMetaData() {
+ FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
+ FlutterLoader flutterLoader = new FlutterLoader(mockFlutterJNI);
+ Bundle metaData = new Bundle();
+ metaData.putBoolean("io.flutter.embedding.android.ImpellerLazyShaderInitialization", true);
+ ctx.getApplicationInfo().metaData = metaData;
+
+ FlutterLoader.Settings settings = new FlutterLoader.Settings();
+ assertFalse(flutterLoader.initialized());
+ flutterLoader.startInitialization(ctx, settings);
+ flutterLoader.ensureInitializationComplete(ctx, null);
+ shadowOf(getMainLooper()).idle();
+
+ final String shaderModeArg = "--impeller-lazy-shader-mode";
+ ArgumentCaptor shellArgsCaptor = ArgumentCaptor.forClass(String[].class);
+ verify(mockFlutterJNI, times(1))
+ .init(
+ eq(ctx),
+ shellArgsCaptor.capture(),
+ anyString(),
+ anyString(),
+ anyString(),
+ anyLong(),
+ anyInt());
+ List arguments = Arrays.asList(shellArgsCaptor.getValue());
+ assertTrue(arguments.contains(shaderModeArg));
}
@Test
@@ -408,11 +446,9 @@ public class FlutterLoaderTest {
for (Path testPath : pathsToTest) {
String path = testPath.toString();
- Bundle metadata = new Bundle();
- metadata.putString("io.flutter.embedding.android.AOTSharedLibraryName", path);
- ctx.getApplicationInfo().metaData = metadata;
-
- flutterLoader.ensureInitializationComplete(ctx, null);
+ String aotSharedLibraryNameArg = FlutterLoader.aotSharedLibraryNameFlag + path;
+ String[] args = {aotSharedLibraryNameArg};
+ flutterLoader.ensureInitializationComplete(ctx, args);
ArgumentCaptor shellArgsCaptor = ArgumentCaptor.forClass(String[].class);
verify(mockFlutterJNI)
@@ -431,7 +467,8 @@ public class FlutterLoaderTest {
// mode, actualArgs would contain the default arguments for AOT shared library name on top
// of aotSharedLibraryNameArg.
String canonicalTestPath = testPath.toFile().getCanonicalPath();
- String canonicalAotSharedLibraryNameArg = "--aot-shared-library-name=" + canonicalTestPath;
+ String canonicalAotSharedLibraryNameArg =
+ FlutterLoader.aotSharedLibraryNameFlag + canonicalTestPath;
assertTrue(
"Args sent to FlutterJni.init incorrectly did not include path " + path,
actualArgs.contains(canonicalAotSharedLibraryNameArg));
@@ -486,11 +523,9 @@ public class FlutterLoaderTest {
for (Path testPath : pathsToTest) {
String path = testPath.toString();
- Bundle metadata = new Bundle();
- metadata.putString("io.flutter.embedding.android.AOTSharedLibraryName", path);
- ctx.getApplicationInfo().metaData = metadata;
-
- flutterLoader.ensureInitializationComplete(ctx, null);
+ String aotSharedLibraryNameArg = FlutterLoader.aotSharedLibraryNameFlag + path;
+ String[] args = {aotSharedLibraryNameArg};
+ flutterLoader.ensureInitializationComplete(ctx, args);
ArgumentCaptor shellArgsCaptor = ArgumentCaptor.forClass(String[].class);
verify(mockFlutterJNI)
@@ -509,7 +544,8 @@ public class FlutterLoaderTest {
// mode, actualArgs would contain the default arguments for AOT shared library name on top
// of aotSharedLibraryNameArg.
String canonicalTestPath = testPath.toFile().getCanonicalPath();
- String canonicalAotSharedLibraryNameArg = "--aot-shared-library-name=" + canonicalTestPath;
+ String canonicalAotSharedLibraryNameArg =
+ FlutterLoader.aotSharedLibraryNameFlag + canonicalTestPath;
assertFalse(
"Args sent to FlutterJni.init incorrectly included canonical path " + canonicalTestPath,
actualArgs.contains(canonicalAotSharedLibraryNameArg));
@@ -536,11 +572,8 @@ public class FlutterLoaderTest {
String invalidFilePath = "my\0file.so";
- Bundle metadata = new Bundle();
- metadata.putString("io.flutter.embedding.android.AOTSharedLibraryName", invalidFilePath);
- ctx.getApplicationInfo().metaData = metadata;
-
- flutterLoader.ensureInitializationComplete(ctx, null);
+ String[] args = {FlutterLoader.aotSharedLibraryNameFlag + invalidFilePath};
+ flutterLoader.ensureInitializationComplete(ctx, args);
ArgumentCaptor shellArgsCaptor = ArgumentCaptor.forClass(String[].class);
verify(mockFlutterJNI)
@@ -559,7 +592,7 @@ public class FlutterLoaderTest {
// mode, actualArgs would contain the default arguments for AOT shared library name on top
// of aotSharedLibraryNameArg.
for (String arg : actualArgs) {
- if (arg.startsWith("--aot-shared-library-name=")) {
+ if (arg.startsWith(FlutterLoader.aotSharedLibraryNameFlag)) {
fail();
}
}
@@ -587,11 +620,9 @@ public class FlutterLoaderTest {
when(flutterLoader.getFileFromPath(spySymlinkFile.getPath())).thenReturn(spySymlinkFile);
doReturn(realSoFile.getCanonicalPath()).when(spySymlinkFile).getCanonicalPath();
- Bundle metadata = new Bundle();
- metadata.putString(
- "io.flutter.embedding.android.AOTSharedLibraryName", spySymlinkFile.getPath());
- ctx.getApplicationInfo().metaData = metadata;
- flutterLoader.ensureInitializationComplete(ctx, null);
+ String symlinkArg = FlutterLoader.aotSharedLibraryNameFlag + spySymlinkFile.getPath();
+ String[] args = {symlinkArg};
+ flutterLoader.ensureInitializationComplete(ctx, args);
ArgumentCaptor shellArgsCaptor = ArgumentCaptor.forClass(String[].class);
verify(mockFlutterJNI)
@@ -607,14 +638,12 @@ public class FlutterLoaderTest {
List actualArgs = Arrays.asList(shellArgsCaptor.getValue());
String canonicalSymlinkCanonicalizedPath = realSoFile.getCanonicalPath();
- String aotSharedLibraryNameFlag = "--aot-shared-library-name=";
- String symlinkAotSharedLibraryNameArg = aotSharedLibraryNameFlag + spySymlinkFile.getPath();
String canonicalAotSharedLibraryNameArg =
- aotSharedLibraryNameFlag + canonicalSymlinkCanonicalizedPath;
+ FlutterLoader.aotSharedLibraryNameFlag + canonicalSymlinkCanonicalizedPath;
assertFalse(
"Args sent to FlutterJni.init incorrectly included absolute symlink path: "
+ spySymlinkFile.getAbsolutePath(),
- actualArgs.contains(symlinkAotSharedLibraryNameArg));
+ actualArgs.contains(symlinkArg));
assertTrue(
"Args sent to FlutterJni.init incorrectly did not include canonicalized path of symlink: "
+ canonicalSymlinkCanonicalizedPath,
@@ -645,17 +674,15 @@ public class FlutterLoaderTest {
List unsafeFiles = Arrays.asList(nonSoFile, fileJustOutsideInternalStorage);
Files.deleteIfExists(spySymlinkFile.toPath());
- Bundle metadata = new Bundle();
- metadata.putString(
- "io.flutter.embedding.android.AOTSharedLibraryName", spySymlinkFile.getAbsolutePath());
- ctx.getApplicationInfo().metaData = metadata;
+ String symlinkArg = FlutterLoader.aotSharedLibraryNameFlag + spySymlinkFile.getAbsolutePath();
+ String[] args = {symlinkArg};
for (File unsafeFile : unsafeFiles) {
// Simulate a symlink since some filesystems do not support symlinks.
when(flutterLoader.getFileFromPath(spySymlinkFile.getPath())).thenReturn(spySymlinkFile);
doReturn(unsafeFile.getCanonicalPath()).when(spySymlinkFile).getCanonicalPath();
- flutterLoader.ensureInitializationComplete(ctx, null);
+ flutterLoader.ensureInitializationComplete(ctx, args);
ArgumentCaptor shellArgsCaptor = ArgumentCaptor.forClass(String[].class);
verify(mockFlutterJNI)
@@ -671,11 +698,8 @@ public class FlutterLoaderTest {
List actualArgs = Arrays.asList(shellArgsCaptor.getValue());
String canonicalSymlinkCanonicalizedPath = unsafeFile.getCanonicalPath();
- String aotSharedLibraryNameFlag = "--aot-shared-library-name=";
- String symlinkAotSharedLibraryNameArg =
- aotSharedLibraryNameFlag + spySymlinkFile.getAbsolutePath();
String canonicalAotSharedLibraryNameArg =
- aotSharedLibraryNameFlag + canonicalSymlinkCanonicalizedPath;
+ FlutterLoader.aotSharedLibraryNameFlag + canonicalSymlinkCanonicalizedPath;
assertFalse(
"Args sent to FlutterJni.init incorrectly included canonicalized path of symlink: "
+ canonicalSymlinkCanonicalizedPath,
@@ -683,7 +707,7 @@ public class FlutterLoaderTest {
assertFalse(
"Args sent to FlutterJni.init incorrectly included absolute path of symlink: "
+ spySymlinkFile.getAbsolutePath(),
- actualArgs.contains(symlinkAotSharedLibraryNameArg));
+ actualArgs.contains(symlinkArg));
// Clean up created files.
spySymlinkFile.delete();
@@ -695,623 +719,4 @@ public class FlutterLoaderTest {
clearInvocations(mockFlutterJNI);
}
}
-
- @Test
- public void itSetsEnableSoftwareRenderingFromMetadata() {
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.EnableSoftwareRendering",
- true,
- "--enable-software-rendering");
- }
-
- @Test
- public void getSofwareRenderingEnabledViaManifest_returnsExpectedValueWhenSetViaManifest() {
- FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
- FlutterLoader flutterLoader = new FlutterLoader(mockFlutterJNI);
- Bundle metadata = new Bundle();
-
- metadata.putBoolean("io.flutter.embedding.android.EnableSoftwareRendering", true);
-
- ctx.getApplicationInfo().metaData = metadata;
-
- FlutterLoader.Settings settings = new FlutterLoader.Settings();
- assertFalse(flutterLoader.initialized());
- flutterLoader.startInitialization(ctx, settings);
- flutterLoader.ensureInitializationComplete(ctx, null);
- shadowOf(getMainLooper()).idle();
-
- assertTrue(flutterLoader.getSofwareRenderingEnabledViaManifest());
- }
-
- @Test
- public void itSetsSkiaDeterministicRenderingFromMetadata() {
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.SkiaDeterministicRendering",
- true,
- "--skia-deterministic-rendering");
- }
-
- @Test
- public void itSetsFlutterAssetsDirFromMetadata() {
- String expectedAssetsDir = "flutter_assets_dir";
- // Test debug mode
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.FlutterAssetsDir",
- expectedAssetsDir,
- "--flutter-assets-dir=" + expectedAssetsDir);
-
- // Test release mode.
- testFlagFromMetadataPresentInReleaseMode(
- "io.flutter.embedding.android.FlutterAssetsDir",
- expectedAssetsDir,
- "--flutter-assets-dir=" + expectedAssetsDir);
- }
-
- @Test
- public void itSetsDeprecatedFlutterAssetsDirFromMetadata() {
- String expectedAssetsDir = "flutter_assets_dir";
-
- // Test debug mode.
- testFlagFromMetadataPresent(
- "io.flutter.embedding.engine.loader.FlutterLoader.flutter-assets-dir",
- expectedAssetsDir,
- "--flutter-assets-dir=" + expectedAssetsDir);
-
- // Test release mode.
- testFlagFromMetadataPresentInReleaseMode(
- "io.flutter.embedding.engine.loader.FlutterLoader.flutter-assets-dir",
- expectedAssetsDir,
- "--flutter-assets-dir=" + expectedAssetsDir);
- }
-
- @Test
- public void itSetsOldGenHeapSizeFromMetadata() {
- // Test old gen heap size can be set from metadata in debug mode.
- int expectedOldGenHeapSize = 256;
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.OldGenHeapSize",
- expectedOldGenHeapSize,
- "--old-gen-heap-size=" + expectedOldGenHeapSize);
-
- // Test old gen heap size can be set from metadta in release mode.
- testFlagFromMetadataPresentInReleaseMode(
- "io.flutter.embedding.android.OldGenHeapSize",
- expectedOldGenHeapSize,
- "--old-gen-heap-size=" + expectedOldGenHeapSize);
-
- // Test that default old gen heap size will not be included if it
- // is configured via the manifest.
- ActivityManager activityManager =
- (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
- ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
- activityManager.getMemoryInfo(memInfo);
- int oldGenHeapSizeMegaBytes = (int) (memInfo.totalMem / 1e6 / 2);
- testFlagFromMetadataNotPresent(
- "io.flutter.embedding.android.OldGenHeapSize",
- expectedOldGenHeapSize,
- "--old-gen-heap-size=" + oldGenHeapSizeMegaBytes);
- }
-
- @Test
- public void itSetsEnableImpellerFromMetadata() {
- // Test debug mode.
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.EnableImpeller", true, "--enable-impeller=true");
-
- // Test release mode.
- testFlagFromMetadataPresentInReleaseMode(
- "io.flutter.embedding.android.EnableImpeller", true, "--enable-impeller=true");
- }
-
- @Test
- public void itSetsImpellerBackendFromMetadata() {
- String expectedImpellerBackend = "Vulkan";
-
- // Test debug mode.
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.ImpellerBackend",
- expectedImpellerBackend,
- "--impeller-backend=" + expectedImpellerBackend);
-
- // Test release mode.
- testFlagFromMetadataPresentInReleaseMode(
- "io.flutter.embedding.android.ImpellerBackend",
- expectedImpellerBackend,
- "--impeller-backend=" + expectedImpellerBackend);
- }
-
- @Test
- public void itSetsEnableSurfaceControlFromMetadata() {
- // Test debug mode.
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.EnableSurfaceControl", true, "--enable-surface-control");
-
- // Test release mode.
- testFlagFromMetadataPresentInReleaseMode(
- "io.flutter.embedding.android.EnableSurfaceControl", true, "--enable-surface-control");
- }
-
- @Test
- public void itSetsEnableFlutterGPUFromMetadata() {
- // Test debug mode.
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.EnableFlutterGPU", true, "--enable-flutter-gpu");
-
- // Test release mode.
- testFlagFromMetadataPresentInReleaseMode(
- "io.flutter.embedding.android.EnableFlutterGPU", true, "--enable-flutter-gpu");
- }
-
- @Test
- public void itSetsImpellerLazyShaderModeFromMetadata() {
- // Test debug mode.
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.ImpellerLazyShaderInitialization",
- true,
- "--impeller-lazy-shader-mode=true");
-
- // Test release mode.
- testFlagFromMetadataPresentInReleaseMode(
- "io.flutter.embedding.android.ImpellerLazyShaderInitialization",
- true,
- "--impeller-lazy-shader-mode=true");
- }
-
- @Test
- public void itSetsImpellerAntiAliasLinesFromMetadata() {
- // Test debug mode.
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.ImpellerAntialiasLines", true, "--impeller-antialias-lines");
-
- // Test release mode.
- testFlagFromMetadataPresentInReleaseMode(
- "io.flutter.embedding.android.ImpellerAntialiasLines", true, "--impeller-antialias-lines");
- }
-
- @Test
- public void itSetsVmSnapshotDataFromMetadata() {
- String expectedVmSnapshotData = "vm_snapshot_data";
-
- // Test debug mode.
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.VmSnapshotData",
- expectedVmSnapshotData,
- "--vm-snapshot-data=" + expectedVmSnapshotData);
-
- // Test release mode.
- testFlagFromMetadataPresentInReleaseMode(
- "io.flutter.embedding.android.VmSnapshotData",
- expectedVmSnapshotData,
- "--vm-snapshot-data=" + expectedVmSnapshotData);
- }
-
- @Test
- public void itSetsIsolateSnapshotDataFromMetadata() {
- String expectedIsolateSnapshotData = "isolate_snapshot_data";
-
- // Test debug mode.
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.IsolateSnapshotData",
- expectedIsolateSnapshotData,
- "--isolate-snapshot-data=" + expectedIsolateSnapshotData);
-
- // Test release mode.
- testFlagFromMetadataPresentInReleaseMode(
- "io.flutter.embedding.android.IsolateSnapshotData",
- expectedIsolateSnapshotData,
- "--isolate-snapshot-data=" + expectedIsolateSnapshotData);
- }
-
- @Test
- public void itSetsUseTestFontsFromMetadata() {
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.UseTestFonts", true, "--use-test-fonts");
- }
-
- @Test
- public void itSetsVmServicePortFromMetadata() {
- int expectedVmServicePort = 12345;
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.VMServicePort",
- expectedVmServicePort,
- "--vm-service-port=" + expectedVmServicePort);
- }
-
- @Test
- public void itSetsEnableVulkanValidationFromMetadata() {
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.EnableVulkanValidation", true, "--enable-vulkan-validation");
- }
-
- @Test
- public void itSetsEnableOpenGLGPUTracingFromMetadata() {
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.EnableOpenGLGPUTracing", true, "--enable-opengl-gpu-tracing");
- }
-
- @Test
- public void itSetsEnableVulkanGPUTracingFromMetadata() {
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.EnableVulkanGPUTracing", true, "--enable-vulkan-gpu-tracing");
- }
-
- @Test
- public void itSetsLeakVMFromMetadata() {
- // Test that LeakVM can be set via manifest.
- testFlagFromMetadataPresent("io.flutter.embedding.android.LeakVM", false, "--leak-vm=false");
-
- // Test that default LeakVM will not be included if it is configured via the manifest.
- testFlagFromMetadataNotPresent("io.flutter.embedding.android.LeakVM", false, "--leak-vm=true");
- }
-
- @Test
- public void itSetsTraceStartupFromMetadata() {
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.TraceStartup", true, "--trace-startup");
- }
-
- @Test
- public void itSetsStartPausedFromMetadata() {
- testFlagFromMetadataPresent("io.flutter.embedding.android.StartPaused", true, "--start-paused");
- }
-
- @Test
- public void itSetsDisableServiceAuthCodesFromMetadata() {
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.DisableServiceAuthCodes",
- true,
- "--disable-service-auth-codes");
- }
-
- @Test
- public void itSetsEndlessTraceBufferFromMetadata() {
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.EndlessTraceBuffer", true, "--endless-trace-buffer");
- }
-
- @Test
- public void itSetsEnableDartProfilingFromMetadata() {
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.EnableDartProfiling", true, "--enable-dart-profiling");
- }
-
- @Test
- public void itSetsProfileStartupFromMetadata() {
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.ProfileStartup", true, "--profile-startup");
- }
-
- @Test
- public void itSetsTraceSkiaFromMetadata() {
- testFlagFromMetadataPresent("io.flutter.embedding.android.TraceSkia", true, "--trace-skia");
- }
-
- @Test
- public void itSetsTraceSkiaAllowlistFromMetadata() {
- String expectedTraceSkiaAllowList = "allowed1,allowed2,allowed3";
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.TraceSkiaAllowList",
- expectedTraceSkiaAllowList,
- "--trace-skia-allowlist=" + expectedTraceSkiaAllowList);
- }
-
- @Test
- public void itSetsTraceSystraceFromMetadata() {
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.TraceSystrace", true, "--trace-systrace");
- }
-
- @Test
- public void itSetsTraceToFileFromMetadata() {
- String expectedTraceToFilePath = "/path/to/trace/file";
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.TraceToFile",
- expectedTraceToFilePath,
- "--trace-to-file=" + expectedTraceToFilePath);
- }
-
- @Test
- public void itSetsProfileMicrotasksFromMetadata() {
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.ProfileMicrotasks", true, "--profile-microtasks");
- }
-
- @Test
- public void itSetsDumpSkpOnShaderCompilationFromMetadata() {
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.DumpSkpOnShaderCompilation",
- true,
- "--dump-skp-on-shader-compilation");
- }
-
- @Test
- public void itSetsPurgePersistentCacheFromMetadata() {
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.PurgePersistentCache", true, "--purge-persistent-cache");
- }
-
- @Test
- public void itSetsVerboseLoggingFromMetadata() {
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.VerboseLogging", true, "--verbose-logging");
- }
-
- @Test
- public void itSetsDartFlagsFromMetadata() {
- String expectedDartFlags = "--enable-asserts --enable-vm-service";
- testFlagFromMetadataPresent(
- "io.flutter.embedding.android.DartFlags",
- expectedDartFlags,
- "--dart-flags=" + expectedDartFlags);
- }
-
- @Test
- public void itDoesNotSetDisableMergedPlatformUIThreadFromMetadata() {
- FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
- FlutterLoader flutterLoader = new FlutterLoader(mockFlutterJNI);
- Bundle metadata = new Bundle();
-
- metadata.putBoolean("io.flutter.embedding.android.DisableMergedPlatformUIThread", true);
- ctx.getApplicationInfo().metaData = metadata;
-
- FlutterLoader.Settings settings = new FlutterLoader.Settings();
- assertFalse(flutterLoader.initialized());
- flutterLoader.startInitialization(ctx, settings);
-
- // Verify that an IllegalArgumentException is thrown when DisableMergedPlatformUIThread is set,
- // as it is no longer supported.
- Exception exception =
- assertThrows(
- RuntimeException.class, () -> flutterLoader.ensureInitializationComplete(ctx, null));
- Throwable cause = exception.getCause();
-
- assertNotNull(cause);
- assertTrue(
- "Expected cause to be IllegalArgumentException", cause instanceof IllegalArgumentException);
- assertTrue(
- cause
- .getMessage()
- .contains(
- "io.flutter.embedding.android.DisableMergedPlatformUIThread is disabled and no longer allowed."));
- }
-
- @Test
- public void itDoesNotSetUnrecognizedCommandLineArgument() {
- FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
- FlutterLoader flutterLoader = new FlutterLoader(mockFlutterJNI);
- Bundle metadata = new Bundle();
-
- String[] unrecognizedArg = {"--unrecognized-argument"};
-
- FlutterLoader.Settings settings = new FlutterLoader.Settings();
- assertFalse(flutterLoader.initialized());
- flutterLoader.startInitialization(ctx, settings);
- flutterLoader.ensureInitializationComplete(ctx, unrecognizedArg);
- shadowOf(getMainLooper()).idle();
-
- ArgumentCaptor shellArgsCaptor = ArgumentCaptor.forClass(String[].class);
- verify(mockFlutterJNI, times(1))
- .init(
- eq(ctx),
- shellArgsCaptor.capture(),
- anyString(),
- anyString(),
- anyString(),
- anyLong(),
- anyInt());
- List arguments = Arrays.asList(shellArgsCaptor.getValue());
-
- assertFalse(
- "Unrecognized argument '"
- + unrecognizedArg[0]
- + "' was found in the arguments passed to FlutterJNI.init",
- arguments.contains(unrecognizedArg[0]));
- }
-
- @Test
- public void itDoesSetRecognizedCommandLineArgument() {
- FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
- FlutterLoader flutterLoader = new FlutterLoader(mockFlutterJNI);
- Bundle metadata = new Bundle();
-
- String[] recognizedArg = {"--enable-impeller=true"};
-
- FlutterLoader.Settings settings = new FlutterLoader.Settings();
- assertFalse(flutterLoader.initialized());
- flutterLoader.startInitialization(ctx, settings);
- flutterLoader.ensureInitializationComplete(ctx, recognizedArg);
- shadowOf(getMainLooper()).idle();
-
- ArgumentCaptor shellArgsCaptor = ArgumentCaptor.forClass(String[].class);
- verify(mockFlutterJNI, times(1))
- .init(
- eq(ctx),
- shellArgsCaptor.capture(),
- anyString(),
- anyString(),
- anyString(),
- anyLong(),
- anyInt());
- List arguments = Arrays.asList(shellArgsCaptor.getValue());
-
- assertTrue(
- "Recognized argument '"
- + recognizedArg[0]
- + "' was not found in the arguments passed to FlutterJNI.init",
- arguments.contains(recognizedArg[0]));
- }
-
- @Test
- public void ifFlagSetViaManifestAndCommandLineThenCommandLineTakesPrecedence() {
- String expectedImpellerArgFromMetadata = "--enable-impeller=true";
- String expectedImpellerArgFromCommandLine = "--enable-impeller=false";
-
- FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
- FlutterLoader flutterLoader = new FlutterLoader(mockFlutterJNI);
- Bundle metadata = new Bundle();
-
- // Place metadata key and value into the metadata bundle used to mock the manifest.
- metadata.putBoolean("io.flutter.embedding.android.EnableImpeller", true);
- ctx.getApplicationInfo().metaData = metadata;
-
- FlutterLoader.Settings settings = new FlutterLoader.Settings();
- assertFalse(flutterLoader.initialized());
- flutterLoader.startInitialization(ctx, settings);
- flutterLoader.ensureInitializationComplete(
- ctx, new String[] {expectedImpellerArgFromCommandLine});
- shadowOf(getMainLooper()).idle();
-
- ArgumentCaptor shellArgsCaptor = ArgumentCaptor.forClass(String[].class);
- verify(mockFlutterJNI, times(1))
- .init(
- eq(ctx),
- shellArgsCaptor.capture(),
- anyString(),
- anyString(),
- anyString(),
- anyLong(),
- anyInt());
- List arguments = Arrays.asList(shellArgsCaptor.getValue());
-
- // Verify that the command line argument takes precedence over the manifest metadata.
- assertTrue(
- arguments.indexOf(expectedImpellerArgFromMetadata)
- < arguments.indexOf(expectedImpellerArgFromCommandLine));
- }
-
- @Test
- public void ifAOTSharedLibraryNameSetViaManifestAndCommandLineThenCommandLineTakesPrecedence()
- throws IOException {
- FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
- FlutterLoader flutterLoader = spy(new FlutterLoader(mockFlutterJNI));
- File internalStorageDir = ctx.getFilesDir();
- Path internalStorageDirAsPathObj = internalStorageDir.toPath();
-
- ctx.getApplicationInfo().nativeLibraryDir =
- Paths.get("some", "path", "doesnt", "matter").toString();
- assertFalse(flutterLoader.initialized());
- flutterLoader.startInitialization(ctx);
-
- // Test paths for library living within internal storage.
- Path pathWithDirectInternalStoragePath1 = internalStorageDirAsPathObj.resolve("library1.so");
- Path pathWithDirectInternalStoragePath2 = internalStorageDirAsPathObj.resolve("library2.so");
-
- String expectedAotSharedLibraryNameFromMetadata =
- "--aot-shared-library-name="
- + pathWithDirectInternalStoragePath1.toFile().getCanonicalPath();
- String expectedAotSharedLibraryNameFromCommandLine =
- "--aot-shared-library-name="
- + pathWithDirectInternalStoragePath2.toFile().getCanonicalPath();
-
- Bundle metadata = new Bundle();
-
- // Place metadata key and value into the metadata bundle used to mock the manifest.
- metadata.putString(
- "io.flutter.embedding.android.AOTSharedLibraryName",
- pathWithDirectInternalStoragePath1.toFile().getCanonicalPath());
- ctx.getApplicationInfo().metaData = metadata;
-
- FlutterLoader.Settings settings = new FlutterLoader.Settings();
- assertFalse(flutterLoader.initialized());
- flutterLoader.startInitialization(ctx, settings);
- flutterLoader.ensureInitializationComplete(
- ctx,
- new String[] {expectedAotSharedLibraryNameFromCommandLine, "--enable-opengl-gpu-tracing"});
- shadowOf(getMainLooper()).idle();
-
- ArgumentCaptor shellArgsCaptor = ArgumentCaptor.forClass(String[].class);
- verify(mockFlutterJNI, times(1))
- .init(
- eq(ctx),
- shellArgsCaptor.capture(),
- anyString(),
- anyString(),
- anyString(),
- anyLong(),
- anyInt());
- List arguments = Arrays.asList(shellArgsCaptor.getValue());
-
- // Verify that the command line argument takes precedence over the manifest metadata.
- assertTrue(
- arguments.indexOf(expectedAotSharedLibraryNameFromCommandLine)
- < arguments.indexOf(expectedAotSharedLibraryNameFromMetadata));
-
- // Verify other command line arguments are still passed through.
- assertTrue(
- "Expected argument --enable-opengl-gpu-tracing was not found in the arguments passed to FlutterJNI.init",
- arguments.contains("--enable-opengl-gpu-tracing"));
- }
-
- private void testFlagFromMetadataPresentInReleaseMode(
- String metadataKey, Object metadataValue, String expectedArg) {
- testFlagFromMetadata(metadataKey, metadataValue, expectedArg, true, true);
- }
-
- private void testFlagFromMetadataNotPresent(
- String metadataKey, Object metadataValue, String expectedArg) {
- testFlagFromMetadata(metadataKey, metadataValue, expectedArg, false, false);
- }
-
- private void testFlagFromMetadataPresent(
- String metadataKey, Object metadataValue, String expectedArg) {
- testFlagFromMetadata(metadataKey, metadataValue, expectedArg, true, false);
- }
-
- // Test that specified shell argument can be set via manifest metadata as expected.
- private void testFlagFromMetadata(
- String metadataKey,
- Object metadataValue,
- String expectedArg,
- boolean shouldBeSet,
- boolean isReleaseMode) {
- FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
- FlutterLoader flutterLoader = new FlutterLoader(mockFlutterJNI);
- Bundle metadata = new Bundle();
-
- // Place metadata key and value into the metadata bundle used to mock the manifest.
- if (metadataValue instanceof Boolean) {
- metadata.putBoolean(metadataKey, (Boolean) metadataValue);
- } else if (metadataValue instanceof Integer) {
- metadata.putInt(metadataKey, (Integer) metadataValue);
- } else if (metadataValue instanceof String) {
- metadata.putString(metadataKey, (String) metadataValue);
- } else {
- throw new IllegalArgumentException(
- "Unsupported metadataValue type: " + metadataValue.getClass());
- }
-
- ctx.getApplicationInfo().metaData = metadata;
-
- FlutterLoader.Settings settings = new FlutterLoader.Settings();
- assertFalse(flutterLoader.initialized());
- flutterLoader.startInitialization(ctx, settings);
- flutterLoader.ensureInitializationComplete(ctx, null, isReleaseMode);
- shadowOf(getMainLooper()).idle();
-
- ArgumentCaptor shellArgsCaptor = ArgumentCaptor.forClass(String[].class);
- verify(mockFlutterJNI, times(1))
- .init(
- eq(ctx),
- shellArgsCaptor.capture(),
- anyString(),
- anyString(),
- anyString(),
- anyLong(),
- anyInt());
- List arguments = Arrays.asList(shellArgsCaptor.getValue());
-
- if (shouldBeSet) {
- assertTrue(
- "Expected argument '"
- + expectedArg
- + "' was not found in the arguments passed to FlutterJNI.init",
- arguments.contains(expectedArg));
- } else {
- assertFalse(
- "Unexpected argument '"
- + expectedArg
- + "' was found in the arguments passed to FlutterJNI.init",
- arguments.contains(expectedArg));
- }
- }
-}
+}
\ No newline at end of file
diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/external/FlutterLaunchTests.java b/engine/src/flutter/shell/platform/android/test/io/flutter/external/FlutterLaunchTests.java
index d10fcc77c26..16b82490175 100644
--- a/engine/src/flutter/shell/platform/android/test/io/flutter/external/FlutterLaunchTests.java
+++ b/engine/src/flutter/shell/platform/android/test/io/flutter/external/FlutterLaunchTests.java
@@ -26,7 +26,7 @@ public class FlutterLaunchTests {
assertEquals("main", flutterActivity.getDartEntrypointFunctionName());
assertEquals("/", flutterActivity.getInitialRoute());
- assertArrayEquals(new String[] {}, flutterActivity.getFlutterShellArgs());
+ assertArrayEquals(new String[] {}, flutterActivity.getFlutterShellArgs().toArray());
assertTrue(flutterActivity.shouldAttachEngineToActivity());
assertNull(flutterActivity.getCachedEngineId());
assertTrue(flutterActivity.shouldDestroyEngineWithHost());
diff --git a/examples/flutter_view/android/app/src/main/java/com/example/view/MainActivity.java b/examples/flutter_view/android/app/src/main/java/com/example/view/MainActivity.java
index 125868b3af6..3c53005f354 100644
--- a/examples/flutter_view/android/app/src/main/java/com/example/view/MainActivity.java
+++ b/examples/flutter_view/android/app/src/main/java/com/example/view/MainActivity.java
@@ -15,14 +15,11 @@ import io.flutter.embedding.android.FlutterView;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.dart.DartExecutor.DartEntrypoint;
-import io.flutter.Log;
import io.flutter.plugin.common.BasicMessageChannel;
import io.flutter.plugin.common.BasicMessageChannel.MessageHandler;
import io.flutter.plugin.common.BasicMessageChannel.Reply;
import io.flutter.plugin.common.StringCodec;
-import java.util.Arrays;
import java.util.ArrayList;
-import java.util.List;
public class MainActivity extends AppCompatActivity {
private static FlutterEngine flutterEngine;
@@ -34,28 +31,34 @@ public class MainActivity extends AppCompatActivity {
private static final String PING = "ping";
private BasicMessageChannel messageChannel;
- // Previously, this example checked for certain flags set via Intent. Engine
- // flags can no longer be set via Intent, so warn developers that Intent extras
- // will be ignored and point to alternative methods for setting engine flags.
- private void warnIfEngineFlagsSetViaIntent(Intent intent) {
- List previouslySupportedFlagsViaIntent = Arrays.asList(
- "trace-startup", "start-paused", "enable-dart-profiling");
- for (String flag : previouslySupportedFlagsViaIntent) {
- if (intent.hasExtra(flag)) {
- Log.w("MainActivity", "Engine flags can no longer be set via Intent on Android. If you wish to set " + flag + ", see https://github.com/flutter/flutter/blob/main/docs/engine/Android-Flutter-Shell-Arguments.md for alternative methods.");
- break;
- }
+ private String[] getArgsFromIntent(Intent intent) {
+ // Before adding more entries to this list, consider that arbitrary
+ // Android applications can generate intents with extra data and that
+ // there are many security-sensitive args in the binary.
+ ArrayList args = new ArrayList<>();
+ if (intent.getBooleanExtra("trace-startup", false)) {
+ args.add("--trace-startup");
}
+ if (intent.getBooleanExtra("start-paused", false)) {
+ args.add("--start-paused");
+ }
+ if (intent.getBooleanExtra("enable-dart-profiling", false)) {
+ args.add("--enable-dart-profiling");
+ }
+ if (!args.isEmpty()) {
+ String[] argsArray = new String[args.size()];
+ return args.toArray(argsArray);
+ }
+ return null;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- warnIfEngineFlagsSetViaIntent(getIntent());
-
+ String[] args = getArgsFromIntent(getIntent());
if (flutterEngine == null) {
- flutterEngine = new FlutterEngine(this);
+ flutterEngine = new FlutterEngine(this, args);
flutterEngine.getDartExecutor().executeDartEntrypoint(
DartEntrypoint.createDefault()
);