Extract Android SDK version from named platform dirs. (#13056)

Previously, we were mapping certain named platforms
(e.g. `android-stable`) to their corresponding version.
this had two problems:

1. The version could become out of date. For instance, we had
   mapped `android-stable` to version 24, but the stable version
   is now 27.
2. The list of possible named versions wasn't comprehensive.
   Some Android SDKs just list the platform as `stable`, or
   `experimental`, etc.

This change updates the platform version detection to use
the `build.prop` file that exists in the platform directory
(only for cases where the version number is not encoded into
the directory name).
diff --git a/packages/flutter_tools/lib/src/android/android_sdk.dart b/packages/flutter_tools/lib/src/android/android_sdk.dart
index 0ea844b..fb88234 100644
--- a/packages/flutter_tools/lib/src/android/android_sdk.dart
+++ b/packages/flutter_tools/lib/src/android/android_sdk.dart
@@ -2,6 +2,10 @@
 // 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:meta/meta.dart';
+
 import '../base/common.dart';
 import '../base/context.dart';
 import '../base/file_system.dart';
@@ -29,11 +33,8 @@
 // $ANDROID_HOME/platforms/android-23/android.jar
 // $ANDROID_HOME/platforms/android-N/android.jar
 
-// Special case some version names in the sdk.
-const Map<String, int> _namedVersionMap = const <String, int> {
-  'android-N': 24,
-  'android-stable': 24,
-};
+final RegExp _numberedAndroidPlatformRe = new RegExp(r'^android-([0-9]+)$');
+final RegExp _sdkVersionRe = new RegExp(r'^ro.build.version.sdk=([0-9]+)$');
 
 /// The minimum Android SDK version we support.
 const int minimumAndroidSdkVersion = 25;
@@ -142,15 +143,17 @@
   }
 
   void _init() {
-    List<String> platforms = <String>[]; // android-22, ...
+    Iterable<Directory> platforms = <Directory>[]; // android-22, ...
 
     final Directory platformsDir = fs.directory(fs.path.join(directory, 'platforms'));
     if (platformsDir.existsSync()) {
       platforms = platformsDir
         .listSync()
-        .map<String>((FileSystemEntity entity) => fs.path.basename(entity.path))
-        .where((String name) => name.startsWith('android-'))
-        .toList();
+        .where((FileSystemEntity entity) => entity is Directory)
+        .map<Directory>((FileSystemEntity entity) {
+          final Directory dir = entity;
+          return dir;
+        });
     }
 
     List<Version> buildTools = <Version>[]; // 19.1.0, 22.0.1, ...
@@ -161,7 +164,7 @@
         .listSync()
         .map((FileSystemEntity entity) {
           try {
-            return new Version.parse(fs.path.basename(entity.path));
+            return new Version.parse(entity.basename);
           } catch (error) {
             return null;
           }
@@ -171,14 +174,23 @@
     }
 
     // Match up platforms with the best corresponding build-tools.
-    _sdkVersions = platforms.map((String platformName) {
+    _sdkVersions = platforms.map((Directory platformDir) {
+      final String platformName = platformDir.basename;
       int platformVersion;
 
       try {
-        if (_namedVersionMap.containsKey(platformName))
-          platformVersion = _namedVersionMap[platformName];
-        else
-          platformVersion = int.parse(platformName.substring('android-'.length));
+        final Match numberedVersion = _numberedAndroidPlatformRe.firstMatch(platformName);
+        if (numberedVersion != null) {
+          platformVersion = int.parse(numberedVersion.group(1));
+        } else {
+          final String buildProps = platformDir.childFile('build.prop').readAsStringSync();
+          final String versionString = const LineSplitter()
+              .convert(buildProps)
+              .map(_sdkVersionRe.firstMatch)
+              .firstWhere((Match match) => match != null)
+              .group(1);
+          platformVersion = int.parse(versionString);
+        }
       } catch (error) {
         return null;
       }
@@ -192,10 +204,11 @@
       if (buildToolsVersion == null)
         return null;
 
-      return new AndroidSdkVersion(
+      return new AndroidSdkVersion._(
         this,
-        platformVersionName: platformName,
-        buildToolsVersion: buildToolsVersion
+        sdkLevel: platformVersion,
+        platformName: platformName,
+        buildToolsVersion: buildToolsVersion,
       );
     }).where((AndroidSdkVersion version) => version != null).toList();
 
@@ -209,22 +222,20 @@
 }
 
 class AndroidSdkVersion implements Comparable<AndroidSdkVersion> {
-  AndroidSdkVersion(this.sdk, {
-    this.platformVersionName,
-    this.buildToolsVersion,
-  });
+  AndroidSdkVersion._(this.sdk, {
+    @required this.sdkLevel,
+    @required this.platformName,
+    @required this.buildToolsVersion,
+  }) : assert(sdkLevel != null),
+       assert(platformName != null),
+       assert(buildToolsVersion != null);
 
   final AndroidSdk sdk;
-  final String platformVersionName;
+  final int sdkLevel;
+  final String platformName;
   final Version buildToolsVersion;
-  String get buildToolsVersionName => buildToolsVersion.toString();
 
-  int get sdkLevel {
-    if (_namedVersionMap.containsKey(platformVersionName))
-      return _namedVersionMap[platformVersionName];
-    else
-      return int.parse(platformVersionName.substring('android-'.length));
-  }
+  String get buildToolsVersionName => buildToolsVersion.toString();
 
   String get androidJarPath => getPlatformsPath('android.jar');
 
@@ -241,7 +252,7 @@
   }
 
   String getPlatformsPath(String itemName) {
-    return fs.path.join(sdk.directory, 'platforms', platformVersionName, itemName);
+    return fs.path.join(sdk.directory, 'platforms', platformName, itemName);
   }
 
   String getBuildToolsPath(String binaryName) {