[driver] serialize commands to plain strings

Turns out VM service only accepts flat parameter name/value pairs. It
only worked this far because vm_service_client (mistakenly) uses JSON
encoder to encode parameters.

This change separates the Command type hierarchy from that of Result and
tightens type constraints on Command object parameters.
diff --git a/packages/flutter_driver/lib/src/driver.dart b/packages/flutter_driver/lib/src/driver.dart
index 74a2386..4069f2e 100644
--- a/packages/flutter_driver/lib/src/driver.dart
+++ b/packages/flutter_driver/lib/src/driver.dart
@@ -148,9 +148,9 @@
   final VMIsolateRef _appIsolate;
 
   Future<Map<String, dynamic>> _sendCommand(Command command) async {
-    Map<String, dynamic> json = <String, dynamic>{'command': command.kind}
-      ..addAll(command.toJson());
-    return _appIsolate.invokeExtension(_kFlutterExtensionMethod, json)
+    Map<String, String> parameters = <String, String>{'command': command.kind}
+      ..addAll(command.serialize());
+    return _appIsolate.invokeExtension(_kFlutterExtensionMethod, parameters)
       .then((Map<String, dynamic> result) => result, onError: (error, stackTrace) {
         throw new DriverError(
           'Failed to fulfill ${command.runtimeType} due to remote error',
diff --git a/packages/flutter_driver/lib/src/extension.dart b/packages/flutter_driver/lib/src/extension.dart
index 9b57ab2..e99e50f 100644
--- a/packages/flutter_driver/lib/src/extension.dart
+++ b/packages/flutter_driver/lib/src/extension.dart
@@ -60,11 +60,11 @@
     };
 
     _commandDeserializers = {
-      'get_health': GetHealth.fromJson,
-      'find': Find.fromJson,
-      'tap': Tap.fromJson,
-      'get_text': GetText.fromJson,
-      'scroll': Scroll.fromJson,
+      'get_health': GetHealth.deserialize,
+      'find': Find.deserialize,
+      'tap': Tap.deserialize,
+      'get_text': GetText.deserialize,
+      'scroll': Scroll.deserialize,
     };
   }
 
diff --git a/packages/flutter_driver/lib/src/find.dart b/packages/flutter_driver/lib/src/find.dart
index 1e78815..11715eb 100644
--- a/packages/flutter_driver/lib/src/find.dart
+++ b/packages/flutter_driver/lib/src/find.dart
@@ -15,10 +15,10 @@
 
   final SearchSpecification searchSpec;
 
-  Map<String, dynamic> toJson() => searchSpec.toJson();
+  Map<String, String> serialize() => searchSpec.serialize();
 
-  static Find fromJson(Map<String, dynamic> json) {
-    return new Find(SearchSpecification.fromJson(json));
+  static Find deserialize(Map<String, String> json) {
+    return new Find(SearchSpecification.deserialize(json));
   }
 
   static _throwInvalidKeyValueType(String invalidType) {
@@ -27,20 +27,20 @@
 }
 
 /// Describes how to the driver should search for elements.
-abstract class SearchSpecification extends Message {
+abstract class SearchSpecification {
   String get searchSpecType;
 
-  static SearchSpecification fromJson(Map<String, dynamic> json) {
+  static SearchSpecification deserialize(Map<String, String> json) {
     String searchSpecType = json['searchSpecType'];
     switch(searchSpecType) {
-      case 'ByValueKey': return ByValueKey.fromJson(json);
-      case 'ByTooltipMessage': return ByTooltipMessage.fromJson(json);
-      case 'ByText': return ByText.fromJson(json);
+      case 'ByValueKey': return ByValueKey.deserialize(json);
+      case 'ByTooltipMessage': return ByTooltipMessage.deserialize(json);
+      case 'ByText': return ByText.deserialize(json);
     }
     throw new DriverError('Unsupported search specification type $searchSpecType');
   }
 
-  Map<String, dynamic> toJson() => {
+  Map<String, String> serialize() => {
     'searchSpecType': searchSpecType,
   };
 }
@@ -54,11 +54,11 @@
   /// Tooltip message text.
   final String text;
 
-  Map<String, dynamic> toJson() => super.toJson()..addAll({
+  Map<String, String> serialize() => super.serialize()..addAll({
     'text': text,
   });
 
-  static ByTooltipMessage fromJson(Map<String, dynamic> json) {
+  static ByTooltipMessage deserialize(Map<String, String> json) {
     return new ByTooltipMessage(json['text']);
   }
 }
@@ -71,11 +71,11 @@
 
   final String text;
 
-  Map<String, dynamic> toJson() => super.toJson()..addAll({
+  Map<String, String> serialize() => super.serialize()..addAll({
     'text': text,
   });
 
-  static ByText fromJson(Map<String, dynamic> json) {
+  static ByText deserialize(Map<String, String> json) {
     return new ByText(json['text']);
   }
 }
@@ -103,12 +103,12 @@
   /// May be one of "String", "int". The list of supported types may change.
   final String keyValueType;
 
-  Map<String, dynamic> toJson() => super.toJson()..addAll({
+  Map<String, String> serialize() => super.serialize()..addAll({
     'keyValueString': keyValueString,
     'keyValueType': keyValueType,
   });
 
-  static ByValueKey fromJson(Map<String, dynamic> json) {
+  static ByValueKey deserialize(Map<String, String> json) {
     String keyValueString = json['keyValueString'];
     String keyValueType = json['keyValueType'];
     switch(keyValueType) {
@@ -130,14 +130,14 @@
 class GetText extends CommandWithTarget {
   final String kind = 'get_text';
 
-  static GetText fromJson(Map<String, dynamic> json) {
+  static GetText deserialize(Map<String, String> json) {
     return new GetText(new ObjectRef(json['targetRef']));
   }
 
   /// [targetRef] identifies an element that contains a piece of text.
   GetText(ObjectRef targetRef) : super(targetRef);
 
-  Map<String, dynamic> toJson() => super.toJson();
+  Map<String, String> serialize() => super.serialize();
 }
 
 class GetTextResult extends Result {
diff --git a/packages/flutter_driver/lib/src/gesture.dart b/packages/flutter_driver/lib/src/gesture.dart
index b4927ec..4cf0339 100644
--- a/packages/flutter_driver/lib/src/gesture.dart
+++ b/packages/flutter_driver/lib/src/gesture.dart
@@ -9,11 +9,11 @@
 
   Tap(ObjectRef targetRef) : super(targetRef);
 
-  static Tap fromJson(Map<String, dynamic> json) {
+  static Tap deserialize(Map<String, String> json) {
     return new Tap(new ObjectRef(json['targetRef']));
   }
 
-  Map<String, dynamic> toJson() => super.toJson();
+  Map<String, String> serialize() => super.serialize();
 }
 
 class TapResult extends Result {
@@ -37,7 +37,7 @@
     this.frequency
   ) : super(targetRef);
 
-  static Scroll fromJson(Map<String, dynamic> json) {
+  static Scroll deserialize(Map<String, dynamic> json) {
     return new Scroll(
       new ObjectRef(json['targetRef']),
       double.parse(json['dx']),
@@ -59,11 +59,11 @@
   /// The frequency in Hz of the generated move events.
   final int frequency;
 
-  Map<String, dynamic> toJson() => super.toJson()..addAll({
-    'dx': dx,
-    'dy': dy,
-    'duration': duration.inMicroseconds,
-    'frequency': frequency,
+  Map<String, String> serialize() => super.serialize()..addAll({
+    'dx': '$dx',
+    'dy': '$dy',
+    'duration': '${duration.inMicroseconds}',
+    'frequency': '$frequency',
   });
 }
 
diff --git a/packages/flutter_driver/lib/src/health.dart b/packages/flutter_driver/lib/src/health.dart
index f9d4f59..d46a302 100644
--- a/packages/flutter_driver/lib/src/health.dart
+++ b/packages/flutter_driver/lib/src/health.dart
@@ -9,9 +9,9 @@
 class GetHealth implements Command {
   final String kind = 'get_health';
 
-  static fromJson(Map<String, dynamic> json) => new GetHealth();
+  static deserialize(Map<String, String> json) => new GetHealth();
 
-  Map<String, dynamic> toJson() => const {};
+  Map<String, String> serialize() => const {};
 }
 
 /// Application health status.
diff --git a/packages/flutter_driver/lib/src/message.dart b/packages/flutter_driver/lib/src/message.dart
index 9497ef8..7290eb7 100644
--- a/packages/flutter_driver/lib/src/message.dart
+++ b/packages/flutter_driver/lib/src/message.dart
@@ -4,23 +4,23 @@
 
 import 'error.dart';
 
-/// A piece of data travelling between Flutter Driver and a Flutter application.
-abstract class Message {
+/// An object sent from the Flutter Driver to a Flutter application to instruct
+/// the application to perform a task.
+abstract class Command {
+  /// Identifies the type of the command object and of the handler.
+  String get kind;
+
+  /// Serializes this command to parameter name/value pairs.
+  Map<String, String> serialize();
+}
+
+/// An object sent from a Flutter application back to the Flutter Driver in
+/// response to a command.
+abstract class Result {
   /// Serializes this message to a JSON map.
   Map<String, dynamic> toJson();
 }
 
-/// A message that travels from the Flutter Driver to a Flutter application to
-/// instruct the application to perform a task.
-abstract class Command extends Message {
-  /// Identifies the type of the command object and of the handler.
-  String get kind;
-}
-
-/// A message sent from a Flutter application back to the Flutter Driver in
-/// response to a command.
-abstract class Result extends Message { }
-
 /// A serializable reference to an object that lives in the application isolate.
 class ObjectRef extends Result {
   ObjectRef(this.objectReferenceKey);
@@ -47,7 +47,7 @@
 /// A command aimed at an object represented by [targetRef].
 ///
 /// Implementations must provide a concrete [kind]. If additional data is
-/// required beyond the [targetRef] the implementation may override [toJson]
+/// required beyond the [targetRef] the implementation may override [serialize]
 /// and add more keys to the returned map.
 abstract class CommandWithTarget extends Command {
   CommandWithTarget(ObjectRef ref) : this.targetRef = ref?.objectReferenceKey {
@@ -66,10 +66,10 @@
   ///
   /// Example:
   ///
-  ///     Map<String, dynamic> toJson() => super.toJson()..addAll({
+  ///     Map<String, String> toJson() => super.toJson()..addAll({
   ///       'foo': this.foo,
   ///     });
-  Map<String, dynamic> toJson() => {
+  Map<String, String> serialize() => <String, String>{
     'targetRef': targetRef,
   };
 }