[fuchsia_reload] Find and attach to the first instance of the named app (#8830)

diff --git a/packages/flutter_tools/lib/src/commands/fuchsia_reload.dart b/packages/flutter_tools/lib/src/commands/fuchsia_reload.dart
index 31a8268..808d5a6 100644
--- a/packages/flutter_tools/lib/src/commands/fuchsia_reload.dart
+++ b/packages/flutter_tools/lib/src/commands/fuchsia_reload.dart
@@ -9,12 +9,14 @@
 import '../base/file_system.dart';
 import '../base/io.dart';
 import '../base/platform.dart';
+import '../cache.dart';
 import '../device.dart';
 import '../flx.dart' as flx;
 import '../fuchsia/fuchsia_device.dart';
 import '../globals.dart';
 import '../run_hot.dart';
 import '../runner/flutter_command.dart';
+import '../vmservice.dart';
 
 // Usage:
 // With e.g. flutter_gallery already running, a HotRunner can be attached to it
@@ -62,6 +64,8 @@
 
   @override
   Future<Null> runCommand() async {
+    Cache.releaseLockEarly();
+
     _validateArguments();
 
     // Find the network ports used on the device by VM service instances.
@@ -70,24 +74,57 @@
       throwToolExit("Couldn't find any running Observatory instances.");
     }
     for (int port in servicePorts) {
-      printStatus("Fuchsia service port: $port");
+      printTrace("Fuchsia service port: $port");
     }
 
-    // TODO(zra): Check that there are running VM services on the returned
+    // Check that there are running VM services on the returned
     // ports, and find the Isolates that are running the target app.
+    final String isolateName = "$_projectName\$main";
+    final List<int> targetPorts = await _filterPorts(servicePorts, isolateName);
+    if (targetPorts.length == 0) {
+      throwToolExit("No VMs found running $_projectName");
+    }
+    for (int port in targetPorts) {
+      printTrace("Found $_projectName at $port");
+    }
 
     // Set up a device and hot runner and attach the hot runner to the first
     // vm service we found.
-    final int firstPort = servicePorts[0];
-    final FuchsiaDevice device = new FuchsiaDevice("$_address:$firstPort");
+    final int firstPort = targetPorts[0];
+    final String fullAddress = "$_address:$firstPort";
+    final FuchsiaDevice device = new FuchsiaDevice(fullAddress);
     final HotRunner hotRunner = new HotRunner(
         device,
         debuggingOptions: new DebuggingOptions.enabled(getBuildMode()),
         target: _target,
         projectRootPath: _fuchsiaProjectPath,
         packagesFilePath: _dotPackagesPath);
-    final Uri observatoryUri = Uri.parse("http://$_address:$firstPort");
-    await hotRunner.attach(observatoryUri);
+    final Uri observatoryUri = Uri.parse("http://$fullAddress");
+    printStatus("Connecting to $_projectName at $observatoryUri");
+    await hotRunner.attach(observatoryUri, isolateFilter: isolateName);
+  }
+
+  // Find ports where there is a view isolate with the given name
+  Future<List<int>> _filterPorts(List<int> ports, String isolateFilter) async {
+    final List<int> result = new List<int>();
+    for (int port in ports) {
+      final String addr = "http://$_address:$port";
+      final Uri uri = Uri.parse(addr);
+      final VMService vmService = VMService.connect(uri);
+      await vmService.getVM();
+      await vmService.waitForViews();
+      if (vmService.vm.firstView == null) {
+        printTrace("Found no views at $addr");
+        continue;
+      }
+      for (FlutterView v in vmService.vm.views) {
+        printTrace("At $addr, found view: ${v.uiIsolate.name}");
+        if (v.uiIsolate.name.indexOf(isolateFilter) == 0) {
+          result.add(port);
+        }
+      }
+    }
+    return result;
   }
 
   void _validateArguments() {
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index fbe0296..bd3b874 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -209,7 +209,7 @@
     _loggingSubscription = null;
   }
 
-  Future<Null> connectToServiceProtocol(Uri uri) async {
+  Future<Null> connectToServiceProtocol(Uri uri, {String isolateFilter}) async {
     if (!debuggingOptions.debuggingEnabled) {
       return new Future<Null>.error('Error the service protocol is not enabled.');
     }
@@ -217,15 +217,11 @@
     printTrace('Connected to service protocol: $uri');
     await vmService.getVM();
 
-    // Refresh the view list.
-    await vmService.vm.refreshViews();
-    for (int i = 0; vmService.vm.mainView == null && i < 5; i++) {
-      // If the VM doesn't yet have a view, wait for one to show up.
-      printTrace('Waiting for Flutter view');
-      await new Future<Null>.delayed(const Duration(seconds: 1));
-      await vmService.vm.refreshViews();
-    }
-    currentView = vmService.vm.mainView;
+    // Refresh the view list, and wait a bit for the list to populate.
+    await vmService.waitForViews();
+    currentView = (isolateFilter == null)
+                ? vmService.vm.firstView
+                : vmService.vm.firstViewWithName(isolateFilter);
     if (currentView == null)
       throwToolExit('No Flutter view is available');
 
diff --git a/packages/flutter_tools/lib/src/run_cold.dart b/packages/flutter_tools/lib/src/run_cold.dart
index b82eeb0..ab2587c 100644
--- a/packages/flutter_tools/lib/src/run_cold.dart
+++ b/packages/flutter_tools/lib/src/run_cold.dart
@@ -115,7 +115,7 @@
 
     if (vmService != null) {
       await vmService.vm.refreshViews();
-      printTrace('Connected to ${vmService.vm.mainView}\.');
+      printTrace('Connected to ${vmService.vm.firstView}\.');
     }
 
     if (vmService != null && traceStartup) {
diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart
index 183491a..27379c5 100644
--- a/packages/flutter_tools/lib/src/run_hot.dart
+++ b/packages/flutter_tools/lib/src/run_hot.dart
@@ -90,10 +90,12 @@
   Future<int> attach(Uri observatoryUri, {
     Completer<DebugConnectionInfo> connectionInfoCompleter,
     Completer<Null> appStartedCompleter,
+    String isolateFilter,
   }) async {
     _observatoryUri = observatoryUri;
     try {
-      await connectToServiceProtocol(_observatoryUri);
+      await connectToServiceProtocol(
+          _observatoryUri, isolateFilter: isolateFilter);
     } catch (error) {
       printError('Error connecting to the service protocol: $error');
       return 2;
@@ -121,7 +123,7 @@
     }
 
     await vmService.vm.refreshViews();
-    printTrace('Connected to ${vmService.vm.mainView}.');
+    printTrace('Connected to $currentView.');
 
     if (stayResident) {
       setupTerminal();
@@ -302,7 +304,7 @@
   Future<Null> _launchInView(Uri entryUri,
                              Uri packagesUri,
                              Uri assetsDirectoryUri) async {
-    final FlutterView view = vmService.vm.mainView;
+    final FlutterView view = currentView;
     return view.runFromSource(entryUri, packagesUri, assetsDirectoryUri);
   }
 
diff --git a/packages/flutter_tools/lib/src/vmservice.dart b/packages/flutter_tools/lib/src/vmservice.dart
index 8a5b844..bbbee28 100644
--- a/packages/flutter_tools/lib/src/vmservice.dart
+++ b/packages/flutter_tools/lib/src/vmservice.dart
@@ -170,6 +170,16 @@
   Future<VM> getVM() {
     return _vm.reload();
   }
+
+  Future<Null> waitForViews({int attempts = 5, int attemptSeconds = 1}) async {
+    await vm.refreshViews();
+    for (int i = 0; (vm.firstView == null) && (i < attempts); i++) {
+      // If the VM doesn't yet have a view, wait for one to show up.
+      printTrace('Waiting for Flutter view');
+      await new Future<Null>.delayed(new Duration(seconds: attemptSeconds));
+      await vm.refreshViews();
+    }
+  }
 }
 
 /// An error that is thrown when constructing/updating a service object.
@@ -743,9 +753,20 @@
     await vmService.vm.invokeRpc('_flutter.listViews', timeout: kLongRequestTimeout);
   }
 
-  FlutterView get mainView {
+  Iterable<FlutterView> get views => _viewCache.values;
+
+  FlutterView get firstView {
     return _viewCache.values.isEmpty ? null : _viewCache.values.first;
   }
+
+  FlutterView firstViewWithName(String isolateFilter) {
+    if (_viewCache.values.isEmpty) {
+      return null;
+    }
+    return _viewCache.values.firstWhere(
+        (FlutterView v) => v.uiIsolate.name.contains(isolateFilter),
+        orElse: () => null);
+  }
 }
 
 /// An isolate running inside the VM. Instances of the Isolate class are always