Fix v1 embedding support heuristic for plugins (#44214)

diff --git a/packages/flutter_tools/lib/src/platform_plugins.dart b/packages/flutter_tools/lib/src/platform_plugins.dart
index cfadd8f..07fc58e 100644
--- a/packages/flutter_tools/lib/src/platform_plugins.dart
+++ b/packages/flutter_tools/lib/src/platform_plugins.dart
@@ -64,17 +64,20 @@
       'name': name,
       'package': package,
       'class': pluginClass,
-      'usesEmbedding2': _embeddingVersion == '2',
+      // Mustache doesn't support complex types.
+      'supportsEmbeddingV1': _supportedEmbedings.contains('1'),
+      'supportsEmbeddingV2': _supportedEmbedings.contains('2'),
     };
   }
 
-  String _cachedEmbeddingVersion;
+  Set<String> _cachedEmbeddingVersion;
 
   /// Returns the version of the Android embedding.
-  String get _embeddingVersion => _cachedEmbeddingVersion ??= _getEmbeddingVersion();
+  Set<String> get _supportedEmbedings => _cachedEmbeddingVersion ??= _getSupportedEmbeddings();
 
-  String _getEmbeddingVersion() {
+  Set<String> _getSupportedEmbeddings() {
     assert(pluginPath != null);
+    final Set<String> supportedEmbeddings = <String>{};
     final String baseMainPath = fs.path.join(
       pluginPath,
       'android',
@@ -113,9 +116,15 @@
     }
     if (mainClassContent
         .contains('io.flutter.embedding.engine.plugins.FlutterPlugin')) {
-      return '2';
+      supportedEmbeddings.add('2');
+    } else {
+      supportedEmbeddings.add('1');
     }
-    return '1';
+    if (mainClassContent.contains('PluginRegistry')
+        && mainClassContent.contains('registerWith')) {
+      supportedEmbeddings.add('1');
+    }
+    return supportedEmbeddings;
   }
 }
 
diff --git a/packages/flutter_tools/lib/src/plugins.dart b/packages/flutter_tools/lib/src/plugins.dart
index 11dda77..613400c 100644
--- a/packages/flutter_tools/lib/src/plugins.dart
+++ b/packages/flutter_tools/lib/src/plugins.dart
@@ -337,12 +337,14 @@
     ShimPluginRegistry shimPluginRegistry = new ShimPluginRegistry(flutterEngine);
 {{/needsShim}}
 {{#plugins}}
-  {{#usesEmbedding2}}
+  {{#supportsEmbeddingV2}}
     flutterEngine.getPlugins().add(new {{package}}.{{class}}());
-  {{/usesEmbedding2}}
-  {{^usesEmbedding2}}
-    {{package}}.{{class}}.registerWith(shimPluginRegistry.registrarFor("{{package}}.{{class}}"));
-  {{/usesEmbedding2}}
+  {{/supportsEmbeddingV2}}
+  {{^supportsEmbeddingV2}}
+    {{#supportsEmbeddingV1}}
+      {{package}}.{{class}}.registerWith(shimPluginRegistry.registrarFor("{{package}}.{{class}}"));
+    {{/supportsEmbeddingV1}}
+  {{/supportsEmbeddingV2}}
 {{/plugins}}
   }
 }
@@ -396,14 +398,24 @@
       // 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']) {
+        if (plugin['supportsEmbeddingV1'] && !plugin['supportsEmbeddingV2']) {
           templateContext['needsShim'] = true;
           break;
         }
       }
       templateContent = _androidPluginRegistryTemplateNewEmbedding;
-    break;
+      break;
+    case AndroidEmbeddingVersion.v1:
     default:
+      for (Map<String, dynamic> plugin in androidPlugins) {
+        if (!plugin['supportsEmbeddingV1'] && plugin['supportsEmbeddingV2']) {
+          throwToolExit(
+            'The plugin `${plugin['name']}` requires your app to be migrated to '
+            'the Android embedding v2. Follow the steps on https://flutter.dev/go/android-project-migration '
+            'and re-run this command.'
+          );
+        }
+      }
       templateContent = _androidPluginRegistryTemplateOldEmbedding;
       break;
   }
diff --git a/packages/flutter_tools/test/general.shard/plugins_test.dart b/packages/flutter_tools/test/general.shard/plugins_test.dart
index fe45f10..ea41d4d 100644
--- a/packages/flutter_tools/test/general.shard/plugins_test.dart
+++ b/packages/flutter_tools/test/general.shard/plugins_test.dart
@@ -265,6 +265,100 @@
         XcodeProjectInterpreter: () => xcodeProjectInterpreter,
       });
 
+      testUsingContext('exits the tool if an app uses the v1 embedding and a plugin only supports the v2 embedding', () async {
+        when(flutterProject.isModule).thenReturn(false);
+        when(androidProject.getEmbeddingVersion()).thenReturn(AndroidEmbeddingVersion.v1);
+
+        final Directory pluginUsingJavaAndNewEmbeddingDir =
+          fs.systemTempDirectory.createTempSync('flutter_plugin_using_java_and_new_embedding_dir.');
+        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;');
+
+        flutterProject.directory
+          .childFile('.packages')
+          .writeAsStringSync('''
+plugin1:${pluginUsingJavaAndNewEmbeddingDir.childDirectory('lib').uri.toString()}
+''');
+        await expectLater(
+          () async {
+            await injectPlugins(flutterProject);
+          },
+          throwsToolExit(
+            message: 'The plugin `plugin1` requires your app to be migrated to the Android embedding v2. '
+                     'Follow the steps on https://flutter.dev/go/android-project-migration and re-run this command.'
+          ),
+        );
+      }, overrides: <Type, Generator>{
+        FileSystem: () => fs,
+        ProcessManager: () => FakeProcessManager.any(),
+        FeatureFlags: () => featureFlags,
+        XcodeProjectInterpreter: () => xcodeProjectInterpreter,
+      });
+
+      testUsingContext('allows app use a plugin that supports v1 and v2 embedding', () async {
+        when(flutterProject.isModule).thenReturn(false);
+        when(androidProject.getEmbeddingVersion()).thenReturn(AndroidEmbeddingVersion.v1);
+
+        final Directory pluginUsingJavaAndNewEmbeddingDir =
+          fs.systemTempDirectory.createTempSync('flutter_plugin_using_java_and_new_embedding_dir.');
+        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;\n'
+            'PluginRegistry\n'
+            'registerWith(Irrelevant registrar)\n'
+          );
+
+        flutterProject.directory
+          .childFile('.packages')
+          .writeAsStringSync('''
+plugin1:${pluginUsingJavaAndNewEmbeddingDir.childDirectory('lib').uri.toString()}
+''');
+        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,
+        ProcessManager: () => FakeProcessManager.any(),
+        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(androidProject.getEmbeddingVersion()).thenReturn(AndroidEmbeddingVersion.v1);