Introduce the concept of asynchronicity to the service extensions. (#7823)

This allows us, for example, to wait for the slow mode banner to have
been removed from the screen before triggering a screen shot.
diff --git a/packages/flutter_tools/lib/src/vmservice.dart b/packages/flutter_tools/lib/src/vmservice.dart
index 00ba858..a7dd705 100644
--- a/packages/flutter_tools/lib/src/vmservice.dart
+++ b/packages/flutter_tools/lib/src/vmservice.dart
@@ -34,7 +34,7 @@
   ///
   /// Requests made via the returns [VMService] time out after [requestTimeout]
   /// amount of time, which is [kDefaultRequestTimeout] by default.
-  static Future<VMService> connect(Uri httpUri, {Duration requestTimeout: kDefaultRequestTimeout}) async {
+  static Future<VMService> connect(Uri httpUri, { Duration requestTimeout: kDefaultRequestTimeout }) async {
     Uri wsUri = httpUri.replace(scheme: 'ws', path: path.join(httpUri.path, 'ws'));
     WebSocket ws;
     try {
@@ -264,7 +264,7 @@
     Map<String, dynamic> params = <String, dynamic>{
       'objectId': id,
     };
-    return _owner.isolate.invokeRpcRaw('getObject', params);
+    return _owner.isolate.invokeRpcRaw('getObject', params: params);
   }
 
   Future<ServiceObject> _inProgressReload;
@@ -437,7 +437,7 @@
 
   @override
   Future<Map<String, dynamic>> _fetchDirect() async {
-    return invokeRpcRaw('getVM', <String, dynamic> {});
+    return invokeRpcRaw('getVM');
   }
 
   @override
@@ -567,27 +567,39 @@
   }
 
   /// Invoke the RPC and return the raw response.
-  Future<Map<String, dynamic>> invokeRpcRaw(
-      String method, [Map<String, dynamic> params, Duration timeout]) async {
-    if (params == null) {
-      params = <String, dynamic>{};
-    }
-
+  ///
+  /// If `timeoutFatal` is false, then a timeout will result in a null return
+  /// value. Otherwise, it results in an exception.
+  Future<Map<String, dynamic>> invokeRpcRaw(String method, {
+    Map<String, dynamic> params: const <String, dynamic>{},
+    Duration timeout,
+    bool timeoutFatal: true,
+  }) async {
+    assert(params != null);
+    timeout ??= _vmService._requestTimeout;
     try {
       Map<String, dynamic> result = await _vmService.peer
           .sendRequest(method, params)
-          .timeout(timeout ?? _vmService._requestTimeout);
+          .timeout(timeout);
       return result;
-    } on TimeoutException catch(_) {
-      printError('Request to Dart VM Service timed out: $method($params)');
-      rethrow;
+    } on TimeoutException {
+      printTrace('Request to Dart VM Service timed out: $method($params)');
+      if (timeoutFatal)
+        throw new TimeoutException('Request to Dart VM Service timed out: $method($params)');
+      return null;
     }
   }
 
-  /// Invoke the RPC and return a ServiceObject response.
-  Future<ServiceObject> invokeRpc(
-      String method, { Map<String, dynamic> params, Duration timeout }) async {
-    Map<String, dynamic> response = await invokeRpcRaw(method, params, timeout);
+  /// Invoke the RPC and return a [ServiceObject] response.
+  Future<ServiceObject> invokeRpc(String method, {
+    Map<String, dynamic> params: const <String, dynamic>{},
+    Duration timeout,
+  }) async {
+    Map<String, dynamic> response = await invokeRpcRaw(
+      method,
+      params: params,
+      timeout: timeout,
+    );
     ServiceObject serviceObject = new ServiceObject._fromMap(this, response);
     if ((serviceObject != null) && (serviceObject._canCache)) {
       String serviceObjectId = serviceObject.id;
@@ -597,19 +609,13 @@
   }
 
   /// Create a new development file system on the device.
-  Future<Map<String, dynamic>> createDevFS(String fsName) async {
-    Map<String, dynamic> response =
-        await invokeRpcRaw('_createDevFS', <String, dynamic> {
-                           'fsName': fsName
-                         });
-    return response;
+  Future<Map<String, dynamic>> createDevFS(String fsName) {
+    return invokeRpcRaw('_createDevFS', params: <String, dynamic> { 'fsName': fsName });
   }
 
   /// List the development file system son the device.
   Future<List<String>> listDevFS() async {
-    Map<String, dynamic> response =
-        await invokeRpcRaw('_listDevFS', <String, dynamic>{});
-    return response['fsNames'];
+    return (await invokeRpcRaw('_listDevFS'))['fsNames'];
   }
 
   // Write one file into a file system.
@@ -619,36 +625,36 @@
   }) {
     assert(path != null);
     assert(fileContents != null);
-
-    return invokeRpcRaw('_writeDevFSFile', <String, dynamic> {
-      'fsName': fsName,
-      'path': path,
-      'fileContents': BASE64.encode(fileContents)
-    });
+    return invokeRpcRaw(
+      '_writeDevFSFile',
+      params: <String, dynamic>{
+        'fsName': fsName,
+        'path': path,
+        'fileContents': BASE64.encode(fileContents),
+      },
+    );
   }
 
   // Read one file from a file system.
-  Future<List<int>> readDevFSFile(String fsName, String path) {
-    return invokeRpcRaw('_readDevFSFile', <String, dynamic> {
-      'fsName': fsName,
-      'path': path
-    }).then((Map<String, dynamic> response) {
-      return BASE64.decode(response['fileContents']);
-    });
+  Future<List<int>> readDevFSFile(String fsName, String path) async {
+    Map<String, dynamic> response = await invokeRpcRaw(
+      '_readDevFSFile',
+      params: <String, dynamic>{
+        'fsName': fsName,
+        'path': path,
+      },
+    );
+    return BASE64.decode(response['fileContents']);
   }
 
   /// The complete list of a file system.
-  Future<List<String>> listDevFSFiles(String fsName) {
-    return invokeRpcRaw('_listDevFSFiles', <String, dynamic> {
-      'fsName': fsName
-    }).then((Map<String, dynamic> response) {
-      return response['files'];
-    });
+  Future<List<String>> listDevFSFiles(String fsName) async {
+    return (await invokeRpcRaw('_listDevFSFiles', params: <String, dynamic>{ 'fsName': fsName }))['files'];
   }
 
   /// Delete an existing file system.
   Future<Map<String, dynamic>> deleteDevFS(String fsName) {
-    return invokeRpcRaw('_deleteDevFS',  <String, dynamic> { 'fsName': fsName });
+    return invokeRpcRaw('_deleteDevFS', params: <String, dynamic>{ 'fsName': fsName });
   }
 
   Future<ServiceMap> runInView(String viewId,
@@ -665,20 +671,21 @@
   }
 
   Future<Map<String, dynamic>> clearVMTimeline() {
-    return invokeRpcRaw('_clearVMTimeline', <String, dynamic>{});
+    return invokeRpcRaw('_clearVMTimeline');
   }
 
-  Future<Map<String, dynamic>> setVMTimelineFlags(
-      List<String> recordedStreams) {
+  Future<Map<String, dynamic>> setVMTimelineFlags(List<String> recordedStreams) {
     assert(recordedStreams != null);
-
-    return invokeRpcRaw('_setVMTimelineFlags', <String, dynamic> {
-      'recordedStreams': recordedStreams
-    });
+    return invokeRpcRaw(
+      '_setVMTimelineFlags',
+      params: <String, dynamic>{
+        'recordedStreams': recordedStreams,
+      },
+    );
   }
 
   Future<Map<String, dynamic>> getVMTimeline() {
-    return invokeRpcRaw('_getVMTimeline', <String, dynamic> {}, kLongRequestTimeout);
+    return invokeRpcRaw('_getVMTimeline', timeout: kLongRequestTimeout);
   }
 
   Future<Null> refreshViews() async {
@@ -736,12 +743,15 @@
 
   @override
   Future<Map<String, dynamic>> _fetchDirect() {
-    return invokeRpcRaw('getIsolate', <String, dynamic>{});
+    return invokeRpcRaw('getIsolate');
   }
 
   /// Invoke the RPC and return the raw response.
-  Future<Map<String, dynamic>> invokeRpcRaw(
-      String method, [Map<String, dynamic> params, Duration timeout]) {
+  Future<Map<String, dynamic>> invokeRpcRaw(String method, {
+    Map<String, dynamic> params,
+    Duration timeout,
+    bool timeoutFatal: true,
+  }) {
     // Inject the 'isolateId' parameter.
     if (params == null) {
       params = <String, dynamic>{
@@ -750,21 +760,18 @@
     } else {
       params['isolateId'] = id;
     }
-    return vm.invokeRpcRaw(method, params, timeout);
+    return vm.invokeRpcRaw(method, params: params, timeout: timeout, timeoutFatal: timeoutFatal);
   }
 
   /// Invoke the RPC and return a ServiceObject response.
-  Future<ServiceObject> invokeRpc(
-      String method, Map<String, dynamic> params) async {
-    Map<String, dynamic> response = await invokeRpcRaw(method, params);
-    return getFromMap(response);
+  Future<ServiceObject> invokeRpc(String method, Map<String, dynamic> params) async {
+    return getFromMap(await invokeRpcRaw(method, params: params));
   }
 
   @override
   void _update(Map<String, dynamic> map, bool mapIsRef) {
-    if (mapIsRef) {
+    if (mapIsRef)
       return;
-    }
     _loaded = true;
 
     int startTimeMillis = map['startTime'];
@@ -791,7 +798,7 @@
       if (packagesPath != null) {
         arguments['packagesUri'] = packagesPath;
       }
-      Map<String, dynamic> response = await invokeRpcRaw('_reloadSources', arguments);
+      Map<String, dynamic> response = await invokeRpcRaw('_reloadSources', params: arguments);
       return response;
     } on rpc.RpcException catch(e) {
       return new Future<Map<String, dynamic>>.error(<String, dynamic>{
@@ -807,9 +814,14 @@
   // Invoke a flutter extension method, if the flutter extension is not
   // available, returns null.
   Future<Map<String, dynamic>> invokeFlutterExtensionRpcRaw(
-      String method, { Map<String, dynamic> params, Duration timeout }) async {
+    String method, {
+      Map<String, dynamic> params,
+      Duration timeout,
+      bool timeoutFatal: true,
+    }
+  ) async {
     try {
-      return await invokeRpcRaw(method, params, timeout);
+      return await invokeRpcRaw(method, params: params, timeout: timeout, timeoutFatal: timeoutFatal);
     } on rpc.RpcException catch (e) {
       // If an application is not using the framework
       if (e.code == rpc_error_code.METHOD_NOT_FOUND)
@@ -821,29 +833,42 @@
   // Debug dump extension methods.
 
   Future<Map<String, dynamic>> flutterDebugDumpApp() {
-    return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpApp');
+    return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpApp', timeout: kLongRequestTimeout);
   }
 
   Future<Map<String, dynamic>> flutterDebugDumpRenderTree() {
-    return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpRenderTree');
+    return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpRenderTree', timeout: kLongRequestTimeout);
   }
 
   Future<Map<String, dynamic>> flutterToggleDebugPaintSizeEnabled() async {
     Map<String, dynamic> state = await invokeFlutterExtensionRpcRaw('ext.flutter.debugPaint');
-    if (state != null && state.containsKey('enabled') && state['enabled'] is bool)
-      state = await invokeFlutterExtensionRpcRaw('ext.flutter.debugPaint',
-          params: <String, dynamic>{ 'enabled': !state['enabled'] });
+    if (state != null && state.containsKey('enabled') && state['enabled'] is bool) {
+      state = await invokeFlutterExtensionRpcRaw(
+        'ext.flutter.debugPaint',
+        params: <String, dynamic>{ 'enabled': !state['enabled'] },
+        timeout: const Duration(milliseconds: 150),
+        timeoutFatal: false,
+      );
+    }
     return state;
   }
 
   Future<Null> flutterDebugAllowBanner(bool show) async {
-    await invokeFlutterExtensionRpcRaw('ext.flutter.debugAllowBanner', params: <String, dynamic>{ 'enabled': show });
+    await invokeFlutterExtensionRpcRaw(
+      'ext.flutter.debugAllowBanner',
+      params: <String, dynamic>{ 'enabled': show },
+      timeout: const Duration(milliseconds: 150),
+      timeoutFatal: false,
+    );
   }
 
   // Reload related extension methods.
   Future<Map<String, dynamic>> flutterReassemble() async {
-    return await invokeFlutterExtensionRpcRaw('ext.flutter.reassemble',
-        timeout: kLongRequestTimeout);
+    return await invokeFlutterExtensionRpcRaw(
+      'ext.flutter.reassemble',
+      timeout: kLongRequestTimeout,
+      timeoutFatal: false,
+    );
   }
 
   Future<bool> flutterFrameworkPresent() async {
@@ -856,16 +881,19 @@
 
   Future<Map<String, dynamic>> flutterEvictAsset(String assetPath) async {
     return await invokeFlutterExtensionRpcRaw('ext.flutter.evict',
-        params: <String, dynamic>{
-          'value': assetPath
-        }
+      params: <String, dynamic>{
+        'value': assetPath,
+      }
     );
   }
 
   // Application control extension methods.
   Future<Map<String, dynamic>> flutterExit() async {
-    return await invokeFlutterExtensionRpcRaw('ext.flutter.exit').timeout(
-          const Duration(seconds: 2), onTimeout: () => null);
+    return await invokeFlutterExtensionRpcRaw(
+      'ext.flutter.exit',
+      timeout: const Duration(seconds: 2),
+      timeoutFatal: false,
+    );
   }
 }