Reland #40810: Re-enable AAR plugins when an AndroidX failure occurred (#41160)

diff --git a/packages/flutter_tools/test/general.shard/android/gradle_test.dart b/packages/flutter_tools/test/general.shard/android/gradle_test.dart
index 1c7c447..d2ff5d6 100644
--- a/packages/flutter_tools/test/general.shard/android/gradle_test.dart
+++ b/packages/flutter_tools/test/general.shard/android/gradle_test.dart
@@ -25,7 +25,6 @@
 
 import '../../src/common.dart';
 import '../../src/context.dart';
-import '../../src/mocks.dart';
 import '../../src/pubspec_schema.dart';
 
 void main() {
@@ -1150,6 +1149,153 @@
     });
   });
 
+  group('isAppUsingAndroidX', () {
+    FileSystem fs;
+
+    setUp(() {
+      fs = MemoryFileSystem();
+    });
+
+    testUsingContext('returns true when the project is using AndroidX', () async {
+      final Directory androidDirectory = fs.systemTempDirectory.createTempSync('android.');
+
+      androidDirectory
+        .childFile('gradle.properties')
+        .writeAsStringSync('android.useAndroidX=true');
+
+      expect(isAppUsingAndroidX(androidDirectory), isTrue);
+
+    }, overrides: <Type, Generator>{
+      FileSystem: () => fs,
+    });
+
+    testUsingContext('returns false when the project is not using AndroidX', () async {
+      final Directory androidDirectory = fs.systemTempDirectory.createTempSync('android.');
+
+      androidDirectory
+        .childFile('gradle.properties')
+        .writeAsStringSync('android.useAndroidX=false');
+
+      expect(isAppUsingAndroidX(androidDirectory), isFalse);
+
+    }, overrides: <Type, Generator>{
+      FileSystem: () => fs,
+    });
+
+    testUsingContext('returns false when gradle.properties does not exist', () async {
+      final Directory androidDirectory = fs.systemTempDirectory.createTempSync('android.');
+
+      expect(isAppUsingAndroidX(androidDirectory), isFalse);
+
+    }, overrides: <Type, Generator>{
+      FileSystem: () => fs,
+    });
+  });
+
+  group('buildPluginsAsAar', () {
+    FileSystem fs;
+    MockProcessManager mockProcessManager;
+    MockAndroidSdk mockAndroidSdk;
+
+    setUp(() {
+      fs = MemoryFileSystem();
+
+      mockProcessManager = MockProcessManager();
+      when(mockProcessManager.run(
+        any,
+        workingDirectory: anyNamed('workingDirectory'),
+        environment: anyNamed('environment'),
+      )).thenAnswer((_) async => ProcessResult(1, 0, '', ''));
+
+      mockAndroidSdk = MockAndroidSdk();
+      when(mockAndroidSdk.directory).thenReturn('irrelevant');
+    });
+
+    testUsingContext('calls gradle', () async {
+      final Directory androidDirectory = fs.directory('android.');
+      androidDirectory.createSync();
+      androidDirectory
+        .childFile('pubspec.yaml')
+        .writeAsStringSync('name: irrelevant');
+
+      final Directory plugin1 = fs.directory('plugin1.');
+      plugin1
+        ..createSync()
+        ..childFile('pubspec.yaml')
+        .writeAsStringSync('''
+name: irrelevant
+flutter:
+  plugin:
+    androidPackage: irrelevant
+''');
+      final Directory plugin2 = fs.directory('plugin2.');
+      plugin2
+        ..createSync()
+        ..childFile('pubspec.yaml')
+        .writeAsStringSync('''
+name: irrelevant
+flutter:
+  plugin:
+    androidPackage: irrelevant
+''');
+
+      androidDirectory
+        .childFile('.flutter-plugins')
+        .writeAsStringSync('''
+plugin1=${plugin1.path}
+plugin2=${plugin2.path}
+''');
+      final Directory buildDirectory = androidDirectory.childDirectory('build');
+      buildDirectory
+        .childDirectory('outputs')
+        .childDirectory('repo')
+        .createSync(recursive: true);
+
+      await buildPluginsAsAar(
+        FlutterProject.fromPath(androidDirectory.path),
+        const AndroidBuildInfo(BuildInfo.release),
+        buildDirectory: buildDirectory.path,
+      );
+
+      final String flutterRoot = fs.path.absolute(Cache.flutterRoot);
+      final String initScript = fs.path.join(flutterRoot, 'packages',
+          'flutter_tools', 'gradle', 'aar_init_script.gradle');
+      verify(mockProcessManager.run(
+        <String>[
+          'gradlew',
+          '-I=$initScript',
+          '-Pflutter-root=$flutterRoot',
+          '-Poutput-dir=${buildDirectory.path}',
+          '-Pis-plugin=true',
+          '-Ptarget-platform=android-arm,android-arm64',
+          'assembleAarRelease',
+        ],
+        environment: anyNamed('environment'),
+        workingDirectory: plugin1.childDirectory('android').path),
+      ).called(1);
+
+      verify(mockProcessManager.run(
+        <String>[
+          'gradlew',
+          '-I=$initScript',
+          '-Pflutter-root=$flutterRoot',
+          '-Poutput-dir=${buildDirectory.path}',
+          '-Pis-plugin=true',
+          '-Ptarget-platform=android-arm,android-arm64',
+          'assembleAarRelease',
+        ],
+        environment: anyNamed('environment'),
+        workingDirectory: plugin2.childDirectory('android').path),
+      ).called(1);
+
+    }, overrides: <Type, Generator>{
+      AndroidSdk: () => mockAndroidSdk,
+      FileSystem: () => fs,
+      GradleUtils: () => FakeGradleUtils(),
+      ProcessManager: () => mockProcessManager,
+    });
+  });
+
   group('gradle build', () {
     MockAndroidSdk mockAndroidSdk;
     MockAndroidStudio mockAndroidStudio;
@@ -1230,11 +1376,13 @@
       fs.currentDirectory = 'path/to/project';
 
       // Let any process start. Assert after.
-      when(mockProcessManager.start(
+      when(mockProcessManager.run(
         any,
         environment: anyNamed('environment'),
         workingDirectory: anyNamed('workingDirectory'))
-      ).thenAnswer((Invocation invocation) => Future<Process>.value(MockProcess()));
+      ).thenAnswer(
+          (_) async => ProcessResult(1, 0, '', ''),
+      );
       fs.directory('build/outputs/repo').createSync(recursive: true);
 
       await buildGradleAar(
@@ -1244,11 +1392,11 @@
         target: '',
       );
 
-      final List<String> actualGradlewCall = verify(mockProcessManager.start(
+      final List<String> actualGradlewCall = verify(mockProcessManager.run(
         captureAny,
         environment: anyNamed('environment'),
         workingDirectory: anyNamed('workingDirectory')),
-      ).captured.single;
+      ).captured.last;
 
       expect(actualGradlewCall, contains('/path/to/project/.android/gradlew'));
       expect(actualGradlewCall, contains('-PlocalEngineOut=out/android_arm'));
@@ -1279,6 +1427,14 @@
   return FakePlatform.fromPlatform(const LocalPlatform())..operatingSystem = name;
 }
 
+class FakeGradleUtils extends GradleUtils {
+  @override
+  Future<String> getExecutable(FlutterProject project) async {
+    return 'gradlew';
+  }
+}
+
+class MockAndroidSdk extends Mock implements AndroidSdk {}
 class MockAndroidStudio extends Mock implements AndroidStudio {}
 class MockDirectory extends Mock implements Directory {}
 class MockFile extends Mock implements File {}