show device list in flutter doctor output (#5697)

* show device list in flutter doctor output
fixes https://github.com/flutter/flutter/issues/5625
diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart
index a5b587f..ddcd53f 100644
--- a/packages/flutter_tools/lib/executable.dart
+++ b/packages/flutter_tools/lib/executable.dart
@@ -116,26 +116,26 @@
         // Print the stack trace on the bots - don't write a crash report.
         stderr.writeln('$error');
         stderr.writeln(chain.terse.toString());
+        _exit(1);
       } else {
         if (error is String)
           stderr.writeln('Oops; flutter has exited unexpectedly: "$error".');
         else
           stderr.writeln('Oops; flutter has exited unexpectedly.');
 
-        File file = _createCrashReport(args, error, chain);
-
-        stderr.writeln(
-          'Crash report written to ${file.path};\n'
-          'please let us know at https://github.com/flutter/flutter/issues.'
-        );
+        _createCrashReport(args, error, chain).then((File file) {
+          stderr.writeln(
+              'Crash report written to ${file.path};\n'
+              'please let us know at https://github.com/flutter/flutter/issues.'
+          );
+          _exit(1);
+        });
       }
-
-      _exit(1);
     }
   });
 }
 
-File _createCrashReport(List<String> args, dynamic error, Chain chain) {
+Future<File> _createCrashReport(List<String> args, dynamic error, Chain chain) async {
   File crashFile = getUniqueFile(Directory.current, 'flutter', 'log');
 
   StringBuffer buffer = new StringBuffer();
@@ -150,21 +150,21 @@
   buffer.writeln('```\n${chain.terse}```\n');
 
   buffer.writeln('## flutter doctor\n');
-  buffer.writeln('```\n${_doctorText()}```');
+  buffer.writeln('```\n${await _doctorText()}```');
 
   crashFile.writeAsStringSync(buffer.toString());
 
   return crashFile;
 }
 
-String _doctorText() {
+Future<String> _doctorText() async {
   try {
     BufferLogger logger = new BufferLogger();
     AppContext appContext = new AppContext();
 
     appContext[Logger] = logger;
 
-    appContext.runInZone(() => doctor.diagnose());
+    await appContext.runInZone(() => doctor.diagnose());
 
     return logger.statusText;
   } catch (error, trace) {
diff --git a/packages/flutter_tools/lib/src/android/android_workflow.dart b/packages/flutter_tools/lib/src/android/android_workflow.dart
index b849b25..472f6c4 100644
--- a/packages/flutter_tools/lib/src/android/android_workflow.dart
+++ b/packages/flutter_tools/lib/src/android/android_workflow.dart
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'dart:async';
 import 'dart:io';
 
 import '../base/os.dart';
@@ -22,7 +23,7 @@
   bool get canLaunchDevices => androidSdk != null && androidSdk.validateSdkWellFormed().isEmpty;
 
   @override
-  ValidationResult validate() {
+  Future<ValidationResult> validate() async {
     List<ValidationMessage> messages = <ValidationMessage>[];
     ValidationType type = ValidationType.missing;
     String sdkVersionText;
diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart
index b6bb2ec..f0a0aaf 100644
--- a/packages/flutter_tools/lib/src/commands/create.dart
+++ b/packages/flutter_tools/lib/src/commands/create.dart
@@ -117,7 +117,7 @@
     // Run doctor; tell the user the next steps.
     if (doctor.canLaunchAnything) {
       // Let them know a summary of the state of their tooling.
-      doctor.summary();
+      await doctor.summary();
 
       printStatus('''
 All done! In order to run your application, type:
@@ -131,7 +131,7 @@
       printStatus('');
 
       // Give the user more detailed analysis.
-      doctor.diagnose();
+      await doctor.diagnose();
       printStatus('');
       printStatus("After installing components, run 'flutter doctor' in order to "
         "re-validate your setup.");
diff --git a/packages/flutter_tools/lib/src/commands/doctor.dart b/packages/flutter_tools/lib/src/commands/doctor.dart
index 50b10d8..affd6c4 100644
--- a/packages/flutter_tools/lib/src/commands/doctor.dart
+++ b/packages/flutter_tools/lib/src/commands/doctor.dart
@@ -19,7 +19,7 @@
 
   @override
   Future<int> runInProject() async {
-    doctor.diagnose();
+    await doctor.diagnose();
     return 0;
   }
 }
diff --git a/packages/flutter_tools/lib/src/commands/setup.dart b/packages/flutter_tools/lib/src/commands/setup.dart
index f39d0a2..7aa8ac9 100644
--- a/packages/flutter_tools/lib/src/commands/setup.dart
+++ b/packages/flutter_tools/lib/src/commands/setup.dart
@@ -88,7 +88,7 @@
 
     // run doctor
     printStatus('\nFlutter doctor:');
-    bool goodInstall = doctor.diagnose();
+    bool goodInstall = await doctor.diagnose();
 
     // Validate that flutter is available on the path.
     if (os.which('flutter') == null) {
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index e78b19c..c9d454e 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -236,7 +236,7 @@
   @override
   String toString() => name;
 
-  static void printDevices(List<Device> devices) {
+  static Iterable<String> descriptions(List<Device> devices) {
     int nameWidth = 0;
     int idWidth = 0;
 
@@ -245,12 +245,16 @@
       idWidth = math.max(idWidth, device.id.length);
     }
 
-    for (Device device in devices) {
+    return devices.map((Device device) {
       String supportIndicator = device.isSupported() ? '' : ' (unsupported)';
-      printStatus('${device.name.padRight(nameWidth)} • '
-        '${device.id.padRight(idWidth)} • '
-        '${getNameForTargetPlatform(device.platform)}$supportIndicator');
-    }
+      return '${device.name.padRight(nameWidth)} • '
+             '${device.id.padRight(idWidth)} • '
+             '${getNameForTargetPlatform(device.platform)}$supportIndicator';
+    });
+  }
+
+  static void printDevices(List<Device> devices) {
+    descriptions(devices).forEach((String msg) => printStatus(msg));
   }
 }
 
diff --git a/packages/flutter_tools/lib/src/doctor.dart b/packages/flutter_tools/lib/src/doctor.dart
index 2ea6ac3..3f351b0 100644
--- a/packages/flutter_tools/lib/src/doctor.dart
+++ b/packages/flutter_tools/lib/src/doctor.dart
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'dart:async';
 import 'dart:convert' show JSON;
 import 'dart:io';
 
@@ -10,6 +11,7 @@
 import 'android/android_workflow.dart';
 import 'base/context.dart';
 import 'base/os.dart';
+import 'device.dart';
 import 'globals.dart';
 import 'ios/ios_workflow.dart';
 import 'version.dart';
@@ -38,6 +40,7 @@
       _validators.add(_iosWorkflow);
 
     _validators.add(new AtomValidator());
+    _validators.add(new DeviceValidator());
   }
 
   static void initGlobal() {
@@ -59,15 +62,17 @@
   }
 
   /// Print a summary of the state of the tooling, as well as how to get more info.
-  void summary() => printStatus(summaryText);
+  Future<Null> summary() async {
+    printStatus(await summaryText);
+  }
 
-  String get summaryText {
+  Future<String> get summaryText async {
     StringBuffer buffer = new StringBuffer();
 
     bool allGood = true;
 
     for (DoctorValidator validator in _validators) {
-      ValidationResult result = validator.validate();
+      ValidationResult result = await validator.validate();
       buffer.write('${result.leadingBox} ${validator.title} is ');
       if (result.type == ValidationType.missing)
         buffer.write('not installed.');
@@ -94,7 +99,7 @@
   }
 
   /// Print verbose information about the state of installed tooling.
-  bool diagnose() {
+  Future<bool> diagnose() async {
     bool firstLine = true;
     bool doctorResult = true;
 
@@ -103,7 +108,7 @@
         printStatus('');
       firstLine = false;
 
-      ValidationResult result = validator.validate();
+      ValidationResult result = await validator.validate();
 
       if (result.type == ValidationType.missing)
         doctorResult = false;
@@ -155,7 +160,7 @@
 
   final String title;
 
-  ValidationResult validate();
+  Future<ValidationResult> validate();
 }
 
 class ValidationResult {
@@ -193,7 +198,7 @@
   _FlutterValidator() : super('Flutter');
 
   @override
-  ValidationResult validate() {
+  Future<ValidationResult> validate() async {
     List<ValidationMessage> messages = <ValidationMessage>[];
     ValidationType valid = ValidationType.installed;
 
@@ -239,7 +244,7 @@
   }
 
   @override
-  ValidationResult validate() {
+  Future<ValidationResult> validate() async {
     List<ValidationMessage> messages = <ValidationMessage>[];
 
     int installCount = 0;
@@ -291,3 +296,20 @@
     return FileSystemEntity.isDirectorySync(packagePath);
   }
 }
+
+class DeviceValidator extends DoctorValidator {
+  DeviceValidator() : super('Connected devices');
+
+  @override
+  Future<ValidationResult> validate() async {
+    List<Device> devices = await deviceManager.getAllConnectedDevices();
+    List<ValidationMessage> messages;
+    if (devices.isEmpty) {
+      messages = <ValidationMessage>[new ValidationMessage('None')];
+    } else {
+      messages = Device.descriptions(devices)
+          .map((String msg) => new ValidationMessage(msg)).toList();
+    }
+    return new ValidationResult(ValidationType.installed, messages);
+  }
+}
diff --git a/packages/flutter_tools/lib/src/ios/ios_workflow.dart b/packages/flutter_tools/lib/src/ios/ios_workflow.dart
index 337026a..9a0c8f5 100644
--- a/packages/flutter_tools/lib/src/ios/ios_workflow.dart
+++ b/packages/flutter_tools/lib/src/ios/ios_workflow.dart
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'dart:async';
 import 'dart:io';
 
 import '../base/os.dart';
@@ -29,7 +30,7 @@
   bool get hasIDeviceId => exitsHappy(<String>['idevice_id', '-h']);
 
   @override
-  ValidationResult validate() {
+  Future<ValidationResult> validate() async {
     List<ValidationMessage> messages = <ValidationMessage>[];
     int installCount = 0;
     String xcodeVersionInfo;