Delinting future awaits round 2 (#10790)

* round 2

* deal with null futures

* review

* review

* review

* review
diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart
index 8a688b2..7b97bf6 100644
--- a/packages/flutter_tools/lib/src/android/android_device.dart
+++ b/packages/flutter_tools/lib/src/android/android_device.dart
@@ -15,6 +15,7 @@
 import '../base/port_scanner.dart';
 import '../base/process.dart';
 import '../base/process_manager.dart';
+import '../base/utils.dart';
 import '../build_info.dart';
 import '../commands/build_apk.dart';
 import '../device.dart';
@@ -452,8 +453,10 @@
       printError('Error waiting for a debug connection: $error');
       return new LaunchResult.failed();
     } finally {
-      observatoryDiscovery.cancel();
-      diagnosticDiscovery.cancel();
+      await waitGroup<Null>(<Future<Null>>[
+        observatoryDiscovery.cancel(),
+        diagnosticDiscovery.cancel(),
+      ]);
     }
   }
 
@@ -524,8 +527,10 @@
       'shell', 'am', 'broadcast', '-a', 'io.flutter.view.DISCOVER'
     ]));
 
-    await new Future<Null>.delayed(const Duration(seconds: 1));
-    logs.cancel();
+    await waitGroup<Null>(<Future<Null>>[
+      new Future<Null>.delayed(const Duration(seconds: 1)),
+      logs.cancel(),
+    ]);
     return result;
   }
 }
diff --git a/packages/flutter_tools/lib/src/android/android_workflow.dart b/packages/flutter_tools/lib/src/android/android_workflow.dart
index 5b7f99b..902807a 100644
--- a/packages/flutter_tools/lib/src/android/android_workflow.dart
+++ b/packages/flutter_tools/lib/src/android/android_workflow.dart
@@ -11,6 +11,7 @@
 import '../base/platform.dart';
 import '../base/process.dart';
 import '../base/process_manager.dart';
+import '../base/utils.dart';
 import '../doctor.dart';
 import '../globals.dart';
 import 'android_sdk.dart';
@@ -189,9 +190,12 @@
         <String>[sdkManagerPath, '--licenses'],
         environment: sdkManagerEnv,
     );
-    stdout.addStream(process.stdout);
-    stderr.addStream(process.stderr);
-    process.stdin.addStream(stdin);
+
+    waitGroup<Null>(<Future<Null>>[
+      stdout.addStream(process.stdout),
+      stderr.addStream(process.stderr),
+      process.stdin.addStream(stdin),
+    ]);
 
     final int exitCode = await process.exitCode;
     return exitCode == 0;
diff --git a/packages/flutter_tools/lib/src/base/process.dart b/packages/flutter_tools/lib/src/base/process.dart
index 0d51386..b3b4169 100644
--- a/packages/flutter_tools/lib/src/base/process.dart
+++ b/packages/flutter_tools/lib/src/base/process.dart
@@ -9,6 +9,7 @@
 import 'file_system.dart';
 import 'io.dart';
 import 'process_manager.dart';
+import 'utils.dart';
 
 typedef String StringConverter(String string);
 
@@ -156,12 +157,15 @@
 
   // Wait for stdout to be fully processed
   // because process.exitCode may complete first causing flaky tests.
-  await Future.wait(<Future<Null>>[
+  await waitGroup<Null>(<Future<Null>>[
     stdoutSubscription.asFuture<Null>(),
     stderrSubscription.asFuture<Null>(),
   ]);
-  stdoutSubscription.cancel();
-  stderrSubscription.cancel();
+
+  await waitGroup<Null>(<Future<Null>>[
+    stdoutSubscription.cancel(),
+    stderrSubscription.cancel(),
+  ]);
 
   return await process.exitCode;
 }
diff --git a/packages/flutter_tools/lib/src/base/utils.dart b/packages/flutter_tools/lib/src/base/utils.dart
index fddf40c..af7b8b8 100644
--- a/packages/flutter_tools/lib/src/base/utils.dart
+++ b/packages/flutter_tools/lib/src/base/utils.dart
@@ -256,3 +256,14 @@
     _timer = null;
   }
 }
+
+/// Returns a [Future] that completes when all given [Future]s complete.
+///
+/// Uses [Future.wait] but removes null elements from the provided
+/// `futures` iterable first.
+///
+/// The returned [Future<List>] will be shorter than the given `futures` if
+/// it contains nulls.
+Future<List<T>> waitGroup<T>(Iterable<Future<T>> futures) {
+  return Future.wait<T>(futures.where((Future<T> future) => future != null));
+}
diff --git a/packages/flutter_tools/lib/src/commands/analyze_continuously.dart b/packages/flutter_tools/lib/src/commands/analyze_continuously.dart
index dac4347..86f1818 100644
--- a/packages/flutter_tools/lib/src/commands/analyze_continuously.dart
+++ b/packages/flutter_tools/lib/src/commands/analyze_continuously.dart
@@ -172,7 +172,8 @@
 
     printTrace('dart ${command.skip(1).join(' ')}');
     _process = await processManager.start(command);
-    _process.exitCode.whenComplete(() => _process = null);
+    // This callback hookup can't throw.
+    _process.exitCode.whenComplete(() => _process = null); // ignore: unawaited_futures
 
     final Stream<String> errorStream = _process.stderr.transform(UTF8.decoder).transform(const LineSplitter());
     errorStream.listen(printError);
diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart
index f5d997f..605876d 100644
--- a/packages/flutter_tools/lib/src/commands/daemon.dart
+++ b/packages/flutter_tools/lib/src/commands/daemon.dart
@@ -394,7 +394,9 @@
 
     if (options.debuggingEnabled) {
       connectionInfoCompleter = new Completer<DebugConnectionInfo>();
-      connectionInfoCompleter.future.then<Null>((DebugConnectionInfo info) {
+      // We don't want to wait for this future to complete and callbacks won't fail.
+      // As it just writes to stdout.
+      connectionInfoCompleter.future.then<Null>((DebugConnectionInfo info) { // ignore: unawaited_futures
         final Map<String, dynamic> params = <String, dynamic>{
           'port': info.httpUri.port,
           'wsUri': info.wsUri.toString(),
@@ -405,7 +407,9 @@
       });
     }
     final Completer<Null> appStartedCompleter = new Completer<Null>();
-    appStartedCompleter.future.then<Null>((Null value) {
+    // We don't want to wait for this future to complete and callbacks won't fail.
+    // As it just writes to stdout.
+    appStartedCompleter.future.then<Null>((Null value) { // ignore: unawaited_futures
       _sendAppEvent(app, 'started');
     });