Toolchain support for Android ARM64 targets (#14394)

diff --git a/packages/flutter_tools/gradle/flutter.gradle b/packages/flutter_tools/gradle/flutter.gradle
index 55c0380..5c655f8 100644
--- a/packages/flutter_tools/gradle/flutter.gradle
+++ b/packages/flutter_tools/gradle/flutter.gradle
@@ -115,9 +115,14 @@
             }
         } else {
             Path baseEnginePath = Paths.get(flutterRoot.absolutePath, "bin", "cache", "artifacts", "engine")
-            debugFlutterJar = baseEnginePath.resolve("android-arm").resolve("flutter.jar").toFile()
-            profileFlutterJar = baseEnginePath.resolve("android-arm-profile").resolve("flutter.jar").toFile()
-            releaseFlutterJar = baseEnginePath.resolve("android-arm-release").resolve("flutter.jar").toFile()
+            String targetArch = 'arm'
+            if (project.hasProperty('target-platform') &&
+                project.property('target-platform') == 'android-arm64') {
+              targetArch = 'arm64'
+            }
+            debugFlutterJar = baseEnginePath.resolve("android-${targetArch}").resolve("flutter.jar").toFile()
+            profileFlutterJar = baseEnginePath.resolve("android-${targetArch}-profile").resolve("flutter.jar").toFile()
+            releaseFlutterJar = baseEnginePath.resolve("android-${targetArch}-release").resolve("flutter.jar").toFile()
             if (!debugFlutterJar.isFile()) {
                 project.exec {
                     executable flutterExecutable.absolutePath
@@ -273,6 +278,10 @@
         if (project.hasProperty('prefer-shared-library')) {
             preferSharedLibraryValue = project.property('prefer-shared-library')
         }
+        String targetPlatformValue = null
+        if (project.hasProperty('target-platform')) {
+            targetPlatformValue = project.property('target-platform')
+        }
 
         project.android.applicationVariants.all { variant ->
             String flutterBuildMode = buildModeFor(variant.buildType)
@@ -297,6 +306,7 @@
                 previewDart2 previewDart2Value
                 strongMode strongModeValue
                 preferSharedLibrary preferSharedLibraryValue
+                targetPlatform targetPlatformValue
                 sourceDir project.file(project.flutter.source)
                 intermediateDir project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}")
             }
@@ -312,6 +322,7 @@
                 previewDart2 previewDart2Value
                 strongMode strongModeValue
                 preferSharedLibrary preferSharedLibraryValue
+                targetPlatform targetPlatformValue
                 sourceDir project.file(project.flutter.source)
                 intermediateDir project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}")
                 extraFrontEndOptions extraFrontEndOptionsValue
@@ -349,6 +360,8 @@
     Boolean strongMode
     @Optional @Input
     Boolean preferSharedLibrary
+    @Optional @Input
+    String targetPlatform
     File sourceDir
     File intermediateDir
     @Optional @Input
@@ -400,6 +413,9 @@
                 if (preferSharedLibrary) {
                     args "--prefer-shared-library"
                 }
+                if (targetPlatform != null) {
+                    args "--target-platform", "${targetPlatform}"
+                }
                 args "--${buildMode}"
             }
         }
diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart
index 9c9cac6..aef3679 100644
--- a/packages/flutter_tools/lib/src/android/android_device.dart
+++ b/packages/flutter_tools/lib/src/android/android_device.dart
@@ -126,6 +126,9 @@
     if (_platform == null) {
       // http://developer.android.com/ndk/guides/abis.html (x86, armeabi-v7a, ...)
       switch (await _getProperty('ro.product.cpu.abi')) {
+        case 'arm64-v8a':
+          _platform = TargetPlatform.android_arm64;
+          break;
         case 'x86_64':
           _platform = TargetPlatform.android_x64;
           break;
@@ -355,16 +358,23 @@
     if (!await _checkForSupportedAdbVersion() || !await _checkForSupportedAndroidVersion())
       return new LaunchResult.failed();
 
-    if (await targetPlatform != TargetPlatform.android_arm && !debuggingOptions.buildInfo.isDebug) {
+    final TargetPlatform devicePlatform = await targetPlatform;
+    if (!(devicePlatform == TargetPlatform.android_arm ||
+          devicePlatform == TargetPlatform.android_arm64) &&
+        !debuggingOptions.buildInfo.isDebug) {
       printError('Profile and release builds are only supported on ARM targets.');
       return new LaunchResult.failed();
     }
 
+    BuildInfo buildInfo = debuggingOptions.buildInfo;
+    if (devicePlatform == TargetPlatform.android_arm64)
+      buildInfo = buildInfo.withTargetPlatform(TargetPlatform.android_arm64);
+
     if (!prebuiltApplication) {
       printTrace('Building APK');
       await buildApk(
           target: mainPath,
-          buildInfo: debuggingOptions.buildInfo,
+          buildInfo: buildInfo,
       );
       // Package has been built, so we can get the updated application ID and
       // activity name from the .apk.
diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart
index 8d8957a..e22bc8b 100644
--- a/packages/flutter_tools/lib/src/android/gradle.dart
+++ b/packages/flutter_tools/lib/src/android/gradle.dart
@@ -301,6 +301,8 @@
   if (buildInfo.preferSharedLibrary && androidSdk.ndkCompiler != null) {
     command.add('-Pprefer-shared-library=true');
   }
+  if (buildInfo.targetPlatform == TargetPlatform.android_arm64)
+    command.add('-Ptarget-platform=android-arm64');
 
   command.add(assembleTask);
   final int exitCode = await runCommandAndStreamOutput(
diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart
index 14c0310..f734d7a 100644
--- a/packages/flutter_tools/lib/src/application_package.dart
+++ b/packages/flutter_tools/lib/src/application_package.dart
@@ -259,6 +259,7 @@
 }) async {
   switch (platform) {
     case TargetPlatform.android_arm:
+    case TargetPlatform.android_arm64:
     case TargetPlatform.android_x64:
     case TargetPlatform.android_x86:
       return applicationBinary == null
@@ -287,6 +288,7 @@
   Future<ApplicationPackage> getPackageForPlatform(TargetPlatform platform) async {
     switch (platform) {
       case TargetPlatform.android_arm:
+      case TargetPlatform.android_arm64:
       case TargetPlatform.android_x64:
       case TargetPlatform.android_x86:
         android ??= await AndroidApk.fromCurrentDirectory();
diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart
index 3c4bfa3..bfe8963 100644
--- a/packages/flutter_tools/lib/src/artifacts.dart
+++ b/packages/flutter_tools/lib/src/artifacts.dart
@@ -97,6 +97,7 @@
     platform ??= _currentHostPlatform;
     switch (platform) {
       case TargetPlatform.android_arm:
+      case TargetPlatform.android_arm64:
       case TargetPlatform.android_x64:
       case TargetPlatform.android_x86:
         return _getAndroidArtifactPath(artifact, platform, mode);
@@ -195,6 +196,7 @@
         return fs.path.join(engineDir, platformName);
       case TargetPlatform.ios:
       case TargetPlatform.android_arm:
+      case TargetPlatform.android_arm64:
       case TargetPlatform.android_x64:
       case TargetPlatform.android_x86:
         assert(mode != null, 'Need to specify a build mode for platform $platform.');
diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart
index 63bf145..8ed2b77 100644
--- a/packages/flutter_tools/lib/src/build_info.dart
+++ b/packages/flutter_tools/lib/src/build_info.dart
@@ -15,7 +15,8 @@
       this.strongMode,
       this.extraFrontEndOptions,
       this.extraGenSnapshotOptions,
-      this.preferSharedLibrary});
+      this.preferSharedLibrary,
+      this.targetPlatform});
 
   final BuildMode mode;
   /// Represents a custom Android product flavor or an Xcode scheme, null for
@@ -41,6 +42,9 @@
   // Whether to prefer AOT compiling to a *so file.
   final bool preferSharedLibrary;
 
+  /// Target platform for the build (e.g. android_arm versus android_arm64).
+  final TargetPlatform targetPlatform;
+
   static const BuildInfo debug = const BuildInfo(BuildMode.debug, null);
   static const BuildInfo profile = const BuildInfo(BuildMode.profile, null);
   static const BuildInfo release = const BuildInfo(BuildMode.release, null);
@@ -64,6 +68,15 @@
   bool get supportsEmulator => isEmulatorBuildMode(mode);
   bool get supportsSimulator => isEmulatorBuildMode(mode);
   String get modeName => getModeName(mode);
+
+  BuildInfo withTargetPlatform(TargetPlatform targetPlatform) =>
+      new BuildInfo(mode, flavor,
+          previewDart2: previewDart2,
+          strongMode: strongMode,
+          extraFrontEndOptions: extraFrontEndOptions,
+          extraGenSnapshotOptions: extraGenSnapshotOptions,
+          preferSharedLibrary: preferSharedLibrary,
+          targetPlatform: targetPlatform);
 }
 
 /// The type of build - `debug`, `profile`, or `release`.
@@ -114,6 +127,7 @@
 
 enum TargetPlatform {
   android_arm,
+  android_arm64,
   android_x64,
   android_x86,
   ios,
@@ -127,6 +141,8 @@
   switch (platform) {
     case TargetPlatform.android_arm:
       return 'android-arm';
+    case TargetPlatform.android_arm64:
+      return 'android-arm64';
     case TargetPlatform.android_x64:
       return 'android-x64';
     case TargetPlatform.android_x86:
@@ -150,6 +166,8 @@
   switch (platform) {
     case 'android-arm':
       return TargetPlatform.android_arm;
+    case 'android-arm64':
+      return TargetPlatform.android_arm64;
     case 'android-x64':
       return TargetPlatform.android_x64;
     case 'android-x86':
diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart
index ba657bf..62083ac 100644
--- a/packages/flutter_tools/lib/src/cache.dart
+++ b/packages/flutter_tools/lib/src/cache.dart
@@ -394,6 +394,8 @@
     <String>['linux-x64', 'dart-sdk-linux-x64.zip'],
     <String>['android-arm-profile/linux-x64', 'android-arm-profile/linux-x64.zip'],
     <String>['android-arm-release/linux-x64', 'android-arm-release/linux-x64.zip'],
+    <String>['android-arm64-profile/linux-x64', 'android-arm64-profile/linux-x64.zip'],
+    <String>['android-arm64-release/linux-x64', 'android-arm64-release/linux-x64.zip'],
   ];
 
   List<List<String>> get _windowsBinaryDirs => <List<String>>[
@@ -409,6 +411,9 @@
     <String>['android-arm', 'android-arm/artifacts.zip'],
     <String>['android-arm-profile', 'android-arm-profile/artifacts.zip'],
     <String>['android-arm-release', 'android-arm-release/artifacts.zip'],
+    <String>['android-arm64', 'android-arm64/artifacts.zip'],
+    <String>['android-arm64-profile', 'android-arm64-profile/artifacts.zip'],
+    <String>['android-arm64-release', 'android-arm64-release/artifacts.zip'],
   ];
 
   List<List<String>> get _iosBinaryDirs => <List<String>>[
diff --git a/packages/flutter_tools/lib/src/commands/build_aot.dart b/packages/flutter_tools/lib/src/commands/build_aot.dart
index 9233bbf..dd60d4d 100644
--- a/packages/flutter_tools/lib/src/commands/build_aot.dart
+++ b/packages/flutter_tools/lib/src/commands/build_aot.dart
@@ -35,7 +35,7 @@
       ..addOption('output-dir', defaultsTo: getAotBuildDirectory())
       ..addOption('target-platform',
         defaultsTo: 'android-arm',
-        allowed: <String>['android-arm', 'ios']
+        allowed: <String>['android-arm', 'android-arm64', 'ios']
       )
       ..addFlag('interpreter')
       ..addFlag('quiet', defaultsTo: false)
@@ -159,7 +159,9 @@
     return null;
   }
 
-  if (platform != TargetPlatform.android_arm && platform != TargetPlatform.ios) {
+  if (!(platform == TargetPlatform.android_arm ||
+        platform == TargetPlatform.android_arm64 ||
+        platform == TargetPlatform.ios)) {
     printError('${getNameForTargetPlatform(platform)} does not support AOT compilation.');
     return null;
   }
@@ -217,6 +219,7 @@
 
   switch (platform) {
     case TargetPlatform.android_arm:
+    case TargetPlatform.android_arm64:
     case TargetPlatform.android_x64:
     case TargetPlatform.android_x86:
       if (compileToSharedLibrary) {
@@ -287,6 +290,7 @@
 
   switch (platform) {
     case TargetPlatform.android_arm:
+    case TargetPlatform.android_arm64:
     case TargetPlatform.android_x64:
     case TargetPlatform.android_x86:
       if (compileToSharedLibrary) {
@@ -300,10 +304,12 @@
           '--isolate_snapshot_instructions=$isolateSnapshotInstructions',
         ]);
       }
-      genSnapshotCmd.addAll(<String>[
-        '--no-sim-use-hardfp',  // Android uses the softfloat ABI.
-        '--no-use-integer-division',  // Not supported by the Pixel in 32-bit mode.
-      ]);
+      if (platform == TargetPlatform.android_arm) {
+        genSnapshotCmd.addAll(<String>[
+          '--no-sim-use-hardfp',  // Android uses the softfloat ABI.
+          '--no-use-integer-division',  // Not supported by the Pixel in 32-bit mode.
+        ]);
+      }
       break;
     case TargetPlatform.ios:
       if (interpreter) {
diff --git a/packages/flutter_tools/lib/src/commands/build_apk.dart b/packages/flutter_tools/lib/src/commands/build_apk.dart
index 4636e48..67d8a18 100644
--- a/packages/flutter_tools/lib/src/commands/build_apk.dart
+++ b/packages/flutter_tools/lib/src/commands/build_apk.dart
@@ -18,7 +18,10 @@
       ..addFlag('preview-dart-2', negatable: false,  hide: !verboseHelp)
       ..addFlag('strong', negatable: false,  hide: !verboseHelp)
       ..addFlag('prefer-shared-library', negatable: false,
-          help: 'Whether to prefer compiling to a *.so file (android only).');
+          help: 'Whether to prefer compiling to a *.so file (android only).')
+      ..addOption('target-platform',
+        defaultsTo: 'android-arm',
+        allowed: <String>['android-arm', 'android-arm64']);
   }
 
   @override
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index a7c520a..db64ccd 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -886,6 +886,7 @@
 String getMissingPackageHintForPlatform(TargetPlatform platform) {
   switch (platform) {
     case TargetPlatform.android_arm:
+    case TargetPlatform.android_arm64:
     case TargetPlatform.android_x64:
     case TargetPlatform.android_x86:
       String manifest = 'android/AndroidManifest.xml';
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index f8962b3..3907599 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -185,7 +185,10 @@
           : null,
       preferSharedLibrary: argParser.options.containsKey('prefer-shared-library')
         ? argResults['prefer-shared-library']
-        : false);
+        : false,
+      targetPlatform: argParser.options.containsKey('target-platform')
+        ? getTargetPlatformForName(argResults['target-platform'])
+        : null);
   }
 
   void setupApplicationPackages() {