Re-enable use of instruments for iOS device lookup (#10838)

This reverts commit b2909a245a607995ce7ec286585cd1f643124f57.

This resubmits the following patches:

1. Use Xcode instruments to list devices (#10801)
Eliminates the dependency on idevice_id from libimobiledevice. Instead,
uses Xcode built-in functionality.

2. Make device discovery asynchronous (#10803)
Migrates DeviceDiscovery.devices and all device-specific lookup to be
asynchronous.
diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart
index bb3eca7..1cc5c96 100644
--- a/packages/flutter_tools/lib/src/ios/devices.dart
+++ b/packages/flutter_tools/lib/src/ios/devices.dart
@@ -36,11 +36,11 @@
   bool get canListAnything => iosWorkflow.canListDevices;
 
   @override
-  List<Device> pollingGetDevices() => IOSDevice.getAttachedDevices();
+  Future<List<Device>> pollingGetDevices() => IOSDevice.getAttachedDevices();
 }
 
 class IOSDevice extends Device {
-  IOSDevice(String id, { this.name }) : super(id) {
+  IOSDevice(String id, { this.name, String sdkVersion }) : _sdkVersion = sdkVersion, super(id) {
     _installerPath = _checkForCommand('ideviceinstaller');
     _iproxyPath = _checkForCommand('iproxy');
     _pusherPath = _checkForCommand(
@@ -55,6 +55,8 @@
   String _iproxyPath;
   String _pusherPath;
 
+  final String _sdkVersion;
+
   @override
   bool get supportsHotMode => true;
 
@@ -71,14 +73,30 @@
   @override
   bool get supportsStartPaused => false;
 
-  static List<IOSDevice> getAttachedDevices() {
-    if (!iMobileDevice.isInstalled)
+  // Physical device line format to be matched:
+  // My iPhone (10.3.2) [75b90e947c5f429fa67f3e9169fda0d89f0492f1]
+  //
+  // Other formats in output (desktop, simulator) to be ignored:
+  // my-mac-pro [2C10513E-4dA5-405C-8EF5-C44353DB3ADD]
+  // iPhone 6s (9.3) [F6CEE7CF-81EB-4448-81B4-1755288C7C11] (Simulator)
+  static final RegExp _deviceRegex = new RegExp(r'^(.*) +\((.*)\) +\[(.*)\]$');
+
+  static Future<List<IOSDevice>> getAttachedDevices() async {
+    if (!xcode.isInstalled)
       return <IOSDevice>[];
 
     final List<IOSDevice> devices = <IOSDevice>[];
-    for (String id in iMobileDevice.getAttachedDeviceIDs()) {
-      final String name = iMobileDevice.getInfoForDevice(id, 'DeviceName');
-      devices.add(new IOSDevice(id, name: name));
+    final Iterable<String> deviceLines = (await xcode.getAvailableDevices())
+        .split('\n')
+        .map((String line) => line.trim());
+    for (String line in deviceLines) {
+      final Match match = _deviceRegex.firstMatch(line);
+      if (match != null) {
+        final String deviceName = match.group(1);
+        final String sdkVersion = match.group(2);
+        final String deviceID = match.group(3);
+        devices.add(new IOSDevice(deviceID, name: deviceName, sdkVersion: sdkVersion));
+      }
     }
     return devices;
   }
@@ -311,11 +329,7 @@
   Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
 
   @override
-  Future<String> get sdkNameAndVersion async => 'iOS $_sdkVersion ($_buildVersion)';
-
-  String get _sdkVersion => iMobileDevice.getInfoForDevice(id, 'ProductVersion');
-
-  String get _buildVersion => iMobileDevice.getInfoForDevice(id, 'BuildVersion');
+  Future<String> get sdkNameAndVersion async => 'iOS $_sdkVersion';
 
   @override
   DeviceLogReader getLogReader({ApplicationPackage app}) {
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index 7d1bfa6..1b1317e 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -70,14 +70,6 @@
     return await exitsHappyAsync(<String>['idevicename']);
   }
 
-  List<String> getAttachedDeviceIDs() {
-    return runSync(<String>['idevice_id', '-l'])
-        .trim()
-        .split('\n')
-        .where((String line) => line.isNotEmpty)
-        .toList();
-  }
-
   /// Returns the value associated with the specified `ideviceinfo` key for a device.
   ///
   /// If either the specified key or device does not exist, returns the empty string.
@@ -165,6 +157,13 @@
 
     return _xcodeVersionCheckValid(_xcodeMajorVersion, _xcodeMinorVersion);
   }
+
+  Future<String> getAvailableDevices() async {
+    final RunResult result = await runAsync(<String>['/usr/bin/instruments', '-s', 'devices']);
+    if (result.exitCode != 0)
+      throw new ToolExit('Failed to invoke /usr/bin/instruments. Is Xcode installed?');
+    return result.stdout;
+  }
 }
 
 bool _xcodeVersionCheckValid(int major, int minor) {
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart
index 697be3b..92788c6 100644
--- a/packages/flutter_tools/lib/src/ios/simulators.dart
+++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -37,7 +37,7 @@
   bool get canListAnything => iosWorkflow.canListDevices;
 
   @override
-  List<Device> pollingGetDevices() => IOSSimulatorUtils.instance.getAttachedDevices();
+  Future<List<Device>> pollingGetDevices() async => IOSSimulatorUtils.instance.getAttachedDevices();
 }
 
 class IOSSimulatorUtils {