Build tool support for Android x86 targets in 32- or 64-bit modes (#3761)
diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart
index b5e37eb..0712fde 100644
--- a/packages/flutter_tools/lib/src/android/android_device.dart
+++ b/packages/flutter_tools/lib/src/android/android_device.dart
@@ -49,23 +49,49 @@
   final String modelID;
   final String deviceCodeName;
 
+  Map<String, String> _properties;
   bool _isLocalEmulator;
+  TargetPlatform _platform;
+
+  String _getProperty(String name) {
+    if (_properties == null) {
+      String getpropOutput = runCheckedSync(adbCommandForDevice(<String>['shell', 'getprop']));
+      RegExp propertyExp = new RegExp(r'\[(.*?)\]: \[(.*?)\]');
+      _properties = <String, String>{};
+      for (Match m in propertyExp.allMatches(getpropOutput)) {
+        _properties[m.group(1)] = m.group(2);
+      }
+    }
+    return _properties[name];
+  }
 
   @override
   bool get isLocalEmulator {
     if (_isLocalEmulator == null) {
+      String characteristics = _getProperty('ro.build.characteristics');
+      _isLocalEmulator = characteristics != null && characteristics.contains('emulator');
+    }
+    return _isLocalEmulator;
+  }
+
+  @override
+  TargetPlatform get platform {
+    if (_platform == null) {
       // http://developer.android.com/ndk/guides/abis.html (x86, armeabi-v7a, ...)
-      try {
-        String value = runCheckedSync(adbCommandForDevice(
-          <String>['shell', 'getprop', 'ro.product.cpu.abi']
-        ));
-        _isLocalEmulator = value.startsWith('x86');
-      } catch (error) {
-        _isLocalEmulator = false;
+      switch (_getProperty('ro.product.cpu.abi')) {
+        case 'x86_64':
+          _platform = TargetPlatform.android_x64;
+          break;
+        case 'x86':
+          _platform = TargetPlatform.android_x86;
+          break;
+        default:
+          _platform = TargetPlatform.android_arm;
+          break;
       }
     }
 
-    return _isLocalEmulator;
+    return _platform;
   }
 
   _AdbLogReader _logReader;
@@ -123,9 +149,7 @@
       runCheckedSync(<String>[androidSdk.adbPath, 'start-server']);
 
       // Sample output: '22'
-      String sdkVersion = runCheckedSync(
-        adbCommandForDevice(<String>['shell', 'getprop', 'ro.build.version.sdk'])
-      ).trimRight();
+      String sdkVersion = _getProperty('ro.build.version.sdk');
 
       int sdkVersionParsed = int.parse(sdkVersion, onError: (String source) => null);
       if (sdkVersionParsed == null) {
@@ -334,9 +358,6 @@
   }
 
   @override
-  TargetPlatform get platform => isLocalEmulator ? TargetPlatform.android_x64 : TargetPlatform.android_arm;
-
-  @override
   void clearLogs() {
     runSync(adbCommandForDevice(<String>['logcat', '-c']));
   }
diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart
index 07a9318..f4154eb 100644
--- a/packages/flutter_tools/lib/src/application_package.dart
+++ b/packages/flutter_tools/lib/src/application_package.dart
@@ -103,6 +103,7 @@
   switch (platform) {
     case TargetPlatform.android_arm:
     case TargetPlatform.android_x64:
+    case TargetPlatform.android_x86:
       return new AndroidApk.fromCurrentDirectory();
     case TargetPlatform.ios:
       return new IOSApp.fromCurrentDirectory();
@@ -122,6 +123,7 @@
     switch (platform) {
       case TargetPlatform.android_arm:
       case TargetPlatform.android_x64:
+      case TargetPlatform.android_x86:
         android ??= new AndroidApk.fromCurrentDirectory();
         return android;
       case TargetPlatform.ios:
diff --git a/packages/flutter_tools/lib/src/build_configuration.dart b/packages/flutter_tools/lib/src/build_configuration.dart
index a70e691..e4e992c 100644
--- a/packages/flutter_tools/lib/src/build_configuration.dart
+++ b/packages/flutter_tools/lib/src/build_configuration.dart
@@ -41,6 +41,7 @@
 enum TargetPlatform {
   android_arm,
   android_x64,
+  android_x86,
   ios,
   darwin_x64,
   linux_x64
diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart
index 892657b..ebb7369 100644
--- a/packages/flutter_tools/lib/src/cache.dart
+++ b/packages/flutter_tools/lib/src/cache.dart
@@ -192,7 +192,8 @@
       'android-arm',
       'android-arm-profile',
       'android-arm-release',
-      'android-x64'
+      'android-x64',
+      'android-x86',
     ];
 
     if (Platform.isMacOS)
diff --git a/packages/flutter_tools/lib/src/commands/build_apk.dart b/packages/flutter_tools/lib/src/commands/build_apk.dart
index 6004bff..0029062 100644
--- a/packages/flutter_tools/lib/src/commands/build_apk.dart
+++ b/packages/flutter_tools/lib/src/commands/build_apk.dart
@@ -261,6 +261,21 @@
   }
 }
 
+// Return the directory name within the APK that is used for native code libraries
+// on the given platform.
+String getAbiDirectory(TargetPlatform platform) {
+  switch (platform) {
+    case TargetPlatform.android_arm:
+      return 'armeabi-v7a';
+    case TargetPlatform.android_x64:
+      return 'x86_64';
+    case TargetPlatform.android_x86:
+      return 'x86';
+    default:
+      throw new Exception('Unsupported platform.');
+  }
+}
+
 Future<_ApkComponents> _findApkComponents(
   TargetPlatform platform,
   BuildMode buildMode,
@@ -274,7 +289,7 @@
   components.extraFiles = extraFiles != null ? extraFiles : <String, File>{};
 
   if (tools.isLocalEngine) {
-    String abiDir = platform == TargetPlatform.android_arm ? 'armeabi-v7a' : 'x86_64';
+    String abiDir = getAbiDirectory(platform);
     String enginePath = tools.engineSrcPath;
     String buildDir = tools.getEngineArtifactsDirectory(platform, buildMode).path;
 
@@ -348,7 +363,7 @@
 
     _AssetBuilder artifactBuilder = new _AssetBuilder(tempDir, 'artifacts');
     artifactBuilder.add(classesDex, 'classes.dex');
-    String abiDir = platform == TargetPlatform.android_arm ? 'armeabi-v7a' : 'x86_64';
+    String abiDir = getAbiDirectory(platform);
     artifactBuilder.add(components.libSkyShell, 'lib/$abiDir/libsky_shell.so');
 
     for (String relativePath in components.extraFiles.keys)
diff --git a/packages/flutter_tools/lib/src/toolchain.dart b/packages/flutter_tools/lib/src/toolchain.dart
index 7c87656..3c3dd97 100644
--- a/packages/flutter_tools/lib/src/toolchain.dart
+++ b/packages/flutter_tools/lib/src/toolchain.dart
@@ -144,10 +144,9 @@
 
       switch (platform) {
         case TargetPlatform.android_arm:
-          type = 'android';
-          break;
         case TargetPlatform.android_x64:
-          type = 'android_sim';
+        case TargetPlatform.android_x86:
+          type = 'android';
           break;
 
         // TODO(devoncarew): We will need an ios vs ios_x86 target (for ios vs. ios_sim).
@@ -166,6 +165,18 @@
       if (isAotBuildMode(mode))
         buildOutputPath += '_Deploy';
 
+      // Add a suffix for the target architecture.
+      switch (platform) {
+        case TargetPlatform.android_x64:
+          buildOutputPath += '_x64';
+          break;
+        case TargetPlatform.android_x86:
+          buildOutputPath += '_x86';
+          break;
+        default:
+          break;
+      }
+
       return new Directory(path.join(engineSrcPath, buildOutputPath));
     } else {
       String suffix = mode != BuildMode.debug ? '-${getModeName(mode)}' : '';