Fix "E2E test setting and using isolate names (#23388)" (#23438)

Updates gradle.

This reverts commit d07c09c9a1a8bd9a0860c579154974437da14024.
diff --git a/dev/devicelab/bin/tasks/named_isolates_test.dart b/dev/devicelab/bin/tasks/named_isolates_test.dart
new file mode 100644
index 0000000..0373f7d
--- /dev/null
+++ b/dev/devicelab/bin/tasks/named_isolates_test.dart
@@ -0,0 +1,111 @@
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:meta/meta.dart';
+import 'package:path/path.dart' as path;
+
+import 'package:flutter_devicelab/framework/adb.dart';
+import 'package:flutter_devicelab/framework/framework.dart';
+import 'package:flutter_devicelab/framework/utils.dart';
+
+const String _kActivityId = 'io.flutter.examples.named_isolates/com.example.view.MainActivity';
+const String _kFirstIsolateName = 'first isolate name';
+const String _kSecondIsolateName = 'second isolate name';
+
+void main() {
+  task(() async {
+    final AndroidDevice device = await devices.workingDevice;
+    await device.unlock();
+
+    section('Compile and run the tester app');
+    Completer<void> firstNameFound = Completer<void>();
+    Completer<void> secondNameFound = Completer<void>();
+    final Process runProcess = await _run(device: device, command: <String>['run'], stdoutListener: (String line) {
+      if (line.contains(_kFirstIsolateName)) {
+        firstNameFound.complete();
+      } else if (line.contains(_kSecondIsolateName)) {
+        secondNameFound.complete();
+      }
+    });
+
+    section('Verify all the debug isolate names are set');
+    runProcess.stdin.write('l');
+    await Future.wait<dynamic>(<Future<dynamic>>[firstNameFound.future, secondNameFound.future])
+                .timeout(Duration(seconds: 1), onTimeout: () => throw 'Isolate names not found.');
+    await _quitRunner(runProcess);
+
+    section('Attach to the second debug isolate');
+    firstNameFound = Completer<void>();
+    secondNameFound = Completer<void>();
+    final String currentTime = (await device.shellEval('date', <String>['"+%F %R:%S.000"'])).trim();
+    await device.shellExec('am', <String>['start', '-n', _kActivityId]);
+    final String observatoryLine = await device.adb(<String>['logcat', '-e', 'Observatory listening on http:', '-m', '1', '-T', currentTime]);
+    print('Found observatory line: $observatoryLine');
+    final String observatoryPort = RegExp(r'Observatory listening on http://.*:([0-9]+)').firstMatch(observatoryLine)[1];
+    print('Extracted observatory port: $observatoryPort');
+    final Process attachProcess =
+      await _run(device: device, command: <String>['attach', '--debug-port', observatoryPort, '--isolate-filter', '$_kSecondIsolateName'], stdoutListener: (String line) {
+        if (line.contains(_kFirstIsolateName)) {
+          firstNameFound.complete();
+        } else if (line.contains(_kSecondIsolateName)) {
+          secondNameFound.complete();
+        }
+      });
+    attachProcess.stdin.write('l');
+    await secondNameFound.future;
+    if (firstNameFound.isCompleted)
+      throw '--isolate-filter failed to attach to a specific isolate';
+    await _quitRunner(attachProcess);
+
+    return TaskResult.success(null);
+  });
+}
+
+Future<Process> _run({@required Device device, @required List<String> command, @required Function(String) stdoutListener}) async {
+  final Directory appDir = dir(path.join(flutterDirectory.path, 'dev/integration_tests/named_isolates'));
+  Process runner;
+  bool observatoryConnected = false;
+  await inDirectory(appDir, () async {
+  runner = await startProcess(
+      path.join(flutterDirectory.path, 'bin', 'flutter'),
+      <String>['--suppress-analytics', '-d', device.deviceId] + command,
+      isBot: false, // we just want to test the output, not have any debugging info
+    );
+    final StreamController<String> stdout = StreamController<String>.broadcast();
+
+    // Mirror output to stdout, listen for ready message
+    final Completer<void> appReady = Completer<void>();
+    runner.stdout
+      .transform<String>(utf8.decoder)
+      .transform<String>(const LineSplitter())
+      .listen((String line) {
+        print('run:stdout: $line');
+        stdout.add(line);
+        if (parseServicePort(line) != null) {
+          appReady.complete();
+          observatoryConnected = true;
+        }
+        stdoutListener(line);
+      });
+    runner.stderr
+      .transform<String>(utf8.decoder)
+      .transform<String>(const LineSplitter())
+      .listen((String line) {
+        stderr.writeln('run:stderr: $line');
+      });
+
+    // Wait for either the process to fail or for the run to begin.
+    await Future.any<dynamic>(<Future<dynamic>>[ appReady.future, runner.exitCode ]);
+    if (!observatoryConnected)
+      throw 'Failed to find service port when running `${command.join(' ')}`';
+  });
+  return runner;
+}
+
+Future<void> _quitRunner(Process runner) async {
+  runner.stdin.write('q');
+  final int result = await runner.exitCode;
+  if (result != 0)
+    throw 'Received unexpected exit code $result when quitting process.';
+}
diff --git a/dev/devicelab/manifest.yaml b/dev/devicelab/manifest.yaml
index 9d344d0..45a342bd 100644
--- a/dev/devicelab/manifest.yaml
+++ b/dev/devicelab/manifest.yaml
@@ -283,6 +283,13 @@
     stage: devicelab
     required_agent_capabilities: ["linux/android"]
 
+  named_isolates_test:
+    description: >
+      Tests naming and attaching to specific isolates.
+    stage: devicelab
+    required_agent_capabilities: ["linux/android"]
+    flaky: true
+
   flutter_create_offline_test_linux:
     description: >
       Tests the `flutter create --offline` command.
diff --git a/dev/integration_tests/named_isolates/README.md b/dev/integration_tests/named_isolates/README.md
new file mode 100644
index 0000000..a9f3fc5
--- /dev/null
+++ b/dev/integration_tests/named_isolates/README.md
@@ -0,0 +1 @@
+Integration app for testing multiple named isolates.
\ No newline at end of file
diff --git a/dev/integration_tests/named_isolates/android/app/build.gradle b/dev/integration_tests/named_isolates/android/app/build.gradle
new file mode 100644
index 0000000..603ec13
--- /dev/null
+++ b/dev/integration_tests/named_isolates/android/app/build.gradle
@@ -0,0 +1,52 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+    localPropertiesFile.withInputStream { stream ->
+        localProperties.load(stream)
+    }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+apply plugin: 'com.android.application'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+    compileSdkVersion 27
+
+    lintOptions {
+        disable 'InvalidPackage'
+    }
+
+    defaultConfig {
+        applicationId "io.flutter.examples.named_isolates"
+        minSdkVersion 16
+        targetSdkVersion 27
+        versionCode 1
+        versionName "0.0.1"
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes {
+        release {
+            // TODO: Add your own signing config for the release build.
+            // Signing with the debug keys for now, so `flutter run --release` works.
+            signingConfig signingConfigs.debug
+        }
+    }
+}
+
+flutter {
+    source '../..'
+}
+
+dependencies {
+    testImplementation 'junit:junit:4.12'
+    androidTestImplementation 'com.android.support.test:runner:1.0.2'
+    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+    implementation 'com.android.support:appcompat-v7:27.1.1'
+    implementation 'com.android.support:design:27.1.1'
+}
diff --git a/dev/integration_tests/named_isolates/android/app/src/main/AndroidManifest.xml b/dev/integration_tests/named_isolates/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..3bdbf10
--- /dev/null
+++ b/dev/integration_tests/named_isolates/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.view">
+
+    <!-- The INTERNET permission is required for development. Specifically, flutter needs it to communicate with the running application
+         to allow setting breakpoints, to provide hot reload, etc.
+    -->
+    <uses-permission android:name="android.permission.INTERNET"/>
+
+    <!-- io.flutter.app.FlutterApplication is an android.app.Application that
+         calls FlutterMain.startInitialization(this); in its onCreate method.
+         In most cases you can leave this as-is, but you if you want to provide
+         additional functionality it is fine to subclass or reimplement
+         FlutterApplication and put your custom class here. -->
+    <application android:name="io.flutter.app.FlutterApplication" android:label="named_isolates">
+        <activity android:name=".MainActivity"
+                  android:launchMode="singleTop"
+                  android:theme="@style/Theme.AppCompat"
+                  android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
+                  android:hardwareAccelerated="true"
+                  android:windowSoftInputMode="adjustResize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/dev/integration_tests/named_isolates/android/app/src/main/java/com/example/view/MainActivity.java b/dev/integration_tests/named_isolates/android/app/src/main/java/com/example/view/MainActivity.java
new file mode 100644
index 0000000..5b497e7
--- /dev/null
+++ b/dev/integration_tests/named_isolates/android/app/src/main/java/com/example/view/MainActivity.java
@@ -0,0 +1,71 @@
+package com.example.view;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.design.widget.FloatingActionButton;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.widget.TextView;
+import io.flutter.plugin.common.BasicMessageChannel;
+import io.flutter.plugin.common.BasicMessageChannel.MessageHandler;
+import io.flutter.plugin.common.BasicMessageChannel.Reply;
+import io.flutter.plugin.common.StringCodec;
+import io.flutter.view.FlutterMain;
+import io.flutter.view.FlutterRunArguments;
+import io.flutter.view.FlutterView;
+import java.util.ArrayList;
+
+public class MainActivity extends AppCompatActivity {
+    private FlutterView firstFlutterView;
+    private FlutterView secondFlutterView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        FlutterMain.ensureInitializationComplete(getApplicationContext(), null);
+        setContentView(R.layout.flutter_view_layout);
+        ActionBar supportActionBar = getSupportActionBar();
+        if (supportActionBar != null) {
+            supportActionBar.hide();
+        }
+
+        FlutterRunArguments firstRunArguments = new FlutterRunArguments();
+        firstRunArguments.bundlePath = FlutterMain.findAppBundlePath(getApplicationContext());
+        firstRunArguments.entrypoint = "first";
+        firstFlutterView = findViewById(R.id.first);
+        firstFlutterView.runFromBundle(firstRunArguments);
+
+        FlutterRunArguments secondRunArguments = new FlutterRunArguments();
+        secondRunArguments.bundlePath = FlutterMain.findAppBundlePath(getApplicationContext());
+        secondRunArguments.entrypoint = "second";
+        secondFlutterView = findViewById(R.id.second);
+        secondFlutterView.runFromBundle(secondRunArguments);
+    }
+
+    @Override
+    protected void onDestroy() {
+        if (firstFlutterView != null) {
+            firstFlutterView.destroy();
+        }
+        if (secondFlutterView != null) {
+            secondFlutterView.destroy();
+        }
+        super.onDestroy();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        firstFlutterView.onPause();
+        secondFlutterView.onPause();
+    }
+
+    @Override
+    protected void onPostResume() {
+        super.onPostResume();
+        firstFlutterView.onPostResume();
+        secondFlutterView.onPostResume();
+    }
+}
diff --git a/dev/integration_tests/named_isolates/android/app/src/main/res/layout/flutter_view_layout.xml b/dev/integration_tests/named_isolates/android/app/src/main/res/layout/flutter_view_layout.xml
new file mode 100644
index 0000000..2187f69
--- /dev/null
+++ b/dev/integration_tests/named_isolates/android/app/src/main/res/layout/flutter_view_layout.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+  xmlns:app="http://schemas.android.com/apk/res-auto"
+  android:orientation="vertical"
+  android:layout_width="match_parent"
+  android:layout_height="match_parent"
+  >
+
+  <io.flutter.view.FlutterView
+    android:id="@+id/first"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_weight="1"
+    />
+
+  <io.flutter.view.FlutterView
+    android:id="@+id/second"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_weight="1"
+    />
+
+</LinearLayout>
diff --git a/dev/integration_tests/named_isolates/android/build.gradle b/dev/integration_tests/named_isolates/android/build.gradle
new file mode 100644
index 0000000..bb8a303
--- /dev/null
+++ b/dev/integration_tests/named_isolates/android/build.gradle
@@ -0,0 +1,29 @@
+buildscript {
+    repositories {
+        google()
+        jcenter()
+    }
+
+    dependencies {
+        classpath 'com.android.tools.build:gradle:3.2.1'
+    }
+}
+
+allprojects {
+    repositories {
+        google()
+        jcenter()
+    }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+    project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+    project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}
diff --git a/dev/integration_tests/named_isolates/android/gradle.properties b/dev/integration_tests/named_isolates/android/gradle.properties
new file mode 100644
index 0000000..8bd86f6
--- /dev/null
+++ b/dev/integration_tests/named_isolates/android/gradle.properties
@@ -0,0 +1 @@
+org.gradle.jvmargs=-Xmx1536M
diff --git a/dev/integration_tests/named_isolates/android/gradle/wrapper/gradle-wrapper.properties b/dev/integration_tests/named_isolates/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..2819f02
--- /dev/null
+++ b/dev/integration_tests/named_isolates/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
diff --git a/dev/integration_tests/named_isolates/android/settings.gradle b/dev/integration_tests/named_isolates/android/settings.gradle
new file mode 100644
index 0000000..115da6c
--- /dev/null
+++ b/dev/integration_tests/named_isolates/android/settings.gradle
@@ -0,0 +1,15 @@
+include ':app'
+
+def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
+
+def plugins = new Properties()
+def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
+if (pluginsFile.exists()) {
+    pluginsFile.withInputStream { stream -> plugins.load(stream) }
+}
+
+plugins.each { name, path ->
+    def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
+    include ":$name"
+    project(":$name").projectDir = pluginDirectory
+}
diff --git a/dev/integration_tests/named_isolates/lib/main.dart b/dev/integration_tests/named_isolates/lib/main.dart
new file mode 100644
index 0000000..ec09c9e
--- /dev/null
+++ b/dev/integration_tests/named_isolates/lib/main.dart
@@ -0,0 +1,23 @@
+import 'dart:ui' as ui;
+import 'package:flutter/material.dart';
+
+// named_isolates_test depends on these values.
+const String _kFirstIsolateName = 'first isolate name';
+const String _kSecondIsolateName = 'second isolate name';
+
+void first() {
+  _run(_kFirstIsolateName);
+}
+
+void second() {
+  _run(_kSecondIsolateName);
+}
+
+void _run(String name) {
+  ui.window.setIsolateDebugName(name);
+  runApp(Center(child: Text(name, textDirection: TextDirection.ltr)));
+}
+
+// `first` and `second` are the actual entrypoints to this app, but dart specs
+// require a main function.
+void main() { }
diff --git a/dev/integration_tests/named_isolates/pubspec.yaml b/dev/integration_tests/named_isolates/pubspec.yaml
new file mode 100644
index 0000000..2361026
--- /dev/null
+++ b/dev/integration_tests/named_isolates/pubspec.yaml
@@ -0,0 +1,20 @@
+name: named_isolates
+description: Tester app for naming specific isolates.
+
+environment:
+  # The pub client defaults to an <2.0.0 sdk constraint which we need to explicitly overwrite.
+  sdk: ">=2.0.0-dev.68.0 <3.0.0"
+
+dependencies:
+  flutter:
+    sdk: flutter
+
+  collection: 1.14.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  meta: 1.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  typed_data: 1.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+  vector_math: 2.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
+
+flutter:
+  uses-material-design: true
+
+# PUBSPEC CHECKSUM: d53c