Handle reassemble timeouts gracefully. (#9467)

- [x] Switch the reassemble timeout to 5 seconds.
- [x] Print a status message if reassemble fails:

```
Performing hot reload...
Reassembling app.flx$main took too long. Hot reload may have failed.
Reloaded 0 of 418 libraries in 5,322ms.
```

Fixes #9316
Fixes #8861
Fixes #8857
Fixes #8856
diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart
index 314c60e..be25e64 100644
--- a/packages/flutter_tools/lib/src/run_hot.dart
+++ b/packages/flutter_tools/lib/src/run_hot.dart
@@ -519,9 +519,15 @@
     await _evictDirtyAssets();
     printTrace('Reassembling application');
     bool reassembleAndScheduleErrors = false;
+    bool reassembleTimedOut = false;
     for (FlutterView view in reassembleViews) {
       try {
         await view.uiIsolate.flutterReassemble();
+      } on TimeoutException {
+        reassembleTimedOut = true;
+        printTrace("Reassembling ${view.uiIsolate.name} took too long. ");
+        printStatus("Hot reloading ${view.uiIsolate.name} took too long. Hot reload may have failed.");
+        continue;
       } catch (error) {
         reassembleAndScheduleErrors = true;
         printError('Reassembling ${view.uiIsolate.name} failed: $error');
@@ -548,7 +554,12 @@
       benchmarkData['hotReloadMillisecondsToFrame'] =
           reloadTimer.elapsed.inMilliseconds;
     }
-    if (shouldReportReloadTime)
+    // Only report timings if we reloaded a single view without any
+    // errors or timeouts.
+    if ((reassembleViews.length == 1) &&
+        !reassembleAndScheduleErrors &&
+        !reassembleTimedOut &&
+        shouldReportReloadTime)
       flutterUsage.sendTiming('hot', 'reload', reloadTimer.elapsed);
     return new OperationResult(
       reassembleAndScheduleErrors ? 1 : OperationResult.ok.code,
diff --git a/packages/flutter_tools/lib/src/vmservice.dart b/packages/flutter_tools/lib/src/vmservice.dart
index 4028445..e9df65d 100644
--- a/packages/flutter_tools/lib/src/vmservice.dart
+++ b/packages/flutter_tools/lib/src/vmservice.dart
@@ -35,6 +35,9 @@
 /// Used for RPC requests that may take a long time.
 const Duration kLongRequestTimeout = const Duration(minutes: 1);
 
+/// Used for RPC requests that should never take a long time.
+const Duration kShortRequestTimeout = const Duration(seconds: 5);
+
 /// A connection to the Dart VM Service.
 class VMService {
   VMService._(this._peer, this.httpAddress, this.wsAddress, this._requestTimeout) {
@@ -1005,8 +1008,8 @@
   Future<Map<String, dynamic>> flutterReassemble() async {
     return await invokeFlutterExtensionRpcRaw(
       'ext.flutter.reassemble',
-      timeout: kLongRequestTimeout,
-      timeoutFatal: false,
+      timeout: kShortRequestTimeout,
+      timeoutFatal: true,
     );
   }