flutter devices/doctor report Android (and iOS) version (#6545)

* refactor device descriptions method - easier to add columns
* show device SDK version
diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart
index 2cff7b3..02ed8b1 100644
--- a/packages/flutter_tools/lib/src/android/android_device.dart
+++ b/packages/flutter_tools/lib/src/android/android_device.dart
@@ -115,6 +115,13 @@
     return _platform;
   }
 
+  @override
+  String get sdkNameAndVersion => 'Android $_sdkVersion (API $_apiVersion)';
+
+  String get _sdkVersion => _getProperty('ro.build.version.release');
+
+  String get _apiVersion => _getProperty('ro.build.version.sdk');
+
   _AdbLogReader _logReader;
   _AndroidDevicePortForwarder _portForwarder;
 
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index 6ca400a..df9bcc3 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -163,6 +163,8 @@
 
   TargetPlatform get platform;
 
+  String get sdkNameAndVersion;
+
   /// Get the log reader for this device.
   DeviceLogReader get logReader;
 
@@ -239,24 +241,36 @@
   String toString() => name;
 
   static Iterable<String> descriptions(List<Device> devices) {
-    int nameWidth = 0;
-    int idWidth = 0;
+    if (devices.isEmpty)
+      return <String>[];
 
+    // Extract device information
+    List<List<String>> table = <List<String>>[];
     for (Device device in devices) {
-      nameWidth = math.max(nameWidth, device.name.length);
-      idWidth = math.max(idWidth, device.id.length);
-    }
-
-    return devices.map((Device device) {
       String supportIndicator = device.isSupported() ? '' : ' (unsupported)';
       if (device.isLocalEmulator) {
         String type = device.platform == TargetPlatform.ios ? 'simulator' : 'emulator';
         supportIndicator += ' ($type)';
       }
-      return '${device.name.padRight(nameWidth)} • '
-             '${device.id.padRight(idWidth)} • '
-             '${getNameForTargetPlatform(device.platform)}$supportIndicator';
-    });
+      table.add(<String>[
+        device.name,
+        device.id,
+        '${getNameForTargetPlatform(device.platform)}',
+        '${device.sdkNameAndVersion}$supportIndicator',
+      ]);
+    }
+
+    // Calculate column widths
+    List<int> indices = new List<int>.generate(table[0].length - 1, (int i) => i);
+    List<int> widths = indices.map((int i) => 0).toList();
+    for (List<String> row in table) {
+      widths = indices.map((int i) => math.max(widths[i], row[i].length)).toList();
+    }
+
+    // Join columns into lines of text
+    return table.map((List<String> row) =>
+        indices.map((int i) => row[i].padRight(widths[i])).join(' • ') +
+        ' • ${row.last}');
   }
 
   static void printDevices(List<Device> devices) {
diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart
index 003a65c..2fcdb31 100644
--- a/packages/flutter_tools/lib/src/ios/devices.dart
+++ b/packages/flutter_tools/lib/src/ios/devices.dart
@@ -89,7 +89,7 @@
 
     List<IOSDevice> devices = <IOSDevice>[];
     for (String id in _getAttachedDeviceIDs(mockIOS)) {
-      String name = _getDeviceName(id, mockIOS);
+      String name = IOSDevice._getDeviceInfo(id, 'DeviceName', mockIOS);
       devices.add(new IOSDevice(id, name: name));
     }
     return devices;
@@ -105,11 +105,11 @@
     }
   }
 
-  static String _getDeviceName(String deviceID, [IOSDevice mockIOS]) {
+  static String _getDeviceInfo(String deviceID, String infoKey, [IOSDevice mockIOS]) {
     String informerPath = (mockIOS != null)
         ? mockIOS.informerPath
         : _checkForCommand('ideviceinfo');
-    return runSync(<String>[informerPath, '-k', 'DeviceName', '-u', deviceID]).trim();
+    return runSync(<String>[informerPath, '-k', infoKey, '-u', deviceID]).trim();
   }
 
   static final Map<String, String> _commandMap = <String, String>{};
@@ -370,6 +370,13 @@
   TargetPlatform get platform => TargetPlatform.ios;
 
   @override
+  String get sdkNameAndVersion => 'iOS $_sdkVersion ($_buildVersion)';
+
+  String get _sdkVersion => _getDeviceInfo(id, 'ProductVersion');
+
+  String get _buildVersion => _getDeviceInfo(id, 'BuildVersion');
+
+  @override
   DeviceLogReader get logReader {
     if (_logReader == null)
       _logReader = new _IOSDeviceLogReader(this);
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart
index f6cfcaa..4a86345 100644
--- a/packages/flutter_tools/lib/src/ios/simulators.dart
+++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -46,7 +46,7 @@
       return <IOSSimulator>[];
 
     return SimControl.instance.getConnectedDevices().map((SimDevice device) {
-      return new IOSSimulator(device.udid, name: device.name);
+      return new IOSSimulator(device.udid, name: device.name, category: device.category);
     }).toList();
   }
 }
@@ -302,11 +302,13 @@
 }
 
 class IOSSimulator extends Device {
-  IOSSimulator(String id, { this.name }) : super(id);
+  IOSSimulator(String id, { this.name, this.category }) : super(id);
 
   @override
   final String name;
 
+  final String category;
+
   @override
   bool get isLocalEmulator => true;
 
@@ -558,6 +560,9 @@
   TargetPlatform get platform => TargetPlatform.ios;
 
   @override
+  String get sdkNameAndVersion => category;
+
+  @override
   DeviceLogReader get logReader {
     if (_logReader == null)
       _logReader = new _IOSSimulatorLogReader(this);