Support unpacking platform error events (#12938)

diff --git a/packages/flutter/lib/src/services/platform_channel.dart b/packages/flutter/lib/src/services/platform_channel.dart
index 941641b..8e7a1d2 100644
--- a/packages/flutter/lib/src/services/platform_channel.dart
+++ b/packages/flutter/lib/src/services/platform_channel.dart
@@ -268,7 +268,7 @@
   /// * a decoded data event (possibly null) for each successful event
   /// received from the platform plugin;
   /// * an error event containing a [PlatformException] for each error event
-  /// received from the platform plugin;
+  /// received from the platform plugin.
   ///
   /// Errors occurring during stream activation or deactivation are reported
   /// through the [FlutterError] facility. Stream activation happens only when
@@ -279,10 +279,15 @@
     StreamController<dynamic> controller;
     controller = new StreamController<dynamic>.broadcast(onListen: () async {
       BinaryMessages.setMessageHandler(name, (ByteData reply) async {
-        if (reply == null)
+        if (reply == null) {
           controller.close();
-        else
-          controller.add(codec.decodeEnvelope(reply));
+        } else {
+          try {
+            controller.add(codec.decodeEnvelope(reply));
+          } on PlatformException catch (e) {
+            controller.addError(e);
+          }
+        }
       });
       try {
         await methodChannel.invokeMethod('listen', arguments);
diff --git a/packages/flutter/test/services/platform_channel_test.dart b/packages/flutter/test/services/platform_channel_test.dart
index bde93d0..d5e845a 100644
--- a/packages/flutter/test/services/platform_channel_test.dart
+++ b/packages/flutter/test/services/platform_channel_test.dart
@@ -161,14 +161,14 @@
     const MessageCodec<dynamic> jsonMessage = const JSONMessageCodec();
     const MethodCodec jsonMethod = const JSONMethodCodec();
     const EventChannel channel = const EventChannel('ch', jsonMethod);
+    void emitEvent(dynamic event) {
+      BinaryMessages.handlePlatformMessage(
+        'ch',
+        event,
+            (ByteData reply) {},
+      );
+    }
     test('can receive event stream', () async {
-      void emitEvent(dynamic event) {
-        BinaryMessages.handlePlatformMessage(
-          'ch',
-          event,
-          (ByteData reply) {},
-        );
-      }
       bool canceled = false;
       BinaryMessages.setMockMessageHandler(
         'ch',
@@ -176,13 +176,13 @@
           final Map<dynamic, dynamic> methodCall = jsonMessage.decodeMessage(message);
           if (methodCall['method'] == 'listen') {
             final String argument = methodCall['args'];
-            emitEvent(jsonMessage.encodeMessage(<dynamic>[argument + '1']));
-            emitEvent(jsonMessage.encodeMessage(<dynamic>[argument + '2']));
+            emitEvent(jsonMethod.encodeSuccessEnvelope(argument + '1'));
+            emitEvent(jsonMethod.encodeSuccessEnvelope(argument + '2'));
             emitEvent(null);
-            return jsonMessage.encodeMessage(<dynamic>[null]);
+            return jsonMethod.encodeSuccessEnvelope(null);
           } else if (methodCall['method'] == 'cancel') {
             canceled = true;
-            return jsonMessage.encodeMessage(<dynamic>[null]);
+            return jsonMethod.encodeSuccessEnvelope(null);
           } else {
             fail('Expected listen or cancel');
           }
@@ -193,5 +193,33 @@
       await new Future<Null>.delayed(Duration.ZERO);
       expect(canceled, isTrue);
     });
+    test('can receive error event', () async {
+      BinaryMessages.setMockMessageHandler(
+        'ch',
+        (ByteData message) async {
+          final Map<dynamic, dynamic> methodCall = jsonMessage.decodeMessage(message);
+          if (methodCall['method'] == 'listen') {
+            final String argument = methodCall['args'];
+            emitEvent(jsonMethod.encodeErrorEnvelope(code: '404', message: 'Not Found.', details: argument));
+            return jsonMethod.encodeSuccessEnvelope(null);
+          } else if (methodCall['method'] == 'cancel') {
+            return jsonMethod.encodeSuccessEnvelope(null);
+          } else {
+            fail('Expected listen or cancel');
+          }
+        },
+      );
+      final List<dynamic> events = <dynamic>[];
+      final List<dynamic> errors = <dynamic>[];
+      channel.receiveBroadcastStream('hello').listen(events.add, onError: errors.add);
+      await new Future<Null>.delayed(Duration.ZERO);
+      expect(events, isEmpty);
+      expect(errors, hasLength(1));
+      expect(errors[0], const isInstanceOf<PlatformException>());
+      final PlatformException error = errors[0];
+      expect(error.code, '404');
+      expect(error.message, 'Not Found.');
+      expect(error.details, 'hello');
+    });
   });
 }