Revert "Launch named iOS simulators (#72323)" (#72447)

This reverts commit 84a7a611b0989d2d7e93d6b98aad2f6b68596b9f.
diff --git a/packages/flutter_tools/lib/src/android/android_emulator.dart b/packages/flutter_tools/lib/src/android/android_emulator.dart
index 3b11621..aee624a 100644
--- a/packages/flutter_tools/lib/src/android/android_emulator.dart
+++ b/packages/flutter_tools/lib/src/android/android_emulator.dart
@@ -142,7 +142,7 @@
   Category get category => Category.mobile;
 
   @override
-  String get platformDisplay => PlatformType.android.toString();
+  PlatformType get platformType => PlatformType.android;
 
   String _prop(String name) => _properties != null ? _properties[name] : null;
 
diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart
index f72b135..7b28ea8 100644
--- a/packages/flutter_tools/lib/src/commands/daemon.dart
+++ b/packages/flutter_tools/lib/src/commands/daemon.dart
@@ -933,7 +933,7 @@
     'id': emulator.id,
     'name': emulator.name,
     'category': emulator.category?.toString(),
-    'platformType': emulator.platformDisplay,
+    'platformType': emulator.platformType?.toString(),
   };
 }
 
diff --git a/packages/flutter_tools/lib/src/commands/emulators.dart b/packages/flutter_tools/lib/src/commands/emulators.dart
index 859e8f6..580e322 100644
--- a/packages/flutter_tools/lib/src/commands/emulators.dart
+++ b/packages/flutter_tools/lib/src/commands/emulators.dart
@@ -27,7 +27,7 @@
   final String description = 'List, launch and create emulators.';
 
   @override
-  final List<String> aliases = <String>['emulator', 'simulators', 'simulator'];
+  final List<String> aliases = <String>['emulator'];
 
   @override
   Future<FlutterCommandResult> runCommand() async {
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index 579b70a..019c129 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -614,6 +614,10 @@
   /// Check if the device is supported by Flutter.
   bool isSupported();
 
+  // String meant to be displayed to the user indicating if the device is
+  // supported by Flutter, and, if not, why.
+  String supportMessage() => isSupported() ? 'Supported' : 'Unsupported';
+
   /// The device's platform.
   Future<TargetPlatform> get targetPlatform;
 
diff --git a/packages/flutter_tools/lib/src/emulator.dart b/packages/flutter_tools/lib/src/emulator.dart
index d0c2500..f8600eb 100644
--- a/packages/flutter_tools/lib/src/emulator.dart
+++ b/packages/flutter_tools/lib/src/emulator.dart
@@ -252,7 +252,7 @@
   String get name;
   String get manufacturer;
   Category get category;
-  String get platformDisplay;
+  PlatformType get platformType;
 
   @override
   int get hashCode => id.hashCode;
@@ -283,7 +283,7 @@
           emulator.id ?? '',
           emulator.name ?? '',
           emulator.manufacturer ?? '',
-          emulator.platformDisplay ?? '',
+          emulator.platformType?.toString() ?? '',
         ],
     ];
 
diff --git a/packages/flutter_tools/lib/src/ios/ios_emulators.dart b/packages/flutter_tools/lib/src/ios/ios_emulators.dart
index dcb7b08..f54748b 100644
--- a/packages/flutter_tools/lib/src/ios/ios_emulators.dart
+++ b/packages/flutter_tools/lib/src/ios/ios_emulators.dart
@@ -16,26 +16,17 @@
   bool get canListAnything => globals.iosWorkflow.canListEmulators;
 
   @override
-  Future<List<Emulator>> get emulators async {
-    final List<IOSSimulator> simulators = await globals.iosSimulatorUtils.getAvailableDevices();
-    return simulators.map<Emulator>((IOSSimulator device) {
-      return IOSEmulator(device);
-    }).toList();
-  }
+  Future<List<Emulator>> get emulators async => getEmulators();
 
   @override
   bool get canLaunchAnything => canListAnything;
 }
 
 class IOSEmulator extends Emulator {
-  IOSEmulator(IOSSimulator simulator)
-      : _simulator = simulator,
-        super(simulator.id, true);
-
-  final IOSSimulator _simulator;
+  const IOSEmulator(String id) : super(id, true);
 
   @override
-  String get name => _simulator.name;
+  String get name => 'iOS Simulator';
 
   @override
   String get manufacturer => 'Apple';
@@ -44,20 +35,43 @@
   Category get category => Category.mobile;
 
   @override
-  String get platformDisplay =>
-      // com.apple.CoreSimulator.SimRuntime.iOS-10-3 => iOS-10-3
-      _simulator.simulatorCategory?.split('.')?.last ?? 'ios';
+  PlatformType get platformType => PlatformType.ios;
 
   @override
   Future<void> launch() async {
-    final RunResult launchResult = await globals.processUtils.run(<String>[
-      'open',
-      '-a',
-      globals.xcode.getSimulatorPath(),
-    ]);
-    if (launchResult.exitCode != 0) {
-      globals.printError('$launchResult');
+    Future<bool> launchSimulator(List<String> additionalArgs) async {
+      final List<String> args = <String>[
+        'open',
+        ...additionalArgs,
+        '-a',
+        globals.xcode.getSimulatorPath(),
+      ];
+
+      final RunResult launchResult = await globals.processUtils.run(args);
+      if (launchResult.exitCode != 0) {
+        globals.printError('$launchResult');
+        return false;
+      }
+      return true;
     }
-    return _simulator.boot();
+
+    // First run with `-n` to force a device to boot if there isn't already one
+    if (!await launchSimulator(<String>['-n'])) {
+      return;
+    }
+
+    // Run again to force it to Foreground (using -n doesn't force existing
+    // devices to the foreground)
+    await launchSimulator(<String>[]);
   }
 }
+
+/// Return the list of iOS Simulators (there can only be zero or one).
+List<IOSEmulator> getEmulators() {
+  final String simulatorPath = globals.xcode.getSimulatorPath();
+  if (simulatorPath == null) {
+    return <IOSEmulator>[];
+  }
+
+  return <IOSEmulator>[const IOSEmulator(iosSimulatorId)];
+}
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart
index 7ad881e..5e32615 100644
--- a/packages/flutter_tools/lib/src/ios/simulators.dart
+++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -66,9 +66,7 @@
       return <IOSSimulator>[];
     }
 
-    final List<SimDevice> connected = (await _simControl.getAvailableDevices())
-        .where((SimDevice device) => device.isBooted)
-        .toList();
+    final List<SimDevice> connected = await _simControl.getConnectedDevices();
     return connected.map<IOSSimulator>((SimDevice device) {
       return IOSSimulator(
         device.udid,
@@ -79,26 +77,6 @@
       );
     }).toList();
   }
-
-  Future<List<IOSSimulator>> getAvailableDevices() async {
-    if (!_xcode.isInstalledAndMeetsVersionCheck) {
-      return <IOSSimulator>[];
-    }
-
-    final List<SimDevice> available = await _simControl.getAvailableDevices();
-    return available
-        .map<IOSSimulator>((SimDevice device) {
-          return IOSSimulator(
-            device.udid,
-            name: device.name,
-            simControl: _simControl,
-            simulatorCategory: device.category,
-            xcode: _xcode,
-          );
-        })
-        .where((IOSSimulator simulator) => simulator.isSupported())
-        .toList();
-  }
 }
 
 /// A wrapper around the `simctl` command line tool.
@@ -111,23 +89,6 @@
         _xcode = xcode,
         _processUtils = ProcessUtils(processManager: processManager, logger: logger);
 
-  /// Create a [SimControl] for testing.
-  ///
-  /// Defaults to a buffer logger.
-  @visibleForTesting
-  factory SimControl.test({
-    @required ProcessManager processManager,
-    Logger logger,
-    Xcode xcode,
-  }) {
-    logger ??= BufferLogger.test();
-    return SimControl(
-      logger: logger,
-      xcode: xcode,
-      processManager: processManager,
-    );
-  }
-
   final Logger _logger;
   final ProcessUtils _processUtils;
   final Xcode _xcode;
@@ -199,10 +160,10 @@
     return devices;
   }
 
-  /// Returns all the available simulator devices.
-  Future<List<SimDevice>> getAvailableDevices() async {
+  /// Returns all the connected simulator devices.
+  Future<List<SimDevice>> getConnectedDevices() async {
     final List<SimDevice> simDevices = await getDevices();
-    return simDevices.where((SimDevice device) => device.isAvailable).toList();
+    return simDevices.where((SimDevice device) => device.isBooted).toList();
   }
 
   Future<bool> isInstalled(String deviceId, String appId) {
@@ -273,17 +234,6 @@
     return result;
   }
 
-  Future<RunResult> boot(String deviceId) {
-    return _processUtils.run(
-      <String>[
-        ..._xcode.xcrunCommand(),
-        'simctl',
-        'boot',
-        deviceId,
-      ],
-    );
-  }
-
   Future<void> takeScreenshot(String deviceId, String outputPath) async {
     try {
       await _processUtils.run(
@@ -346,11 +296,7 @@
   final Map<String, dynamic> data;
 
   String get state => data['state']?.toString();
-
-  bool get isAvailable =>
-      data['isAvailable'] == true ||
-      data['availability']?.toString() == '(available)';
-
+  String get availability => data['availability']?.toString();
   String get name => data['name']?.toString();
   String get udid => data['udid']?.toString();
 
@@ -448,6 +394,7 @@
   @override
   bool isSupported() {
     if (!globals.platform.isMacOS) {
+      _supportMessage = 'iOS devices require a Mac host machine.';
       return false;
     }
 
@@ -455,25 +402,21 @@
     // We do not yet support WatchOS or tvOS devices.
     final RegExp blocklist = RegExp(r'Apple (TV|Watch)', caseSensitive: false);
     if (blocklist.hasMatch(name)) {
+      _supportMessage = 'Flutter does not support Apple TV or Apple Watch.';
       return false;
     }
     return true;
   }
 
-  Future<bool> boot() async {
-    final RunResult result = await _simControl.boot(id);
+  String _supportMessage;
 
-    if (result.exitCode == 0) {
-      return true;
-    }
-    // 149 exit code means the device is already booted. Ignore this error.
-    if (result.exitCode == 149) {
-      globals.printTrace('Simulator "$id" already booted.');
-      return true;
+  @override
+  String supportMessage() {
+    if (isSupported()) {
+      return 'Supported';
     }
 
-    globals.logger.printError('$result');
-    return false;
+    return _supportMessage ?? 'Unknown';
   }
 
   @override
diff --git a/packages/flutter_tools/test/general.shard/android/android_emulator_test.dart b/packages/flutter_tools/test/general.shard/android/android_emulator_test.dart
index 0d46c51..82a1b10 100644
--- a/packages/flutter_tools/test/general.shard/android/android_emulator_test.dart
+++ b/packages/flutter_tools/test/general.shard/android/android_emulator_test.dart
@@ -71,7 +71,7 @@
       expect(emulator.name, displayName);
       expect(emulator.manufacturer, manufacturer);
       expect(emulator.category, Category.mobile);
-      expect(emulator.platformDisplay, 'android');
+      expect(emulator.platformType, PlatformType.android);
     });
 
     testWithoutContext('prefers displayname for name', () {
diff --git a/packages/flutter_tools/test/general.shard/emulator_test.dart b/packages/flutter_tools/test/general.shard/emulator_test.dart
index e51a649..89d6208 100644
--- a/packages/flutter_tools/test/general.shard/emulator_test.dart
+++ b/packages/flutter_tools/test/general.shard/emulator_test.dart
@@ -2,13 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'dart:convert';
+
 import 'package:file/memory.dart';
 import 'package:flutter_tools/src/android/android_workflow.dart';
+import 'package:flutter_tools/src/base/io.dart';
 import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/device.dart';
 import 'package:flutter_tools/src/emulator.dart';
 import 'package:flutter_tools/src/ios/ios_emulators.dart';
-import 'package:flutter_tools/src/ios/simulators.dart';
 import 'package:flutter_tools/src/macos/xcode.dart';
 import 'package:mockito/mockito.dart';
 import 'package:process/process.dart';
@@ -43,10 +45,14 @@
 );
 
 void main() {
+  MockProcessManager mockProcessManager;
   MockAndroidSdk mockSdk;
+  MockXcode mockXcode;
 
   setUp(() {
+    mockProcessManager = MockProcessManager();
     mockSdk = MockAndroidSdk();
+    mockXcode = MockXcode();
 
     when(mockSdk.avdManagerPath).thenReturn('avdmanager');
     when(mockSdk.getAvdManagerPath()).thenReturn('avdmanager');
@@ -293,53 +299,24 @@
   });
 
   group('ios_emulators', () {
-    MockXcode mockXcode;
-    FakeProcessManager fakeProcessManager;
-
+    bool didAttemptToRunSimulator = false;
     setUp(() {
-      fakeProcessManager = FakeProcessManager.list(<FakeCommand>[]);
-      mockXcode = MockXcode();
-      when(mockXcode.xcrunCommand()).thenReturn(<String>['xcrun']);
-      when(mockXcode.getSimulatorPath())
-          .thenAnswer((_) => '/fake/simulator.app');
+      when(mockXcode.xcodeSelectPath).thenReturn('/fake/Xcode.app/Contents/Developer');
+      when(mockXcode.getSimulatorPath()).thenAnswer((_) => '/fake/simulator.app');
+      when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
+        final List<String> args = invocation.positionalArguments[0] as List<String>;
+        if (args.length >= 3 && args[0] == 'open' && args[1] == '-a' && args[2] == '/fake/simulator.app') {
+          didAttemptToRunSimulator = true;
+        }
+        return ProcessResult(101, 0, '', '');
+      });
     });
-
     testUsingContext('runs correct launch commands', () async {
-      fakeProcessManager.addCommands(<FakeCommand>[
-        const FakeCommand(command: <String>[
-          'open',
-          '-a',
-          '/fake/simulator.app',
-        ]),
-        const FakeCommand(command: <String>[
-          'xcrun',
-          'simctl',
-          'boot',
-          '1234',
-        ]),
-      ]);
-      final SimControl simControl = SimControl.test(
-        processManager: fakeProcessManager,
-        xcode: mockXcode,
-      );
-
-      final IOSSimulator simulator = IOSSimulator(
-        '1234',
-        name: 'iPhone 12',
-        simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-14-3',
-        simControl: simControl,
-        xcode: mockXcode,
-      );
-      final IOSEmulator emulator = IOSEmulator(simulator);
-
-      expect(emulator.id, '1234');
-      expect(emulator.name, 'iPhone 12');
-      expect(emulator.category, Category.mobile);
-      expect(emulator.platformDisplay, 'iOS-14-3');
+      const Emulator emulator = IOSEmulator('ios');
       await emulator.launch();
-      expect(fakeProcessManager.hasRemainingExpectations, false);
+      expect(didAttemptToRunSimulator, equals(true));
     }, overrides: <Type, Generator>{
-      ProcessManager: () => fakeProcessManager,
+      ProcessManager: () => mockProcessManager,
       Xcode: () => mockXcode,
     });
   });
@@ -370,11 +347,35 @@
   Category get category => Category.mobile;
 
   @override
-  String get platformDisplay => PlatformType.android.toString();
+  PlatformType get platformType => PlatformType.android;
 
   @override
   Future<void> launch() {
     throw UnimplementedError('Not implemented in Mock');
   }
 }
+
+class MockProcessManager extends Mock implements ProcessManager {
+
+  @override
+  ProcessResult runSync(
+    List<dynamic> command, {
+    String workingDirectory,
+    Map<String, String> environment,
+    bool includeParentEnvironment = true,
+    bool runInShell = false,
+    Encoding stdoutEncoding = systemEncoding,
+    Encoding stderrEncoding = systemEncoding,
+  }) {
+    final String program = command[0] as String;
+    final List<String> args = command.sublist(1) as List<String>;
+    switch (program) {
+      case '/usr/bin/xcode-select':
+        throw ProcessException(program, args);
+        break;
+    }
+    throw StateError('Unexpected process call: $command');
+  }
+}
+
 class MockXcode extends Mock implements Xcode {}
diff --git a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart
index 3048f2f..6b921ec 100644
--- a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart
@@ -11,7 +11,6 @@
 import 'package:flutter_tools/src/base/io.dart';
 import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/base/platform.dart';
-import 'package:flutter_tools/src/base/process.dart';
 import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/build_system/build_system.dart';
 import 'package:flutter_tools/src/devfs.dart';
@@ -786,19 +785,12 @@
         "availability" : "(available)",
         "name" : "iPhone 5s",
         "udid" : "TEST-PHONE-UDID"
-      },
-      {
-        "state" : "Shutdown",
-        "isAvailable" : false,
-        "name" : "iPhone 11",
-        "udid" : "TEST-PHONE-UNAVAILABLE-UDID",
-        "availabilityError" : "runtime profile not found"
       }
     ],
     "tvOS 11.4" : [
       {
         "state" : "Shutdown",
-        "isAvailable" : true,
+        "availability" : "(available)",
         "name" : "Apple TV",
         "udid" : "TEST-TV-UDID"
       }
@@ -832,12 +824,11 @@
 
     testWithoutContext('getDevices succeeds', () async {
       final List<SimDevice> devices = await simControl.getDevices();
-      expect(devices.length, 4);
 
       final SimDevice watch = devices[0];
       expect(watch.category, 'watchOS 4.3');
       expect(watch.state, 'Shutdown');
-      expect(watch.isAvailable, true);
+      expect(watch.availability, '(available)');
       expect(watch.name, 'Apple Watch - 38mm');
       expect(watch.udid, 'TEST-WATCH-UDID');
       expect(watch.isBooted, isFalse);
@@ -845,33 +836,20 @@
       final SimDevice phone = devices[1];
       expect(phone.category, 'iOS 11.4');
       expect(phone.state, 'Booted');
-      expect(phone.isAvailable, true);
+      expect(phone.availability, '(available)');
       expect(phone.name, 'iPhone 5s');
       expect(phone.udid, 'TEST-PHONE-UDID');
       expect(phone.isBooted, isTrue);
 
-      final SimDevice unavailablePhone = devices[2];
-      expect(unavailablePhone.category, 'iOS 11.4');
-      expect(unavailablePhone.state, 'Shutdown');
-      expect(unavailablePhone.isAvailable, isFalse);
-      expect(unavailablePhone.name, 'iPhone 11');
-      expect(unavailablePhone.udid, 'TEST-PHONE-UNAVAILABLE-UDID');
-      expect(unavailablePhone.isBooted, isFalse);
-
-      final SimDevice tv = devices[3];
+      final SimDevice tv = devices[2];
       expect(tv.category, 'tvOS 11.4');
       expect(tv.state, 'Shutdown');
-      expect(tv.isAvailable, true);
+      expect(tv.availability, '(available)');
       expect(tv.name, 'Apple TV');
       expect(tv.udid, 'TEST-TV-UDID');
       expect(tv.isBooted, isFalse);
     });
 
-    testWithoutContext('getAvailableDevices succeeds', () async {
-      final List<SimDevice> devices = await simControl.getAvailableDevices();
-      expect(devices.length, 3);
-    });
-
     testWithoutContext('getDevices handles bad simctl output', () async {
       when(mockProcessManager.run(any))
           .thenAnswer((Invocation _) async => ProcessResult(mockPid, 0, 'Install Started', ''));
@@ -927,78 +905,6 @@
         throwsToolExit(message: r'Unable to launch'),
       );
     });
-
-    testWithoutContext('.boot() calls the right command', () async {
-      await simControl.boot(deviceId);
-      verify(mockProcessManager.run(
-        <String>['xcrun', 'simctl', 'boot', deviceId],
-        environment: anyNamed('environment'),
-        workingDirectory: anyNamed('workingDirectory'),
-      ));
-    });
-  });
-
-  group('boot', () {
-    SimControl simControl;
-    MockXcode mockXcode;
-
-    setUp(() {
-      simControl = MockSimControl();
-      mockXcode = MockXcode();
-      when(mockXcode.xcrunCommand()).thenReturn(<String>['xcrun']);
-    });
-
-    testUsingContext('success', () async {
-      final IOSSimulator device = IOSSimulator(
-        'x',
-        name: 'iPhone SE',
-        simulatorCategory: 'iOS 11.2',
-        simControl: simControl,
-        xcode: mockXcode,
-      );
-      when(simControl.boot(any)).thenAnswer((_) async =>
-          RunResult(ProcessResult(0, 0, '', ''), <String>['simctl']));
-
-      expect(await device.boot(), isTrue);
-    }, overrides: <Type, Generator>{
-      FileSystem: () => fileSystem,
-      ProcessManager: () => FakeProcessManager.any(),
-    });
-
-    testUsingContext('already booted', () async {
-      final IOSSimulator device = IOSSimulator(
-        'x',
-        name: 'iPhone SE',
-        simulatorCategory: 'iOS 11.2',
-        simControl: simControl,
-        xcode: mockXcode,
-      );
-      // 149 means the device is already booted.
-      when(simControl.boot(any)).thenAnswer((_) async =>
-          RunResult(ProcessResult(0, 149, '', ''), <String>['simctl']));
-
-      expect(await device.boot(), isTrue);
-    }, overrides: <Type, Generator>{
-      FileSystem: () => fileSystem,
-      ProcessManager: () => FakeProcessManager.any(),
-    });
-
-    testUsingContext('failed', () async {
-      final IOSSimulator device = IOSSimulator(
-        'x',
-        name: 'iPhone SE',
-        simulatorCategory: 'iOS 11.2',
-        simControl: simControl,
-        xcode: mockXcode,
-      );
-      when(simControl.boot(any)).thenAnswer((_) async =>
-          RunResult(ProcessResult(0, 1, '', ''), <String>['simctl']));
-
-      expect(await device.boot(), isFalse);
-    }, overrides: <Type, Generator>{
-      FileSystem: () => fileSystem,
-      ProcessManager: () => FakeProcessManager.any(),
-    });
   });
 
   group('startApp', () {
diff --git a/packages/flutter_tools/test/src/context.dart b/packages/flutter_tools/test/src/context.dart
index 5e89ddf..b1616fe 100644
--- a/packages/flutter_tools/test/src/context.dart
+++ b/packages/flutter_tools/test/src/context.dart
@@ -114,7 +114,6 @@
           IOSSimulatorUtils: () {
             final MockIOSSimulatorUtils mock = MockIOSSimulatorUtils();
             when(mock.getAttachedDevices()).thenAnswer((Invocation _) async => <IOSSimulator>[]);
-            when(mock.getAvailableDevices()).thenAnswer((Invocation _) async => <IOSSimulator>[]);
             return mock;
           },
           OutputPreferences: () => OutputPreferences.test(),
@@ -287,7 +286,7 @@
 
 class MockSimControl extends Mock implements SimControl {
   MockSimControl() {
-    when(getAvailableDevices()).thenAnswer((Invocation _) async => <SimDevice>[]);
+    when(getConnectedDevices()).thenAnswer((Invocation _) async => <SimDevice>[]);
   }
 }