Add --create option to `flutter emulators` command (#18235)

* Add --create option to flutter emulators

* Tweaks to error message

* Simplify emulator search logic

* Make name optional

* Add a note about this option being used with --create

* Tweaks to help information

* Switch to processManager for easier testing

* Don't crash on missing files or missing properties in Android Emulator

* Move name suffixing into emulator manager

This allows it to be tested in the EmulatorManager tests and also used by daemon later if desired.

* Pass the context's android SDK through so it can be mocked by tests

* Misc fixes

* Add tests around emulator creation

Process calls are mocked to avoid needing a real SDK (and to be fast). Full integration tests may be useful, but may require ensuring all build environments etc. are set up correctly.

* Simplify avdManagerPath

Previous changes were to emulatorPath!

* Fix lint errors

* Fix incorrect file exgtension for Windows

* Fix an issue where no system images would crash

reduce throws on an empty collection.

* Fix "null" appearing in error messages

The name we attempted to use will now always be returned, even in the case of failure.

* Add additional info to missing-system-image failure message

On Windows after installing Andriod Studio I didn't have any of these and got this message. Installing with sdkmanager fixed the issue.

* Fix thrown errors

runResult had a toString() but we moved to ProcessResult when switching to ProcessManager to this ended up throwing "Instance of ProcessResult".

* Fix package import

* Fix more package imports

* Move mock implementation into Mock class

There seemed to be issues using Lists in args with Mockito that I couldn't figure out (docs say to use typed() but I couldn't make this compile with these lists still)..

* Rename method that's ambigious now we have create

* Handle where there's no avd path

* Add another toList() :(

* Remove comment that was rewritten

* Fix forbidden import

* Make optional arg more obviously optional

* Reformat doc

* Note that we create a pixel device in help text

* Make this a named arg
diff --git a/packages/flutter_tools/lib/src/android/android_sdk.dart b/packages/flutter_tools/lib/src/android/android_sdk.dart
index 79a5d67..2dcc5bf 100644
--- a/packages/flutter_tools/lib/src/android/android_sdk.dart
+++ b/packages/flutter_tools/lib/src/android/android_sdk.dart
@@ -64,16 +64,8 @@
 /// will work for those users who have Android Tools installed but
 /// not the full SDK.
 String getEmulatorPath([AndroidSdk existingSdk]) {
-  if (existingSdk?.emulatorPath != null)
-    return existingSdk.emulatorPath;
-
-  final AndroidSdk sdk = AndroidSdk.locateAndroidSdk();
-
-  if (sdk?.latestVersion == null) {
-    return os.which('emulator')?.path;
-  } else {
-    return sdk.emulatorPath;
-  }
+  return existingSdk?.emulatorPath ??
+    AndroidSdk.locateAndroidSdk()?.emulatorPath;
 }
 
 /// Locate the path for storing AVD emulator images. Returns null if none found.
@@ -104,6 +96,15 @@
   );
 }
 
+/// Locate 'avdmanager'. Prefer to use one from an Android SDK, if we can locate that.
+/// This should be used over accessing androidSdk.avdManagerPath directly because it
+/// will work for those users who have Android Tools installed but
+/// not the full SDK.
+String getAvdManagerPath([AndroidSdk existingSdk]) {
+  return existingSdk?.avdManagerPath ??
+    AndroidSdk.locateAndroidSdk()?.avdManagerPath;
+}
+
 class AndroidNdkSearchError {
   AndroidNdkSearchError(this.reason);
 
@@ -314,6 +315,8 @@
 
   String get emulatorPath => getEmulatorPath();
 
+  String get avdManagerPath => getAvdManagerPath();
+
   /// Validate the Android SDK. This returns an empty list if there are no
   /// issues; otherwise, it returns a list of issues found.
   List<String> validateSdkWellFormed() {
@@ -343,6 +346,14 @@
     return null;
   }
 
+  String getAvdManagerPath() {
+    final String binaryName = platform.isWindows ? 'avdmanager.bat' : 'avdmanager';
+    final String path = fs.path.join(directory, 'tools', 'bin', binaryName);
+    if (fs.file(path).existsSync())
+      return path;
+    return null;
+  }
+
   void _init() {
     Iterable<Directory> platforms = <Directory>[]; // android-22, ...