[tools] allow explicitly specifying the JDK to use via a new config setting (#128264)

Closes https://github.com/flutter/flutter/issues/106416.

This PR adds a new `flutter config` setting named `jdk-dir`. When set, the tool will use the JDK found at this location for all Java-dependent tool operations such as building Android apps via gradle and running Android SDK tools.
diff --git a/packages/flutter_tools/lib/src/android/android_workflow.dart b/packages/flutter_tools/lib/src/android/android_workflow.dart
index 47d77a6..40b96eb 100644
--- a/packages/flutter_tools/lib/src/android/android_workflow.dart
+++ b/packages/flutter_tools/lib/src/android/android_workflow.dart
@@ -8,7 +8,6 @@
 
 import '../base/common.dart';
 import '../base/context.dart';
-import '../base/file_system.dart';
 import '../base/io.dart';
 import '../base/logger.dart';
 import '../base/platform.dart';
@@ -18,7 +17,6 @@
 import '../doctor_validator.dart';
 import '../features.dart';
 import 'android_sdk.dart';
-import 'android_studio.dart';
 import 'java.dart';
 
 const int kAndroidSdkMinVersion = 29;
@@ -226,29 +224,23 @@
     required Java? java,
     required AndroidSdk? androidSdk,
     required Platform platform,
-    required FileSystem fileSystem,
     required ProcessManager processManager,
     required Logger logger,
-    required AndroidStudio? androidStudio,
     required Stdio stdio,
     required UserMessages userMessages,
   }) : _java = java,
        _androidSdk = androidSdk,
        _platform = platform,
-       _fileSystem = fileSystem,
        _processManager = processManager,
        _logger = logger,
-       _androidStudio = androidStudio,
        _stdio = stdio,
        _userMessages = userMessages,
        super('Android license subvalidator');
 
   final Java? _java;
   final AndroidSdk? _androidSdk;
-  final AndroidStudio? _androidStudio;
   final Stdio _stdio;
   final Platform _platform;
-  final FileSystem _fileSystem;
   final ProcessManager _processManager;
   final Logger _logger;
   final UserMessages _userMessages;
@@ -287,13 +279,8 @@
   }
 
   Future<bool> _checkJavaVersionNoOutput() async {
-    final String? javaBinary = Java.find(
-      logger: _logger,
-      androidStudio: _androidStudio,
-      fileSystem: _fileSystem,
-      platform: _platform,
-      processManager: _processManager,
-    )?.binaryPath;
+    final String? javaBinary = _java?.binaryPath;
+
     if (javaBinary == null) {
       return false;
     }
diff --git a/packages/flutter_tools/lib/src/android/java.dart b/packages/flutter_tools/lib/src/android/java.dart
index 5304dd2..699fcfd 100644
--- a/packages/flutter_tools/lib/src/android/java.dart
+++ b/packages/flutter_tools/lib/src/android/java.dart
@@ -4,6 +4,7 @@
 
 import 'package:process/process.dart';
 
+import '../base/config.dart';
 import '../base/file_system.dart';
 import '../base/logger.dart';
 import '../base/os.dart';
@@ -52,6 +53,7 @@
   ///
   /// Returns null if no java binary could be found.
   static Java? find({
+    required Config config,
     required AndroidStudio? androidStudio,
     required Logger logger,
     required FileSystem fileSystem,
@@ -65,6 +67,7 @@
       processManager: processManager
     );
     final String? home = _findJavaHome(
+      config: config,
       logger: logger,
       androidStudio: androidStudio,
       platform: platform
@@ -181,10 +184,16 @@
 }
 
 String? _findJavaHome({
+  required Config config,
   required Logger logger,
   required AndroidStudio? androidStudio,
   required Platform platform,
 }) {
+  final Object? configured = config.getValue('jdk-dir');
+  if (configured != null) {
+    return configured as String;
+  }
+
   final String? androidStudioJavaPath = androidStudio?.javaPath;
   if (androidStudioJavaPath != null) {
     return androidStudioJavaPath;
diff --git a/packages/flutter_tools/lib/src/commands/config.dart b/packages/flutter_tools/lib/src/commands/config.dart
index 4b47698..ae03cf3 100644
--- a/packages/flutter_tools/lib/src/commands/config.dart
+++ b/packages/flutter_tools/lib/src/commands/config.dart
@@ -4,6 +4,7 @@
 
 import '../../src/android/android_sdk.dart';
 import '../../src/android/android_studio.dart';
+import '../android/java.dart';
 import '../base/common.dart';
 import '../convert.dart';
 import '../features.dart';
@@ -19,7 +20,12 @@
       negatable: false,
       help: 'Clear the saved development certificate choice used to sign apps for iOS device deployment.');
     argParser.addOption('android-sdk', help: 'The Android SDK directory.');
-    argParser.addOption('android-studio-dir', help: 'The Android Studio install directory. If unset, flutter will search for valid installs at well-known locations.');
+    argParser.addOption('android-studio-dir', help: 'The Android Studio installation directory. If unset, flutter will search for valid installations at well-known locations.');
+    argParser.addOption('jdk-dir', help: 'The Java Development Kit (JDK) installation directory. '
+      'If unset, flutter will search for one in the following order:\n'
+      '    1) the JDK bundled with the latest installation of Android Studio,\n'
+      '    2) the JDK found at the directory found in the JAVA_HOME environment variable, and\n'
+      "    3) the directory containing the java binary found in the user's path.");
     argParser.addOption('build-dir', help: 'The relative path to override a projects build directory.',
         valueHelp: 'out/');
     argParser.addFlag('machine',
@@ -101,7 +107,7 @@
 
   @override
   Future<FlutterCommandResult> runCommand() async {
-    final List<String> rest = argResults?.rest ?? <String>[];
+    final List<String> rest = argResults!.rest;
     if (rest.isNotEmpty) {
       throwToolExit(exitCode: 2,
           'error: flutter config: Too many arguments.\n'
@@ -126,7 +132,7 @@
       return FlutterCommandResult.success();
     }
 
-    if (argResults?.wasParsed('analytics') ?? false) {
+    if (argResults!.wasParsed('analytics')) {
       final bool value = boolArg('analytics');
       // The tool sends the analytics event *before* toggling the flag
       // intentionally to be sure that opt-out events are sent correctly.
@@ -146,19 +152,23 @@
       await globals.analytics.setTelemetry(value);
     }
 
-    if (argResults?.wasParsed('android-sdk') ?? false) {
+    if (argResults!.wasParsed('android-sdk')) {
       _updateConfig('android-sdk', stringArg('android-sdk')!);
     }
 
-    if (argResults?.wasParsed('android-studio-dir') ?? false) {
+    if (argResults!.wasParsed('android-studio-dir')) {
       _updateConfig('android-studio-dir', stringArg('android-studio-dir')!);
     }
 
-    if (argResults?.wasParsed('clear-ios-signing-cert') ?? false) {
+    if (argResults!.wasParsed('jdk-dir')) {
+      _updateConfig('jdk-dir', stringArg('jdk-dir')!);
+    }
+
+    if (argResults!.wasParsed('clear-ios-signing-cert')) {
       _updateConfig('ios-signing-cert', '');
     }
 
-    if (argResults?.wasParsed('build-dir') ?? false) {
+    if (argResults!.wasParsed('build-dir')) {
       final String buildDir = stringArg('build-dir')!;
       if (globals.fs.path.isAbsolute(buildDir)) {
         throwToolExit('build-dir should be a relative path');
@@ -171,7 +181,7 @@
       if (configSetting == null) {
         continue;
       }
-      if (argResults?.wasParsed(configSetting) ?? false) {
+      if (argResults!.wasParsed(configSetting)) {
         final bool keyValue = boolArg(configSetting);
         globals.config.setValue(configSetting, keyValue);
         globals.printStatus('Setting "$configSetting" value to "$keyValue".');
@@ -203,6 +213,10 @@
     if (results['android-sdk'] == null && androidSdk != null) {
       results['android-sdk'] = androidSdk.directory.path;
     }
+    final Java? java = globals.java;
+    if (results['jdk-dir'] == null && java != null) {
+      results['jdk-dir'] = java.javaHome;
+    }
 
     globals.printStatus(const JsonEncoder.withIndent('  ').convert(results));
   }
diff --git a/packages/flutter_tools/lib/src/context_runner.dart b/packages/flutter_tools/lib/src/context_runner.dart
index f24fea2..1fae313 100644
--- a/packages/flutter_tools/lib/src/context_runner.dart
+++ b/packages/flutter_tools/lib/src/context_runner.dart
@@ -102,11 +102,9 @@
         platform: globals.platform,
         userMessages: globals.userMessages,
         processManager: globals.processManager,
-        androidStudio: globals.androidStudio,
         java: globals.java,
         androidSdk: globals.androidSdk,
         logger: globals.logger,
-        fileSystem: globals.fs,
         stdio: globals.stdio,
       ),
       AndroidSdk: AndroidSdk.locateAndroidSdk,
@@ -255,6 +253,7 @@
         platform: globals.platform,
       ),
       Java: () => Java.find(
+        config: globals.config,
         androidStudio: globals.androidStudio,
         logger: globals.logger,
         fileSystem: globals.fs,
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/config_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/config_test.dart
index 98f40c4..d3e86de 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/config_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/config_test.dart
@@ -7,6 +7,7 @@
 import 'package:args/command_runner.dart';
 import 'package:flutter_tools/src/android/android_sdk.dart';
 import 'package:flutter_tools/src/android/android_studio.dart';
+import 'package:flutter_tools/src/android/java.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/cache.dart';
@@ -18,9 +19,11 @@
 
 import '../../src/common.dart';
 import '../../src/context.dart';
+import '../../src/fakes.dart';
 import '../../src/test_flutter_command_runner.dart';
 
 void main() {
+  late Java fakeJava;
   late FakeAndroidStudio fakeAndroidStudio;
   late FakeAndroidSdk fakeAndroidSdk;
   late FakeFlutterVersion fakeFlutterVersion;
@@ -31,6 +34,7 @@
   });
 
   setUp(() {
+    fakeJava = FakeJava();
     fakeAndroidStudio = FakeAndroidStudio();
     fakeAndroidSdk = FakeAndroidSdk();
     fakeFlutterVersion = FakeFlutterVersion();
@@ -65,16 +69,15 @@
       final dynamic jsonObject = json.decode(testLogger.statusText);
       expect(jsonObject, const TypeMatcher<Map<String, dynamic>>());
       if (jsonObject is Map<String, dynamic>) {
-        expect(jsonObject.containsKey('android-studio-dir'), true);
-        expect(jsonObject['android-studio-dir'], isNotNull);
-
-        expect(jsonObject.containsKey('android-sdk'), true);
-        expect(jsonObject['android-sdk'], isNotNull);
+        expect(jsonObject['android-studio-dir'], fakeAndroidStudio.directory);
+        expect(jsonObject['android-sdk'], fakeAndroidSdk.directory.path);
+        expect(jsonObject['jdk-dir'], fakeJava.javaHome);
       }
       verifyNoAnalytics();
     }, overrides: <Type, Generator>{
       AndroidStudio: () => fakeAndroidStudio,
       AndroidSdk: () => fakeAndroidSdk,
+      Java: () => fakeJava,
       Usage: () => testUsage,
     });
 
@@ -289,7 +292,10 @@
 
 class FakeAndroidStudio extends Fake implements AndroidStudio, Comparable<AndroidStudio> {
   @override
-  String get directory => 'path/to/android/stdio';
+  String get directory => 'path/to/android/studio';
+
+  @override
+  String? get javaPath => 'path/to/android/studio/jbr';
 }
 
 class FakeAndroidSdk extends Fake implements AndroidSdk {
diff --git a/packages/flutter_tools/test/general.shard/android/android_sdk_test.dart b/packages/flutter_tools/test/general.shard/android/android_sdk_test.dart
index ecf4cb0..b70b343 100644
--- a/packages/flutter_tools/test/general.shard/android/android_sdk_test.dart
+++ b/packages/flutter_tools/test/general.shard/android/android_sdk_test.dart
@@ -5,10 +5,8 @@
 import 'package:file/memory.dart';
 import 'package:flutter_tools/src/android/android_sdk.dart';
 import 'package:flutter_tools/src/android/android_studio.dart';
-import 'package:flutter_tools/src/android/java.dart';
 import 'package:flutter_tools/src/base/config.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
-import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/base/platform.dart';
 import 'package:flutter_tools/src/globals.dart' as globals;
 import 'package:test/fake.dart';
@@ -345,99 +343,6 @@
       Platform: () => FakePlatform(operatingSystem: 'windows'),
       Config: () => config,
     });
-
-    group('findJavaBinary', () {
-      testUsingContext('returns the path of the JDK bundled with Android Studio, if it exists', () {
-        final String androidStudioBundledJdkHome = globals.androidStudio!.javaPath!;
-        final String expectedJavaBinaryPath = globals.fs.path.join(androidStudioBundledJdkHome, 'bin', 'java');
-
-        final String? foundJavaBinaryPath = Java.find(
-          logger: globals.logger,
-          androidStudio: globals.androidStudio,
-          fileSystem: globals.fs,
-          platform: globals.platform,
-          processManager: globals.processManager,
-        )?.binaryPath;
-
-        expect(foundJavaBinaryPath, expectedJavaBinaryPath);
-      }, overrides: <Type, Generator>{
-        FileSystem: () => MemoryFileSystem.test(),
-        ProcessManager: () => FakeProcessManager.any(),
-        Platform: () => FakePlatform(),
-        Config: () => Config,
-        AndroidStudio: () => FakeAndroidStudioWithJdk(),
-      });
-
-      testUsingContext('returns the current value of JAVA_HOME if it is set and the JDK bundled with Android Studio could not be found', () {
-        final String expectedJavaBinaryPath = globals.fs.path.join('java-home-path', 'bin', 'java');
-
-        final String? foundJavaBinaryPath = Java.find(
-          logger: globals.logger,
-          androidStudio: globals.androidStudio,
-          fileSystem: globals.fs,
-          platform: globals.platform,
-          processManager: globals.processManager,
-        )?.binaryPath;
-
-        expect(foundJavaBinaryPath, expectedJavaBinaryPath);
-      }, overrides: <Type, Generator>{
-        FileSystem: () => MemoryFileSystem.test(),
-        ProcessManager: () => FakeProcessManager.empty(),
-        Platform: () => FakePlatform(environment: <String, String>{
-          Java.javaHomeEnvironmentVariable: 'java-home-path',
-        }),
-        Config: () => Config,
-        AndroidStudio: () => FakeAndroidStudioWithoutJdk(),
-      });
-
-      testUsingContext('returns the java binary found on PATH if no other can be found', () {
-        final String? foundJavaBinaryPath = Java.find(
-          logger: globals.logger,
-          androidStudio: globals.androidStudio,
-          fileSystem: globals.fs,
-          platform: globals.platform,
-          processManager: globals.processManager,
-        )?.binaryPath;
-
-        expect(foundJavaBinaryPath, 'java');
-      }, overrides: <Type, Generator>{
-        Logger: () => BufferLogger.test(),
-        FileSystem: () => MemoryFileSystem.test(),
-        ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
-          const FakeCommand(
-            command: <String>['which', 'java'],
-            stdout: 'java',
-          ),
-        ]),
-        Platform: () => FakePlatform(),
-        Config: () => Config,
-        AndroidStudio: () => FakeAndroidStudioWithoutJdk(),
-      });
-
-      testUsingContext('returns null if no java binary could be found', () {
-        final String? foundJavaBinaryPath = Java.find(
-          logger: globals.logger,
-          androidStudio: globals.androidStudio,
-          fileSystem: globals.fs,
-          platform: globals.platform,
-          processManager: globals.processManager,
-        )?.binaryPath;
-
-        expect(foundJavaBinaryPath, null);
-      }, overrides: <Type, Generator>{
-        Logger: () => BufferLogger.test(),
-        FileSystem: () => MemoryFileSystem.test(),
-        ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
-          const FakeCommand(
-            command: <String>['which', 'java'],
-            exitCode: 1,
-          ),
-        ]),
-        Platform: () => FakePlatform(),
-        Config: () => Config,
-        AndroidStudio: () => FakeAndroidStudioWithoutJdk(),
-      });
-    });
   });
 }
 
diff --git a/packages/flutter_tools/test/general.shard/android/android_workflow_test.dart b/packages/flutter_tools/test/general.shard/android/android_workflow_test.dart
index 0ad77a9..d138215 100644
--- a/packages/flutter_tools/test/general.shard/android/android_workflow_test.dart
+++ b/packages/flutter_tools/test/general.shard/android/android_workflow_test.dart
@@ -4,7 +4,6 @@
 
 import 'package:file/memory.dart';
 import 'package:flutter_tools/src/android/android_sdk.dart';
-import 'package:flutter_tools/src/android/android_studio.dart';
 import 'package:flutter_tools/src/android/android_workflow.dart';
 import 'package:flutter_tools/src/android/java.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
@@ -125,13 +124,11 @@
     final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
       java: FakeJava(),
       androidSdk: sdk,
-      fileSystem: fileSystem,
       processManager: processManager,
       platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
       stdio: stdio,
       logger: BufferLogger.test(),
       userMessages: UserMessages(),
-      androidStudio: FakeAndroidStudio(),
     );
     final LicensesAccepted licenseStatus = await licenseValidator.licensesAccepted;
 
@@ -144,13 +141,11 @@
     final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
       java: FakeJava(),
       androidSdk: sdk,
-      fileSystem: fileSystem,
       processManager: processManager,
       platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
       stdio: stdio,
       logger: BufferLogger.test(),
       userMessages: UserMessages(),
-      androidStudio: FakeAndroidStudio(),
     );
     final LicensesAccepted licenseStatus = await licenseValidator.licensesAccepted;
 
@@ -168,13 +163,11 @@
     final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
       java: FakeJava(),
       androidSdk: sdk,
-      fileSystem: fileSystem,
       processManager: processManager,
       platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
       stdio: stdio,
       logger: BufferLogger.test(),
       userMessages: UserMessages(),
-      androidStudio: FakeAndroidStudio(),
     );
     final LicensesAccepted result = await licenseValidator.licensesAccepted;
 
@@ -197,13 +190,11 @@
     final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
       java: FakeJava(),
       androidSdk: sdk,
-      fileSystem: fileSystem,
       processManager: processManager,
       platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
       stdio: stdio,
       logger: BufferLogger.test(),
       userMessages: UserMessages(),
-      androidStudio: FakeAndroidStudio(),
     );
     final LicensesAccepted result = await licenseValidator.licensesAccepted;
 
@@ -226,13 +217,11 @@
     final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
       java: java,
       androidSdk: sdk,
-      fileSystem: fileSystem,
       processManager: processManager,
       platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
       stdio: stdio,
       logger: BufferLogger.test(),
       userMessages: UserMessages(),
-      androidStudio: FakeAndroidStudio(),
     );
     final LicensesAccepted licenseStatus = await licenseValidator.licensesAccepted;
 
@@ -256,13 +245,11 @@
     final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
       java: FakeJava(),
       androidSdk: sdk,
-      fileSystem: fileSystem,
       processManager: processManager,
       platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
       stdio: stdio,
       logger: BufferLogger.test(),
       userMessages: UserMessages(),
-      androidStudio: FakeAndroidStudio(),
     );
     final LicensesAccepted result = await licenseValidator.licensesAccepted;
 
@@ -286,13 +273,11 @@
     final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
       java: FakeJava(),
       androidSdk: sdk,
-      fileSystem: fileSystem,
       processManager: processManager,
       platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
       stdio: stdio,
       logger: BufferLogger.test(),
       userMessages: UserMessages(),
-      androidStudio: FakeAndroidStudio(),
     );
     final LicensesAccepted result = await licenseValidator.licensesAccepted;
 
@@ -312,13 +297,11 @@
     final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
       java: FakeJava(),
       androidSdk: sdk,
-      fileSystem: fileSystem,
       processManager: processManager,
       platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
       stdio: stdio,
       logger: BufferLogger.test(),
       userMessages: UserMessages(),
-      androidStudio: FakeAndroidStudio(),
     );
 
     expect(await licenseValidator.runLicenseManager(), isTrue);
@@ -331,13 +314,11 @@
     final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
       java: FakeJava(),
       androidSdk: sdk,
-      fileSystem: fileSystem,
       processManager: processManager,
       platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
       stdio: stdio,
       logger: BufferLogger.test(),
       userMessages: UserMessages(),
-      androidStudio: FakeAndroidStudio(),
     );
 
     expect(licenseValidator.runLicenseManager(), throwsToolExit());
@@ -360,13 +341,11 @@
     final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
       java: FakeJava(),
       androidSdk: sdk,
-      fileSystem: fileSystem,
       processManager: processManager,
       platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
       stdio: stdio,
       logger: logger,
       userMessages: UserMessages(),
-      androidStudio: FakeAndroidStudio(),
     );
 
     await licenseValidator.runLicenseManager();
@@ -381,13 +360,11 @@
     final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
       java: FakeJava(),
       androidSdk: sdk,
-      fileSystem: fileSystem,
       processManager: processManager,
       platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
       stdio: stdio,
       logger: BufferLogger.test(),
       userMessages: UserMessages(),
-      androidStudio: FakeAndroidStudio(),
     );
 
     expect(licenseValidator.runLicenseManager(), throwsToolExit());
@@ -408,13 +385,11 @@
     final AndroidLicenseValidator licenseValidator = AndroidLicenseValidator(
       java: FakeJava(),
       androidSdk: sdk,
-      fileSystem: fileSystem,
       processManager: processManager,
       platform: FakePlatform(environment: <String, String>{'HOME': '/home/me'}),
       stdio: stdio,
       logger: logger,
       userMessages: UserMessages(),
-      androidStudio: FakeAndroidStudio(),
     );
 
     await expectLater(
@@ -651,11 +626,6 @@
   String get platformName => '';
 }
 
-class FakeAndroidStudio extends Fake implements AndroidStudio {
-  @override
-  String get javaPath => 'java';
-}
-
 class ThrowingStdin<T> extends Fake implements IOSink {
   ThrowingStdin(this.exception);
 
diff --git a/packages/flutter_tools/test/general.shard/android/java_test.dart b/packages/flutter_tools/test/general.shard/android/java_test.dart
index 81a475b..c7ff678 100644
--- a/packages/flutter_tools/test/general.shard/android/java_test.dart
+++ b/packages/flutter_tools/test/general.shard/android/java_test.dart
@@ -5,6 +5,7 @@
 import 'package:file/memory.dart';
 import 'package:flutter_tools/src/android/android_studio.dart';
 import 'package:flutter_tools/src/android/java.dart';
+import 'package:flutter_tools/src/base/config.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/base/os.dart';
@@ -20,12 +21,14 @@
 
 void main() {
 
+  late Config config;
   late Logger logger;
   late FileSystem fs;
   late Platform platform;
   late FakeProcessManager processManager;
 
   setUp(() {
+    config = Config.test();
     logger = BufferLogger.test();
     fs = MemoryFileSystem.test();
     platform = FakePlatform(environment: <String, String>{
@@ -54,6 +57,7 @@
 '''
         ));
         final Java java = Java.find(
+          config: config,
           androidStudio: androidStudio,
           logger: logger,
           fileSystem: fs,
@@ -74,6 +78,7 @@
         final String expectedJavaBinaryPath = fs.path.join(javaHome, 'bin', 'java');
 
         final Java java = Java.find(
+          config: config,
           androidStudio: androidStudio,
           logger: logger,
           fileSystem: fs,
@@ -99,6 +104,7 @@
         );
 
         final Java java = Java.find(
+          config: config,
           androidStudio: androidStudio,
           logger: logger,
           fileSystem: fs,
@@ -119,6 +125,7 @@
           ),
         );
         final Java? java = Java.find(
+          config: config,
           androidStudio: androidStudio,
           logger: logger,
           fileSystem: fs,
@@ -127,6 +134,53 @@
         );
         expect(java, isNull);
       });
+
+      testWithoutContext('finds and prefers JDK found at config item "jdk-dir" if it is set', () {
+        const String configuredJdkPath = '/jdk';
+        config.setValue('jdk-dir', configuredJdkPath);
+
+        processManager.addCommand(
+          const FakeCommand(
+            command: <String>['which', 'java'],
+            stdout: '/fake/which/java/path',
+          ),
+        );
+
+        final _FakeAndroidStudioWithJdk androidStudio = _FakeAndroidStudioWithJdk();
+        final FakePlatform platformWithJavaHome = FakePlatform(
+          environment: <String, String>{
+            'JAVA_HOME': '/old/jdk'
+          },
+        );
+        Java? java = Java.find(
+          config: config,
+          androidStudio: androidStudio,
+          logger: logger,
+          fileSystem: fs,
+          platform: platformWithJavaHome,
+          processManager: processManager,
+        );
+
+        expect(java, isNotNull);
+        expect(java!.javaHome, configuredJdkPath);
+        expect(java.binaryPath, fs.path.join(configuredJdkPath, 'bin', 'java'));
+
+        config.removeValue('jdk-dir');
+
+        java = Java.find(
+          config: config,
+          androidStudio: androidStudio,
+          logger: logger,
+          fileSystem: fs,
+          platform: platformWithJavaHome,
+          processManager: processManager,
+        );
+
+        expect(java, isNotNull);
+        assert(androidStudio.javaPath != configuredJdkPath);
+        expect(java!.javaHome, androidStudio.javaPath);
+        expect(java.binaryPath, fs.path.join(androidStudio.javaPath!, 'bin', 'java'));
+      });
     });
 
     group('getVersionString', () {