Generate projects using the new Android embedding (#41666)

* Generate projects using the new Android embedding

* Add comment about usesNewEmbedding:true

* Feedback

* Rework way to detect new embedding in new apps
diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart
index ed64c50..71205ff 100644
--- a/packages/flutter_tools/lib/src/android/gradle.dart
+++ b/packages/flutter_tools/lib/src/android/gradle.dart
@@ -940,7 +940,6 @@
 /// Returns [true] if the current app uses AndroidX.
 // TODO(egarciad): https://github.com/flutter/flutter/issues/40800
 // Remove `FlutterManifest.usesAndroidX` and provide a unified `AndroidProject.usesAndroidX`.
-@visibleForTesting
 bool isAppUsingAndroidX(Directory androidDirectory) {
   final File properties = androidDirectory.childFile('gradle.properties');
   if (!properties.existsSync()) {
diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart
index 4c35620..849205a 100644
--- a/packages/flutter_tools/lib/src/commands/create.dart
+++ b/packages/flutter_tools/lib/src/commands/create.dart
@@ -623,6 +623,7 @@
       'description': projectDescription,
       'dartSdk': '$flutterRoot/bin/cache/dart-sdk',
       'androidX': androidX,
+      'useNewAndroidEmbedding': featureFlags.isNewAndroidEmbeddingEnabled,
       'androidMinApiLevel': android.minApiLevel,
       'androidSdkVersion': android_sdk.minimumAndroidSdkVersion,
       'androidFlutterJar': '$flutterRoot/bin/cache/artifacts/engine/android-arm/flutter.jar',
diff --git a/packages/flutter_tools/lib/src/features.dart b/packages/flutter_tools/lib/src/features.dart
index 1701267..b0d979c 100644
--- a/packages/flutter_tools/lib/src/features.dart
+++ b/packages/flutter_tools/lib/src/features.dart
@@ -36,6 +36,9 @@
   /// Whether flutter desktop for Windows is enabled.
   bool get isWindowsEnabled => _isEnabled(flutterWindowsDesktopFeature);
 
+  /// Whether the new Android embedding is enabled.
+  bool get isNewAndroidEmbeddingEnabled => _isEnabled(flutterNewAndroidEmbeddingFeature);
+
   // Calculate whether a particular feature is enabled for the current channel.
   static bool _isEnabled(Feature feature) {
     final String currentChannel = FlutterVersion.instance.channel;
@@ -66,6 +69,7 @@
   flutterMacOSDesktopFeature,
   flutterWindowsDesktopFeature,
   flutterBuildPluginAsAarFeature,
+  flutterNewAndroidEmbeddingFeature,
 ];
 
 /// The [Feature] for flutter web.
@@ -126,6 +130,16 @@
   ),
 );
 
+/// The [Feature] for generating projects using the new Android embedding.
+const Feature flutterNewAndroidEmbeddingFeature = Feature(
+  name: 'flutter create generates projects using the new Android embedding',
+  configSetting: 'enable-new-android-embedding',
+  master: FeatureChannelSetting(
+    available: true,
+    enabledByDefault: false,
+  ),
+);
+
 /// A [Feature] is a process for conditionally enabling tool features.
 ///
 /// All settings are optional, and if not provided will generally default to
diff --git a/packages/flutter_tools/lib/src/platform_plugins.dart b/packages/flutter_tools/lib/src/platform_plugins.dart
index 2b9976b..aba2087 100644
--- a/packages/flutter_tools/lib/src/platform_plugins.dart
+++ b/packages/flutter_tools/lib/src/platform_plugins.dart
@@ -5,6 +5,10 @@
 import 'package:meta/meta.dart';
 import 'package:yaml/yaml.dart';
 
+import 'base/common.dart';
+import 'base/file_system.dart';
+import 'features.dart';
+
 /// Marker interface for all platform specific plugin config impls.
 abstract class PluginPlatform {
   const PluginPlatform();
@@ -17,18 +21,20 @@
 /// The required fields include: [name] of the plugin, [package] of the plugin and
 /// the [pluginClass] that will be the entry point to the plugin's native code.
 class AndroidPlugin extends PluginPlatform {
-  const AndroidPlugin({
+  AndroidPlugin({
     @required this.name,
     @required this.package,
     @required this.pluginClass,
+    @required this.pluginPath,
   });
 
-  factory AndroidPlugin.fromYaml(String name, YamlMap yaml) {
+  factory AndroidPlugin.fromYaml(String name, YamlMap yaml, String pluginPath) {
     assert(validate(yaml));
     return AndroidPlugin(
       name: name,
       package: yaml['package'],
       pluginClass: yaml['pluginClass'],
+      pluginPath: pluginPath,
     );
   }
 
@@ -41,18 +47,80 @@
 
   static const String kConfigKey = 'android';
 
+  /// The plugin name defined in pubspec.yaml.
   final String name;
+
+  /// The plugin package name defined in pubspec.yaml.
   final String package;
+
+  /// The plugin main class defined in pubspec.yaml.
   final String pluginClass;
 
+  /// The absolute path to the plugin in the pub cache.
+  final String pluginPath;
+
   @override
   Map<String, dynamic> toMap() {
     return <String, dynamic>{
       'name': name,
       'package': package,
       'class': pluginClass,
+      'usesEmbedding2': _embeddingVersion == '2',
     };
   }
+
+  String _cachedEmbeddingVersion;
+
+  /// Returns the version of the Android embedding.
+  String get _embeddingVersion => _cachedEmbeddingVersion ??= _getEmbeddingVersion();
+
+  String _getEmbeddingVersion() {
+    if (!featureFlags.isNewAndroidEmbeddingEnabled) {
+      return '1';
+    }
+    assert(pluginPath != null);
+    final String baseMainPath = fs.path.join(
+      pluginPath,
+      'android',
+      'src',
+      'main',
+    );
+    File mainPluginClass = fs.file(
+      fs.path.join(
+        baseMainPath,
+        'java',
+        package.replaceAll('.', fs.path.separator),
+        '$pluginClass.java',
+      )
+    );
+    // Check if the plugin is implemented in Kotlin since the plugin's pubspec.yaml
+    // doesn't include this information.
+    if (!mainPluginClass.existsSync()) {
+      mainPluginClass = fs.file(
+        fs.path.join(
+          baseMainPath,
+          'kotlin',
+          package.replaceAll('.', fs.path.separator),
+          '$pluginClass.kt',
+        )
+      );
+    }
+    assert(mainPluginClass.existsSync());
+    String mainClassContent;
+    try {
+      mainClassContent = mainPluginClass.readAsStringSync();
+    } on FileSystemException {
+      throwToolExit(
+        'Couldn\'t read file $mainPluginClass even though it exists. '
+        'Please verify that this file has read permission and try again.'
+      );
+    }
+    if (mainClassContent
+        .contains('io.flutter.embedding.engine.plugins.FlutterPlugin')) {
+      return '2';
+    }
+    return '1';
+  }
 }
 
 /// Contains the parameters to template an iOS plugin.
diff --git a/packages/flutter_tools/lib/src/plugins.dart b/packages/flutter_tools/lib/src/plugins.dart
index 56ebd0d..997188f 100644
--- a/packages/flutter_tools/lib/src/plugins.dart
+++ b/packages/flutter_tools/lib/src/plugins.dart
@@ -5,8 +5,10 @@
 import 'dart:async';
 
 import 'package:mustache/mustache.dart' as mustache;
+import 'package:xml/xml.dart' as xml;
 import 'package:yaml/yaml.dart';
 
+import 'android/gradle.dart';
 import 'base/common.dart';
 import 'base/file_system.dart';
 import 'dart/package_map.dart';
@@ -63,9 +65,8 @@
     }
     if (pluginYaml != null && pluginYaml['platforms'] != null) {
       return Plugin._fromMultiPlatformYaml(name, path, pluginYaml);
-    } else {
-      return Plugin._fromLegacyYaml(name, path, pluginYaml); // ignore: deprecated_member_use_from_same_package
     }
+    return Plugin._fromLegacyYaml(name, path, pluginYaml); // ignore: deprecated_member_use_from_same_package
   }
 
   factory Plugin._fromMultiPlatformYaml(String name, String path, dynamic pluginYaml) {
@@ -79,8 +80,11 @@
     final Map<String, PluginPlatform> platforms = <String, PluginPlatform>{};
 
     if (platformsYaml[AndroidPlugin.kConfigKey] != null) {
-      platforms[AndroidPlugin.kConfigKey] =
-          AndroidPlugin.fromYaml(name, platformsYaml[AndroidPlugin.kConfigKey]);
+      platforms[AndroidPlugin.kConfigKey] = AndroidPlugin.fromYaml(
+        name,
+        platformsYaml[AndroidPlugin.kConfigKey],
+        path,
+      );
     }
 
     if (platformsYaml[IOSPlugin.kConfigKey] != null) {
@@ -122,12 +126,12 @@
     if (pluginYaml != null && pluginClass != null) {
       final String androidPackage = pluginYaml['androidPackage'];
       if (androidPackage != null) {
-        platforms[AndroidPlugin.kConfigKey] =
-            AndroidPlugin(
-              name: name,
-              package: pluginYaml['androidPackage'],
-              pluginClass: pluginClass,
-            );
+        platforms[AndroidPlugin.kConfigKey] = AndroidPlugin(
+          name: name,
+          package: pluginYaml['androidPackage'],
+          pluginClass: pluginClass,
+          pluginPath: path,
+        );
       }
 
       final String iosPrefix = pluginYaml['iosPrefix'] ?? '';
@@ -221,14 +225,21 @@
   }
   final String packageRootPath = fs.path.fromUri(packageRoot);
   printTrace('Found plugin $name at $packageRootPath');
-  return Plugin.fromYaml(name, packageRootPath, flutterConfig['plugin']);
+  return Plugin.fromYaml(
+    name,
+    packageRootPath,
+    flutterConfig['plugin'],
+  );
 }
 
 List<Plugin> findPlugins(FlutterProject project) {
   final List<Plugin> plugins = <Plugin>[];
   Map<String, Uri> packages;
   try {
-    final String packagesFile = fs.path.join(project.directory.path, PackageMap.globalPackagesPath);
+    final String packagesFile = fs.path.join(
+      project.directory.path,
+      PackageMap.globalPackagesPath,
+    );
     packages = PackageMap(packagesFile).map;
   } on FormatException catch (e) {
     printTrace('Invalid .packages file: $e');
@@ -269,7 +280,7 @@
       : null;
 }
 
-const String _androidPluginRegistryTemplate = '''package io.flutter.plugins;
+const String _androidPluginRegistryTemplateOldEmbedding = '''package io.flutter.plugins;
 
 import io.flutter.plugin.common.PluginRegistry;
 {{#plugins}}
@@ -300,6 +311,41 @@
 }
 ''';
 
+const String _androidPluginRegistryTemplateNewEmbedding = '''package dev.flutter.plugins;
+
+{{#androidX}}
+import androidx.annotation.NonNull;
+{{/androidX}}
+{{^androidX}}
+import android.support.annotation.NonNull;
+{{/androidX}}
+import io.flutter.embedding.engine.FlutterEngine;
+{{#needsShim}}
+import io.flutter.embedding.engine.plugins.shim.ShimPluginRegistry;
+{{/needsShim}}
+
+/**
+ * Generated file. Do not edit.
+ * This file is generated by the Flutter tool based on the
+ * plugins that support the Android platform.
+ */
+public final class GeneratedPluginRegistrant {
+  public static void registerWith(@NonNull FlutterEngine flutterEngine) {
+{{#needsShim}}
+    ShimPluginRegistry shimPluginRegistry = new ShimPluginRegistry(flutterEngine);
+{{/needsShim}}
+{{#plugins}}
+  {{#usesEmbedding2}}
+    flutterEngine.getPlugins().add(new {{package}}.{{class}}());
+  {{/usesEmbedding2}}
+  {{^usesEmbedding2}}
+    {{package}}.{{class}}.registerWith(shimPluginRegistry.registrarFor("{{package}}.{{class}}"));
+  {{/usesEmbedding2}}
+{{/plugins}}
+  }
+}
+''';
+
 List<Map<String, dynamic>> _extractPlatformMaps(List<Plugin> plugins, String type) {
   final List<Map<String, dynamic>> pluginConfigs = <Map<String, dynamic>>[];
   for (Plugin p in plugins) {
@@ -311,26 +357,92 @@
   return pluginConfigs;
 }
 
-Future<void> _writeAndroidPluginRegistrant(FlutterProject project, List<Plugin> plugins) async {
-  final List<Map<String, dynamic>> androidPlugins = _extractPlatformMaps(plugins, AndroidPlugin.kConfigKey);
-  final Map<String, dynamic> context = <String, dynamic>{
-    'plugins': androidPlugins,
-  };
+/// Returns the version of the Android embedding that the current
+/// [project] is using.
+String _getAndroidEmbeddingVersion(FlutterProject project) {
+  if (!featureFlags.isNewAndroidEmbeddingEnabled) {
+    return '1';
+  }
+  assert(project.android != null);
+  final File androidManifest = project.android.appManifestFile;
+  assert(androidManifest.existsSync());
+  xml.XmlDocument document;
+  try {
+    document = xml.parse(androidManifest.readAsStringSync());
+  } on xml.XmlParserException {
+    throwToolExit('Error parsing ${project.android.appManifestFile} '
+                  'Please ensure that the android manifest is a valid XML document and try again.');
+  } on FileSystemException {
+    throwToolExit('Error reading ${project.android.appManifestFile} even though it exists. '
+                  'Please ensure that you have read permission to this file and try again.');
+  }
+  for (xml.XmlElement metaData in document.findAllElements('meta-data')) {
+    final String name = metaData.getAttribute('android:name');
+    if (name == 'flutterEmbedding') {
+      return metaData.getAttribute('android:value');
+    }
+  }
+  return '1';
+}
 
+Future<void> _writeAndroidPluginRegistrant(FlutterProject project, List<Plugin> plugins) async {
+  final List<Map<String, dynamic>> androidPlugins =
+    _extractPlatformMaps(plugins, AndroidPlugin.kConfigKey);
+
+  final Map<String, dynamic> templateContext = <String, dynamic>{
+    'plugins': androidPlugins,
+    'androidX': isAppUsingAndroidX(project.android.hostAppGradleRoot),
+  };
   final String javaSourcePath = fs.path.join(
     project.android.pluginRegistrantHost.path,
     'src',
     'main',
     'java',
   );
-  final String registryPath = fs.path.join(
-    javaSourcePath,
-    'io',
-    'flutter',
-    'plugins',
-    'GeneratedPluginRegistrant.java',
+
+  String registryPath;
+  String templateContent;
+
+  final String appEmbeddingVersion = _getAndroidEmbeddingVersion(project);
+  switch (appEmbeddingVersion) {
+    case '2':
+      templateContext['needsShim'] = false;
+      // If a plugin is using an embedding version older than 2.0 and the app is using 2.0,
+      // then add  shim for the old plugins.
+      for (Map<String, dynamic> plugin in androidPlugins) {
+        if (!plugin['usesEmbedding2']) {
+          templateContext['needsShim'] = true;
+          break;
+        }
+      }
+      registryPath = fs.path.join(
+        javaSourcePath,
+        'dev',
+        'flutter',
+        'plugins',
+        'GeneratedPluginRegistrant.java',
+      );
+      templateContent = _androidPluginRegistryTemplateNewEmbedding;
+    break;
+    case '1':
+      registryPath = fs.path.join(
+        javaSourcePath,
+        'io',
+        'flutter',
+        'plugins',
+        'GeneratedPluginRegistrant.java',
+      );
+      templateContent = _androidPluginRegistryTemplateOldEmbedding;
+    break;
+    default:
+      throwToolExit('Unsupported Android embedding');
+  }
+  printTrace('Generating $registryPath');
+  _renderTemplateToFile(
+    templateContent,
+    templateContext,
+    registryPath,
   );
-  _renderTemplateToFile(_androidPluginRegistryTemplate, context, registryPath);
 }
 
 const String _objcPluginRegistryHeaderTemplate = '''//
diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart
index dd5ce15..3b7ad27 100644
--- a/packages/flutter_tools/lib/src/project.dart
+++ b/packages/flutter_tools/lib/src/project.dart
@@ -608,7 +608,11 @@
 
   void _regenerateLibrary() {
     _deleteIfExistsSync(ephemeralDirectory);
-    _overwriteFromTemplate(fs.path.join('module', 'android', 'library'), ephemeralDirectory);
+    _overwriteFromTemplate(fs.path.join(
+      'module',
+      'android',
+      featureFlags.isNewAndroidEmbeddingEnabled ? 'library_new_embedding' : 'library',
+    ), ephemeralDirectory);
     _overwriteFromTemplate(fs.path.join('module', 'android', 'gradle'), ephemeralDirectory);
     gradle.injectGradleWrapperIfNeeded(ephemeralDirectory);
   }
@@ -621,6 +625,7 @@
         'projectName': parent.manifest.appName,
         'androidIdentifier': parent.manifest.androidPackage,
         'androidX': usesAndroidX,
+        'useNewAndroidEmbedding': featureFlags.isNewAndroidEmbeddingEnabled,
       },
       printStatusWhenWriting: false,
       overwriteExisting: true,
diff --git a/packages/flutter_tools/templates/app/android-java.tmpl/app/src/main/java/androidIdentifier/MainActivity.java.tmpl b/packages/flutter_tools/templates/app/android-java.tmpl/app/src/main/java/androidIdentifier/MainActivity.java.tmpl
index 226d9ff..6506de1 100644
--- a/packages/flutter_tools/templates/app/android-java.tmpl/app/src/main/java/androidIdentifier/MainActivity.java.tmpl
+++ b/packages/flutter_tools/templates/app/android-java.tmpl/app/src/main/java/androidIdentifier/MainActivity.java.tmpl
@@ -1,5 +1,24 @@
 package {{androidIdentifier}};
 
+{{#useNewAndroidEmbedding}}
+{{#androidX}}
+import androidx.annotation.NonNull;
+{{/androidX}}
+{{^androidX}}
+import android.support.annotation.NonNull;
+{{/androidX}}
+import dev.flutter.plugins.GeneratedPluginRegistrant;
+import io.flutter.embedding.android.FlutterActivity;
+import io.flutter.embedding.engine.FlutterEngine;
+
+public class MainActivity extends FlutterActivity {
+  @Override
+  public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
+    GeneratedPluginRegistrant.registerWith(flutterEngine);
+  }
+}
+{{/useNewAndroidEmbedding}}
+{{^useNewAndroidEmbedding}}
 import android.os.Bundle;
 import io.flutter.app.FlutterActivity;
 import io.flutter.plugins.GeneratedPluginRegistrant;
@@ -11,3 +30,4 @@
     GeneratedPluginRegistrant.registerWith(this);
   }
 }
+{{/useNewAndroidEmbedding}}
diff --git a/packages/flutter_tools/templates/app/android-kotlin.tmpl/app/src/main/kotlin/androidIdentifier/MainActivity.kt.tmpl b/packages/flutter_tools/templates/app/android-kotlin.tmpl/app/src/main/kotlin/androidIdentifier/MainActivity.kt.tmpl
index ec654f4..fe04735 100644
--- a/packages/flutter_tools/templates/app/android-kotlin.tmpl/app/src/main/kotlin/androidIdentifier/MainActivity.kt.tmpl
+++ b/packages/flutter_tools/templates/app/android-kotlin.tmpl/app/src/main/kotlin/androidIdentifier/MainActivity.kt.tmpl
@@ -1,7 +1,24 @@
 package {{androidIdentifier}}
 
-import android.os.Bundle
+{{#useNewAndroidEmbedding}}
+{{#androidX}}
+import androidx.annotation.NonNull;
+{{/androidX}}
+{{^androidX}}
+import android.support.annotation.NonNull;
+{{/androidX}}
+import dev.flutter.plugins.GeneratedPluginRegistrant
+import io.flutter.embedding.android.FlutterActivity
+import io.flutter.embedding.engine.FlutterEngine
 
+class MainActivity: FlutterActivity() {
+  override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
+    GeneratedPluginRegistrant.registerWith(flutterEngine);
+  }
+}
+{{/useNewAndroidEmbedding}}
+{{^useNewAndroidEmbedding}}
+import android.os.Bundle
 import io.flutter.app.FlutterActivity
 import io.flutter.plugins.GeneratedPluginRegistrant
 
@@ -11,3 +28,4 @@
     GeneratedPluginRegistrant.registerWith(this)
   }
 }
+{{/useNewAndroidEmbedding}}
diff --git a/packages/flutter_tools/templates/app/android.tmpl/app/src/main/AndroidManifest.xml.tmpl b/packages/flutter_tools/templates/app/android.tmpl/app/src/main/AndroidManifest.xml.tmpl
index 4778a20..bc1305e 100644
--- a/packages/flutter_tools/templates/app/android.tmpl/app/src/main/AndroidManifest.xml.tmpl
+++ b/packages/flutter_tools/templates/app/android.tmpl/app/src/main/AndroidManifest.xml.tmpl
@@ -1,6 +1,5 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="{{androidIdentifier}}">
-
     <!-- io.flutter.app.FlutterApplication is an android.app.Application that
          calls FlutterMain.startInitialization(this); in its onCreate method.
          In most cases you can leave this as-is, but you if you want to provide
@@ -29,5 +28,12 @@
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
+        {{#useNewAndroidEmbedding}}
+        <!-- Don't delete the meta-data below.
+             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
+        <meta-data
+            android:name="flutterEmbedding"
+            android:value="2" />
+        {{/useNewAndroidEmbedding}}
     </application>
 </manifest>
diff --git a/packages/flutter_tools/templates/module/android/host_app_common/app.tmpl/src/main/AndroidManifest.xml.tmpl b/packages/flutter_tools/templates/module/android/host_app_common/app.tmpl/src/main/AndroidManifest.xml.tmpl
index a78b20a..3ef4d25 100644
--- a/packages/flutter_tools/templates/module/android/host_app_common/app.tmpl/src/main/AndroidManifest.xml.tmpl
+++ b/packages/flutter_tools/templates/module/android/host_app_common/app.tmpl/src/main/AndroidManifest.xml.tmpl
@@ -36,5 +36,12 @@
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
+        {{#useNewAndroidEmbedding}}
+        <!-- Don't delete the meta-data below.
+             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
+        <meta-data
+            android:name="flutterEmbedding"
+            android:value="2" />
+        {{/useNewAndroidEmbedding}}
     </application>
 </manifest>
diff --git a/packages/flutter_tools/templates/module/android/host_app_common/app.tmpl/src/main/java/androidIdentifier/host/MainActivity.java.tmpl b/packages/flutter_tools/templates/module/android/host_app_common/app.tmpl/src/main/java/androidIdentifier/host/MainActivity.java.tmpl
index 3441c3a..bc7daff 100644
--- a/packages/flutter_tools/templates/module/android/host_app_common/app.tmpl/src/main/java/androidIdentifier/host/MainActivity.java.tmpl
+++ b/packages/flutter_tools/templates/module/android/host_app_common/app.tmpl/src/main/java/androidIdentifier/host/MainActivity.java.tmpl
@@ -1,6 +1,25 @@
 package {{androidIdentifier}}.host;
 
 import android.os.Bundle;
+{{#useNewAndroidEmbedding}}
+{{#androidX}}
+import androidx.annotation.NonNull;
+{{/androidX}}
+{{^androidX}}
+import android.support.annotation.NonNull;
+{{/androidX}}
+import dev.flutter.plugins.GeneratedPluginRegistrant;
+import io.flutter.embedding.android.FlutterActivity;
+import io.flutter.embedding.engine.FlutterEngine;
+
+public class MainActivity extends FlutterActivity {
+  @Override
+  public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
+    GeneratedPluginRegistrant.registerWith(flutterEngine);
+  }
+}
+{{/useNewAndroidEmbedding}}
+{{^useNewAndroidEmbedding}}
 import io.flutter.app.FlutterActivity;
 import io.flutter.plugins.GeneratedPluginRegistrant;
 
@@ -11,3 +30,4 @@
     GeneratedPluginRegistrant.registerWith(this);
   }
 }
+{{/useNewAndroidEmbedding}}
diff --git a/packages/flutter_tools/templates/module/android/library_new_embedding/Flutter.tmpl/build.gradle.tmpl b/packages/flutter_tools/templates/module/android/library_new_embedding/Flutter.tmpl/build.gradle.tmpl
new file mode 100644
index 0000000..0e9b8fd
--- /dev/null
+++ b/packages/flutter_tools/templates/module/android/library_new_embedding/Flutter.tmpl/build.gradle.tmpl
@@ -0,0 +1,55 @@
+// Generated file. Do not edit.
+
+def localProperties = new Properties()
+def localPropertiesFile = new File(buildscript.sourceFile.parentFile.parentFile, 'local.properties')
+if (localPropertiesFile.exists()) {
+    localPropertiesFile.withReader('UTF-8') { reader ->
+        localProperties.load(reader)
+    }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+    flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+    flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.library'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+group '{{androidIdentifier}}'
+version '1.0'
+
+android {
+    compileSdkVersion 28
+
+    defaultConfig {
+        minSdkVersion 16
+        targetSdkVersion 28
+        versionCode flutterVersionCode.toInteger()
+        versionName flutterVersionName
+        {{#androidX}}
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+        {{/androidX}}
+        {{^androidX}}
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        {{/androidX}}
+    }
+}
+
+flutter {
+    source '../..'
+}
+
+dependencies {
+    testImplementation 'junit:junit:4.12'
+}
diff --git a/packages/flutter_tools/templates/module/android/library_new_embedding/Flutter.tmpl/flutter.iml.copy.tmpl b/packages/flutter_tools/templates/module/android/library_new_embedding/Flutter.tmpl/flutter.iml.copy.tmpl
new file mode 100644
index 0000000..29411bd
--- /dev/null
+++ b/packages/flutter_tools/templates/module/android/library_new_embedding/Flutter.tmpl/flutter.iml.copy.tmpl
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.id=":flutter" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/../../.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="android-gradle" name="Android-Gradle">
+      <configuration>
+        <option name="GRADLE_PROJECT_PATH" value=":flutter" />
+      </configuration>
+    </facet>
+    <facet type="android" name="Android">
+      <configuration>
+        <option name="ALLOW_USER_CONFIGURATION" value="false" />
+        <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
+      </configuration>
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
+    <output url="file://$MODULE_DIR$/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes" />
+    <output-test url="file://$MODULE_DIR$/build/intermediates/javac/debugUnitTest/compileDebugUnitTestJavaWithJavac/classes" />
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+    </content>
+    <orderEntry type="jdk" jdkName="Android API 28 Platform" jdkType="Android SDK" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/packages/flutter_tools/templates/module/android/library_new_embedding/Flutter.tmpl/src/main/AndroidManifest.xml.tmpl b/packages/flutter_tools/templates/module/android/library_new_embedding/Flutter.tmpl/src/main/AndroidManifest.xml.tmpl
new file mode 100644
index 0000000..7e9f4f0
--- /dev/null
+++ b/packages/flutter_tools/templates/module/android/library_new_embedding/Flutter.tmpl/src/main/AndroidManifest.xml.tmpl
@@ -0,0 +1,16 @@
+<!-- Generated file. Do not edit. -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="{{androidIdentifier}}"
+    xmlns:tools="http://schemas.android.com/tools">
+  <uses-permission android:name="android.permission.INTERNET"/>
+  <application tools:node="merge">
+    <meta-data
+      android:name="flutterProjectType"
+      android:value="module" />
+      <!-- Don't delete the meta-data below.
+           It is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
+      <meta-data
+          android:name="flutterEmbedding"
+          android:value="2" />
+  </application>
+</manifest>
diff --git a/packages/flutter_tools/templates/module/android/library_new_embedding/include_flutter.groovy.copy.tmpl b/packages/flutter_tools/templates/module/android/library_new_embedding/include_flutter.groovy.copy.tmpl
new file mode 100644
index 0000000..7be7efb
--- /dev/null
+++ b/packages/flutter_tools/templates/module/android/library_new_embedding/include_flutter.groovy.copy.tmpl
@@ -0,0 +1,48 @@
+// Generated file. Do not edit.
+
+def scriptFile = getClass().protectionDomain.codeSource.location.toURI()
+def flutterProjectRoot = new File(scriptFile).parentFile.parentFile
+
+gradle.include ':flutter'
+gradle.project(':flutter').projectDir = new File(flutterProjectRoot, '.android/Flutter')
+
+if (System.getProperty('build-plugins-as-aars') != 'true') {
+    def plugins = new Properties()
+    def pluginsFile = new File(flutterProjectRoot, '.flutter-plugins')
+    if (pluginsFile.exists()) {
+        pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
+    }
+
+    plugins.each { name, path ->
+        def pluginDirectory = flutterProjectRoot.toPath().resolve(path).resolve('android').toFile()
+        gradle.include ":$name"
+        gradle.project(":$name").projectDir = pluginDirectory
+    }
+}
+gradle.getGradle().projectsLoaded { g ->
+    g.rootProject.beforeEvaluate { p ->
+        _mainModuleName = binding.variables['mainModuleName']
+        if (_mainModuleName != null && !_mainModuleName.empty) {
+            p.ext.mainModuleName = _mainModuleName
+        }
+        def subprojects = []
+        def flutterProject
+        p.subprojects { sp ->
+            if (sp.name == 'flutter') {
+                flutterProject = sp
+            } else {
+                subprojects.add(sp)
+            }
+        }
+        assert flutterProject != null
+        flutterProject.ext.hostProjects = subprojects
+        flutterProject.ext.pluginBuildDir = new File(flutterProjectRoot, 'build/host')
+    }
+    g.rootProject.afterEvaluate { p ->
+        p.subprojects { sp ->
+            if (sp.name != 'flutter') {
+                sp.evaluationDependsOn(':flutter')
+            }
+        }
+    }
+}
diff --git a/packages/flutter_tools/templates/module/android/library_new_embedding/settings.gradle.copy.tmpl b/packages/flutter_tools/templates/module/android/library_new_embedding/settings.gradle.copy.tmpl
new file mode 100644
index 0000000..22b1cdd
--- /dev/null
+++ b/packages/flutter_tools/templates/module/android/library_new_embedding/settings.gradle.copy.tmpl
@@ -0,0 +1,5 @@
+// Generated file. Do not edit.
+
+rootProject.name = 'android_generated'
+setBinding(new Binding([gradle: this]))
+evaluate(new File(settingsDir, 'include_flutter.groovy'))
diff --git a/packages/flutter_tools/templates/plugin/android-java.tmpl/src/main/java/androidIdentifier/pluginClass.java.tmpl b/packages/flutter_tools/templates/plugin/android-java.tmpl/src/main/java/androidIdentifier/pluginClass.java.tmpl
index 01bb87f..f6c9d52 100644
--- a/packages/flutter_tools/templates/plugin/android-java.tmpl/src/main/java/androidIdentifier/pluginClass.java.tmpl
+++ b/packages/flutter_tools/templates/plugin/android-java.tmpl/src/main/java/androidIdentifier/pluginClass.java.tmpl
@@ -1,5 +1,37 @@
 package {{androidIdentifier}};
 
+{{#useNewAndroidEmbedding}}
+{{#androidX}}
+import androidx.annotation.NonNull;
+{{/androidX}}
+{{^androidX}}
+import android.support.annotation.NonNull;
+{{/androidX}}
+import io.flutter.embedding.engine.plugins.FlutterPlugin;
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
+import io.flutter.plugin.common.MethodChannel.Result;
+
+/** {{pluginClass}} */
+public class {{pluginClass}} implements FlutterPlugin, MethodCallHandler {
+  @Override
+  public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
+    final MethodChannel channel = new MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "{{projectName}}");
+    channel.setMethodCallHandler(new {{pluginClass}}());
+  }
+
+  @Override
+  public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
+    if (call.method.equals("getPlatformVersion")) {
+      result.success("Android " + android.os.Build.VERSION.RELEASE);
+    } else {
+      result.notImplemented();
+    }
+  }
+}
+{{/useNewAndroidEmbedding}}
+{{^useNewAndroidEmbedding}}
 import io.flutter.plugin.common.MethodCall;
 import io.flutter.plugin.common.MethodChannel;
 import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
@@ -23,3 +55,4 @@
     }
   }
 }
+{{/useNewAndroidEmbedding}}
diff --git a/packages/flutter_tools/templates/plugin/android-kotlin.tmpl/src/main/kotlin/androidIdentifier/pluginClass.kt.tmpl b/packages/flutter_tools/templates/plugin/android-kotlin.tmpl/src/main/kotlin/androidIdentifier/pluginClass.kt.tmpl
index 8fb848f..af45d9c 100644
--- a/packages/flutter_tools/templates/plugin/android-kotlin.tmpl/src/main/kotlin/androidIdentifier/pluginClass.kt.tmpl
+++ b/packages/flutter_tools/templates/plugin/android-kotlin.tmpl/src/main/kotlin/androidIdentifier/pluginClass.kt.tmpl
@@ -1,5 +1,35 @@
 package {{androidIdentifier}}
 
+{{#useNewAndroidEmbedding}}
+{{#androidX}}
+import androidx.annotation.NonNull;
+{{/androidX}}
+{{^androidX}}
+import android.support.annotation.NonNull;
+{{/androidX}}
+import io.flutter.embedding.engine.plugins.FlutterPlugin
+import io.flutter.plugin.common.MethodCall
+import io.flutter.plugin.common.MethodChannel
+import io.flutter.plugin.common.MethodChannel.MethodCallHandler
+import io.flutter.plugin.common.MethodChannel.Result
+
+/** {{pluginClass}} */
+public class {{pluginClass}}: FlutterPlugin, MethodCallHandler {
+  override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPluginBinding) {
+    val channel = MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "{{projectName}}")
+    channel.setMethodCallHandler({{pluginClass}}());
+  }
+
+  override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
+    if (call.method == "getPlatformVersion") {
+      result.success("Android ${android.os.Build.VERSION.RELEASE}")
+    } else {
+      result.notImplemented()
+    }
+  }
+}
+{{/useNewAndroidEmbedding}}
+{{^useNewAndroidEmbedding}}
 import io.flutter.plugin.common.MethodCall
 import io.flutter.plugin.common.MethodChannel
 import io.flutter.plugin.common.MethodChannel.MethodCallHandler
@@ -23,3 +53,4 @@
     }
   }
 }
+{{/useNewAndroidEmbedding}}
diff --git a/packages/flutter_tools/test/general.shard/plugins_test.dart b/packages/flutter_tools/test/general.shard/plugins_test.dart
index f119fc8..1528828 100644
--- a/packages/flutter_tools/test/general.shard/plugins_test.dart
+++ b/packages/flutter_tools/test/general.shard/plugins_test.dart
@@ -5,22 +5,22 @@
 import 'package:file/file.dart';
 import 'package:file/memory.dart';
 import 'package:flutter_tools/src/dart/package_map.dart';
+import 'package:flutter_tools/src/features.dart';
+import 'package:flutter_tools/src/ios/xcodeproj.dart';
 import 'package:flutter_tools/src/plugins.dart';
 import 'package:flutter_tools/src/project.dart';
+
 import 'package:mockito/mockito.dart';
 
 import '../src/common.dart';
 import '../src/context.dart';
 
-class MockFlutterProject extends Mock implements FlutterProject {}
-class MockIosProject extends Mock implements IosProject {}
-class MockMacOSProject extends Mock implements MacOSProject {}
-
 void main() {
   FileSystem fs;
   MockFlutterProject flutterProject;
   MockIosProject iosProject;
   MockMacOSProject macosProject;
+  MockAndroidProject androidProject;
   File packagesFile;
   Directory dummyPackageDirectory;
 
@@ -33,10 +33,17 @@
     when(flutterProject.flutterPluginsFile).thenReturn(flutterProject.directory.childFile('.plugins'));
     iosProject = MockIosProject();
     when(flutterProject.ios).thenReturn(iosProject);
+    when(iosProject.pluginRegistrantHost).thenReturn(flutterProject.directory.childDirectory('Runner'));
+    when(iosProject.podfile).thenReturn(flutterProject.directory.childDirectory('ios').childFile('Podfile'));
     when(iosProject.podManifestLock).thenReturn(flutterProject.directory.childDirectory('ios').childFile('Podfile.lock'));
     macosProject = MockMacOSProject();
     when(flutterProject.macos).thenReturn(macosProject);
+    when(macosProject.podfile).thenReturn(flutterProject.directory.childDirectory('macos').childFile('Podfile'));
     when(macosProject.podManifestLock).thenReturn(flutterProject.directory.childDirectory('macos').childFile('Podfile.lock'));
+    androidProject = MockAndroidProject();
+    when(flutterProject.android).thenReturn(androidProject);
+    when(androidProject.pluginRegistrantHost).thenReturn(flutterProject.directory.childDirectory('android').childDirectory('app'));
+    when(androidProject.hostAppGradleRoot).thenReturn(flutterProject.directory.childDirectory('android'));
 
     // Set up a simple .packages file for all the tests to use, pointing to one package.
     dummyPackageDirectory = fs.directory('/pubcache/apackage/lib/');
@@ -103,4 +110,276 @@
       FileSystem: () => fs,
     });
   });
+
+  group('injectPlugins', () {
+    MockFeatureFlags featureFlags;
+    MockXcodeProjectInterpreter xcodeProjectInterpreter;
+
+    const String kAndroidManifestUsingOldEmbedding = '''
+<manifest>
+    <application>
+    </application>
+</manifest>
+''';
+    const String kAndroidManifestUsingNewEmbedding = '''
+<manifest>
+    <application>
+        <meta-data
+            android:name="flutterEmbedding"
+            android:value="2" />
+    </application>
+</manifest>
+''';
+
+    setUp(() {
+      featureFlags = MockFeatureFlags();
+      when(featureFlags.isLinuxEnabled).thenReturn(false);
+      when(featureFlags.isMacOSEnabled).thenReturn(false);
+      when(featureFlags.isWindowsEnabled).thenReturn(false);
+      when(featureFlags.isWebEnabled).thenReturn(false);
+
+      xcodeProjectInterpreter = MockXcodeProjectInterpreter();
+      when(xcodeProjectInterpreter.isInstalled).thenReturn(false);
+    });
+
+    testUsingContext('Registrant uses old embedding in app project', () async {
+      when(flutterProject.isModule).thenReturn(false);
+      when(featureFlags.isNewAndroidEmbeddingEnabled).thenReturn(false);
+
+      await injectPlugins(flutterProject);
+
+      final File registrant = flutterProject.directory
+        .childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'io', 'flutter', 'plugins'))
+        .childFile('GeneratedPluginRegistrant.java');
+
+      expect(registrant.existsSync(), isTrue);
+      expect(registrant.readAsStringSync(), contains('package io.flutter.plugins'));
+      expect(registrant.readAsStringSync(), contains('class GeneratedPluginRegistrant'));
+    }, overrides: <Type, Generator>{
+      FileSystem: () => fs,
+      FeatureFlags: () => featureFlags,
+    });
+
+    testUsingContext('Registrant uses new embedding if app uses new embedding', () async {
+      when(flutterProject.isModule).thenReturn(false);
+      when(featureFlags.isNewAndroidEmbeddingEnabled).thenReturn(true);
+
+      final File androidManifest = flutterProject.directory
+        .childDirectory('android')
+        .childFile('AndroidManifest.xml')
+        ..createSync(recursive: true)
+        ..writeAsStringSync(kAndroidManifestUsingNewEmbedding);
+      when(androidProject.appManifestFile).thenReturn(androidManifest);
+
+      await injectPlugins(flutterProject);
+
+      final File registrant = flutterProject.directory
+        .childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'dev', 'flutter', 'plugins'))
+        .childFile('GeneratedPluginRegistrant.java');
+
+      expect(registrant.existsSync(), isTrue);
+      expect(registrant.readAsStringSync(), contains('package dev.flutter.plugins'));
+      expect(registrant.readAsStringSync(), contains('class GeneratedPluginRegistrant'));
+    }, overrides: <Type, Generator>{
+      FileSystem: () => fs,
+      FeatureFlags: () => featureFlags,
+    });
+
+    testUsingContext('Registrant uses shim for plugins using old embedding if app uses new embedding', () async {
+      when(flutterProject.isModule).thenReturn(false);
+      when(featureFlags.isNewAndroidEmbeddingEnabled).thenReturn(true);
+
+      final File androidManifest = flutterProject.directory
+        .childDirectory('android')
+        .childFile('AndroidManifest.xml')
+        ..createSync(recursive: true)
+        ..writeAsStringSync(kAndroidManifestUsingNewEmbedding);
+      when(androidProject.appManifestFile).thenReturn(androidManifest);
+
+      final Directory pluginUsingJavaAndNewEmbeddingDir =
+        fs.systemTempDirectory.createTempSync('pluginUsingJavaAndNewEmbeddingDir.');
+      pluginUsingJavaAndNewEmbeddingDir
+        .childFile('pubspec.yaml')
+        .writeAsStringSync('''
+flutter:
+  plugin:
+    androidPackage: plugin1
+    pluginClass: UseNewEmbedding
+''');
+      pluginUsingJavaAndNewEmbeddingDir
+        .childDirectory('android')
+        .childDirectory('src')
+        .childDirectory('main')
+        .childDirectory('java')
+        .childDirectory('plugin1')
+        .childFile('UseNewEmbedding.java')
+        ..createSync(recursive: true)
+        ..writeAsStringSync('import io.flutter.embedding.engine.plugins.FlutterPlugin;');
+
+      final Directory pluginUsingKotlinAndNewEmbeddingDir =
+        fs.systemTempDirectory.createTempSync('pluginUsingKotlinAndNewEmbeddingDir.');
+      pluginUsingKotlinAndNewEmbeddingDir
+        .childFile('pubspec.yaml')
+        .writeAsStringSync('''
+flutter:
+  plugin:
+    androidPackage: plugin2
+    pluginClass: UseNewEmbedding
+''');
+      pluginUsingKotlinAndNewEmbeddingDir
+        .childDirectory('android')
+        .childDirectory('src')
+        .childDirectory('main')
+        .childDirectory('kotlin')
+        .childDirectory('plugin2')
+        .childFile('UseNewEmbedding.kt')
+        ..createSync(recursive: true)
+        ..writeAsStringSync('import io.flutter.embedding.engine.plugins.FlutterPlugin');
+
+      final Directory pluginUsingOldEmbeddingDir =
+        fs.systemTempDirectory.createTempSync('pluginUsingOldEmbeddingDir.');
+      pluginUsingOldEmbeddingDir
+        .childFile('pubspec.yaml')
+        .writeAsStringSync('''
+flutter:
+  plugin:
+    androidPackage: plugin3
+    pluginClass: UseOldEmbedding
+''');
+      pluginUsingOldEmbeddingDir
+        .childDirectory('android')
+        .childDirectory('src')
+        .childDirectory('main')
+        .childDirectory('java')
+        .childDirectory('plugin3')
+        .childFile('UseOldEmbedding.java')
+        ..createSync(recursive: true);
+
+      flutterProject.directory
+        .childFile('.packages')
+        .writeAsStringSync('''
+plugin1:${pluginUsingJavaAndNewEmbeddingDir.childDirectory('lib').uri.toString()}
+plugin2:${pluginUsingKotlinAndNewEmbeddingDir.childDirectory('lib').uri.toString()}
+plugin3:${pluginUsingOldEmbeddingDir.childDirectory('lib').uri.toString()}
+''');
+
+      await injectPlugins(flutterProject);
+
+      final File registrant = flutterProject.directory
+        .childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'dev', 'flutter', 'plugins'))
+        .childFile('GeneratedPluginRegistrant.java');
+
+      expect(registrant.readAsStringSync(),
+        contains('flutterEngine.getPlugins().add(new plugin1.UseNewEmbedding());'));
+      expect(registrant.readAsStringSync(),
+        contains('flutterEngine.getPlugins().add(new plugin2.UseNewEmbedding());'));
+      expect(registrant.readAsStringSync(),
+        contains('plugin3.UseOldEmbedding.registerWith(shimPluginRegistry.registrarFor("plugin3.UseOldEmbedding"));'));
+
+    }, overrides: <Type, Generator>{
+      FileSystem: () => fs,
+      FeatureFlags: () => featureFlags,
+      XcodeProjectInterpreter: () => xcodeProjectInterpreter,
+    });
+
+    testUsingContext('Registrant doesn\'t use new embedding if app doesn\'t use new embedding', () async {
+      when(flutterProject.isModule).thenReturn(false);
+      when(featureFlags.isNewAndroidEmbeddingEnabled).thenReturn(true);
+
+      final File androidManifest = flutterProject.directory
+        .childDirectory('android')
+        .childFile('AndroidManifest.xml')
+        ..createSync(recursive: true)
+        ..writeAsStringSync(kAndroidManifestUsingOldEmbedding);
+      when(androidProject.appManifestFile).thenReturn(androidManifest);
+
+      await injectPlugins(flutterProject);
+
+      final File registrant = flutterProject.directory
+        .childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'io', 'flutter', 'plugins'))
+        .childFile('GeneratedPluginRegistrant.java');
+
+      expect(registrant.existsSync(), isTrue);
+      expect(registrant.readAsStringSync(), contains('package io.flutter.plugins'));
+      expect(registrant.readAsStringSync(), contains('class GeneratedPluginRegistrant'));
+    }, overrides: <Type, Generator>{
+      FileSystem: () => fs,
+      FeatureFlags: () => featureFlags,
+    });
+
+    testUsingContext('Registrant uses old embedding in module project', () async {
+      when(flutterProject.isModule).thenReturn(true);
+      when(featureFlags.isNewAndroidEmbeddingEnabled).thenReturn(false);
+
+      await injectPlugins(flutterProject);
+
+      final File registrant = flutterProject.directory
+        .childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'io', 'flutter', 'plugins'))
+        .childFile('GeneratedPluginRegistrant.java');
+
+      expect(registrant.existsSync(), isTrue);
+      expect(registrant.readAsStringSync(), contains('package io.flutter.plugins'));
+      expect(registrant.readAsStringSync(), contains('class GeneratedPluginRegistrant'));
+    }, overrides: <Type, Generator>{
+      FileSystem: () => fs,
+      FeatureFlags: () => featureFlags,
+    });
+
+    testUsingContext('Registrant uses new embedding if module uses new embedding', () async {
+      when(flutterProject.isModule).thenReturn(true);
+      when(featureFlags.isNewAndroidEmbeddingEnabled).thenReturn(true);
+
+      final File androidManifest = flutterProject.directory
+        .childDirectory('android')
+        .childFile('AndroidManifest.xml')
+        ..createSync(recursive: true)
+        ..writeAsStringSync(kAndroidManifestUsingNewEmbedding);
+      when(androidProject.appManifestFile).thenReturn(androidManifest);
+
+      await injectPlugins(flutterProject);
+
+      final File registrant = flutterProject.directory
+        .childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'dev', 'flutter', 'plugins'))
+        .childFile('GeneratedPluginRegistrant.java');
+
+      expect(registrant.existsSync(), isTrue);
+      expect(registrant.readAsStringSync(), contains('package dev.flutter.plugins'));
+      expect(registrant.readAsStringSync(), contains('class GeneratedPluginRegistrant'));
+    }, overrides: <Type, Generator>{
+      FileSystem: () => fs,
+      FeatureFlags: () => featureFlags,
+    });
+
+    testUsingContext('Registrant doesn\'t use new embedding if module doesn\'t use new embedding', () async {
+      when(flutterProject.isModule).thenReturn(true);
+      when(featureFlags.isNewAndroidEmbeddingEnabled).thenReturn(true);
+
+      final File androidManifest = flutterProject.directory
+        .childDirectory('android')
+        .childFile('AndroidManifest.xml')
+        ..createSync(recursive: true)
+        ..writeAsStringSync(kAndroidManifestUsingOldEmbedding);
+      when(androidProject.appManifestFile).thenReturn(androidManifest);
+
+      await injectPlugins(flutterProject);
+
+      final File registrant = flutterProject.directory
+        .childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'io', 'flutter', 'plugins'))
+        .childFile('GeneratedPluginRegistrant.java');
+
+      expect(registrant.existsSync(), isTrue);
+      expect(registrant.readAsStringSync(), contains('package io.flutter.plugins'));
+      expect(registrant.readAsStringSync(), contains('class GeneratedPluginRegistrant'));
+    }, overrides: <Type, Generator>{
+      FileSystem: () => fs,
+      FeatureFlags: () => featureFlags,
+    });
+  });
 }
+
+class MockAndroidProject extends Mock implements AndroidProject {}
+class MockFeatureFlags extends Mock implements FeatureFlags {}
+class MockFlutterProject extends Mock implements FlutterProject {}
+class MockIosProject extends Mock implements IosProject {}
+class MockMacOSProject extends Mock implements MacOSProject {}
+class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {}
diff --git a/packages/flutter_tools/test/src/testbed.dart b/packages/flutter_tools/test/src/testbed.dart
index 33a27df..4c2fae1 100644
--- a/packages/flutter_tools/test/src/testbed.dart
+++ b/packages/flutter_tools/test/src/testbed.dart
@@ -689,6 +689,7 @@
     this.isMacOSEnabled = false,
     this.isWebEnabled = false,
     this.isWindowsEnabled = false,
+    this.isNewAndroidEmbeddingEnabled = false,
 });
 
   @override
@@ -702,4 +703,7 @@
 
   @override
   final bool isWindowsEnabled;
+
+  @override
+  final bool isNewAndroidEmbeddingEnabled;
 }