Catch FormatException caused by bad simctl output (#37958)

diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart
index ecf9bf5..2d5db2b 100644
--- a/packages/flutter_tools/lib/src/ios/simulators.dart
+++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -46,11 +46,12 @@
   /// Returns [IOSSimulatorUtils] active in the current app context (i.e. zone).
   static IOSSimulatorUtils get instance => context.get<IOSSimulatorUtils>();
 
-  List<IOSSimulator> getAttachedDevices() {
+  Future<List<IOSSimulator>> getAttachedDevices() async {
     if (!xcode.isInstalledAndMeetsVersionCheck)
       return <IOSSimulator>[];
 
-    return SimControl.instance.getConnectedDevices().map<IOSSimulator>((SimDevice device) {
+    final List<SimDevice> connected = await SimControl.instance.getConnectedDevices();
+    return connected.map<IOSSimulator>((SimDevice device) {
       return IOSSimulator(device.udid, name: device.name, simulatorCategory: device.category);
     }).toList();
   }
@@ -63,7 +64,7 @@
 
   /// Runs `simctl list --json` and returns the JSON of the corresponding
   /// [section].
-  Map<String, dynamic> _list(SimControlListSection section) {
+  Future<Map<String, dynamic>> _list(SimControlListSection section) async {
     // Sample output from `simctl list --json`:
     //
     // {
@@ -83,20 +84,27 @@
 
     final List<String> command = <String>[_xcrunPath, 'simctl', 'list', '--json', section.name];
     printTrace(command.join(' '));
-    final ProcessResult results = processManager.runSync(command);
+    final ProcessResult results = await processManager.run(command);
     if (results.exitCode != 0) {
       printError('Error executing simctl: ${results.exitCode}\n${results.stderr}');
       return <String, Map<String, dynamic>>{};
     }
-
-    return json.decode(results.stdout)[section.name];
+    try {
+      return json.decode(results.stdout)[section.name];
+    } on FormatException {
+      // We failed to parse the simctl output, or it returned junk.
+      // One known message is "Install Started" isn't valid JSON but is
+      // returned sometimes.
+      printError('simctl returned non-JSON response: ${results.stdout}');
+      return <String, dynamic>{};
+    }
   }
 
   /// Returns a list of all available devices, both potential and connected.
-  List<SimDevice> getDevices() {
+  Future<List<SimDevice>> getDevices() async {
     final List<SimDevice> devices = <SimDevice>[];
 
-    final Map<String, dynamic> devicesSection = _list(SimControlListSection.devices);
+    final Map<String, dynamic> devicesSection = await _list(SimControlListSection.devices);
 
     for (String deviceCategory in devicesSection.keys) {
       final List<dynamic> devicesData = devicesSection[deviceCategory];
@@ -109,8 +117,9 @@
   }
 
   /// Returns all the connected simulator devices.
-  List<SimDevice> getConnectedDevices() {
-    return getDevices().where((SimDevice device) => device.isBooted).toList();
+  Future<List<SimDevice>> getConnectedDevices() async {
+    final List<SimDevice> simDevices = await getDevices();
+    return simDevices.where((SimDevice device) => device.isBooted).toList();
   }
 
   Future<bool> isInstalled(String deviceId, String appId) {