1// Copyright 2013 The Flutter Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5part of engine; 6 7/// [MessageCodec] with unencoded binary messages represented using [ByteData]. 8/// 9/// On Android, messages will be represented using `java.nio.ByteBuffer`. 10/// On iOS, messages will be represented using `NSData`. 11class BinaryCodec implements MessageCodec<ByteData> { 12 /// Creates a [MessageCodec] with unencoded binary messages represented using 13 /// [ByteData]. 14 const BinaryCodec(); 15 16 @override 17 ByteData decodeMessage(ByteData message) => message; 18 19 @override 20 ByteData encodeMessage(ByteData message) => message; 21} 22 23/// [MessageCodec] with UTF-8 encoded String messages. 24/// 25/// On Android, messages will be represented using `java.util.String`. 26/// On iOS, messages will be represented using `NSString`. 27class StringCodec implements MessageCodec<String> { 28 /// Creates a [MessageCodec] with UTF-8 encoded String messages. 29 const StringCodec(); 30 31 @override 32 String decodeMessage(ByteData message) { 33 if (message == null) { 34 return null; 35 } 36 return utf8.decoder.convert(message.buffer.asUint8List()); 37 } 38 39 @override 40 ByteData encodeMessage(String message) { 41 if (message == null) { 42 return null; 43 } 44 final Uint8List encoded = utf8.encoder.convert(message); 45 return encoded.buffer.asByteData(); 46 } 47} 48 49/// [MessageCodec] with UTF-8 encoded JSON messages. 50/// 51/// Supported messages are acyclic values of these forms: 52/// 53/// * null 54/// * [bool]s 55/// * [num]s 56/// * [String]s 57/// * [List]s of supported values 58/// * [Map]s from strings to supported values 59/// 60/// On Android, messages are decoded using the `org.json` library. 61/// On iOS, messages are decoded using the `NSJSONSerialization` library. 62/// In both cases, the use of top-level simple messages (null, [bool], [num], 63/// and [String]) is supported (by the Flutter SDK). The decoded value will be 64/// null/nil for null, and identical to what would result from decoding a 65/// singleton JSON array with a Boolean, number, or string value, and then 66/// extracting its single element. 67class JSONMessageCodec implements MessageCodec<dynamic> { 68 // The codec serializes messages as defined by the JSON codec of the 69 // dart:convert package. The format used must match the Android and 70 // iOS counterparts. 71 72 /// Creates a [MessageCodec] with UTF-8 encoded JSON messages. 73 const JSONMessageCodec(); 74 75 @override 76 ByteData encodeMessage(dynamic message) { 77 if (message == null) { 78 return null; 79 } 80 return const StringCodec().encodeMessage(json.encode(message)); 81 } 82 83 @override 84 dynamic decodeMessage(ByteData message) { 85 if (message == null) { 86 return message; 87 } 88 return json.decode(const StringCodec().decodeMessage(message)); 89 } 90} 91 92/// [MethodCodec] with UTF-8 encoded JSON method calls and result envelopes. 93/// 94/// Values supported as method arguments and result payloads are those supported 95/// by [JSONMessageCodec]. 96class JSONMethodCodec implements MethodCodec { 97 // The codec serializes method calls, and result envelopes as outlined below. 98 // This format must match the Android and iOS counterparts. 99 // 100 // * Individual values are serialized as defined by the JSON codec of the 101 // dart:convert package. 102 // * Method calls are serialized as two-element maps, with the method name 103 // keyed by 'method' and the arguments keyed by 'args'. 104 // * Reply envelopes are serialized as either: 105 // * one-element lists containing the successful result as its single 106 // element, or 107 // * three-element lists containing, in order, an error code String, an 108 // error message String, and an error details value. 109 110 /// Creates a [MethodCodec] with UTF-8 encoded JSON method calls and result 111 /// envelopes. 112 const JSONMethodCodec(); 113 114 @override 115 ByteData encodeMethodCall(MethodCall call) { 116 return const JSONMessageCodec().encodeMessage(<String, dynamic>{ 117 'method': call.method, 118 'args': call.arguments, 119 }); 120 } 121 122 @override 123 MethodCall decodeMethodCall(ByteData methodCall) { 124 final dynamic decoded = const JSONMessageCodec().decodeMessage(methodCall); 125 if (decoded is! Map) { 126 throw FormatException('Expected method call Map, got $decoded'); 127 } 128 final dynamic method = decoded['method']; 129 final dynamic arguments = decoded['args']; 130 if (method is String) { 131 return MethodCall(method, arguments); 132 } 133 throw FormatException('Invalid method call: $decoded'); 134 } 135 136 @override 137 dynamic decodeEnvelope(ByteData envelope) { 138 final dynamic decoded = const JSONMessageCodec().decodeMessage(envelope); 139 if (decoded is! List) { 140 throw FormatException('Expected envelope List, got $decoded'); 141 } 142 if (decoded.length == 1) { 143 return decoded[0]; 144 } 145 if (decoded.length == 3 && 146 decoded[0] is String && 147 (decoded[1] == null || decoded[1] is String)) { 148 throw PlatformException( 149 code: decoded[0], 150 message: decoded[1], 151 details: decoded[2], 152 ); 153 } 154 throw FormatException('Invalid envelope: $decoded'); 155 } 156 157 @override 158 ByteData encodeSuccessEnvelope(dynamic result) { 159 return const JSONMessageCodec().encodeMessage(<dynamic>[result]); 160 } 161 162 @override 163 ByteData encodeErrorEnvelope( 164 {@required String code, String message, dynamic details}) { 165 assert(code != null); 166 return const JSONMessageCodec() 167 .encodeMessage(<dynamic>[code, message, details]); 168 } 169} 170 171/// [MessageCodec] using the Flutter standard binary encoding. 172/// 173/// Supported messages are acyclic values of these forms: 174/// 175/// * null 176/// * [bool]s 177/// * [num]s 178/// * [String]s 179/// * [Uint8List]s, [Int32List]s, [Int64List]s, [Float64List]s 180/// * [List]s of supported values 181/// * [Map]s from supported values to supported values 182/// 183/// Decoded values will use `List<dynamic>` and `Map<dynamic, dynamic>` 184/// irrespective of content. 185/// 186/// On Android, messages are represented as follows: 187/// 188/// * null: null 189/// * [bool]\: `java.lang.Boolean` 190/// * [int]\: `java.lang.Integer` for values that are representable using 32-bit 191/// two's complement; `java.lang.Long` otherwise 192/// * [double]\: `java.lang.Double` 193/// * [String]\: `java.lang.String` 194/// * [Uint8List]\: `byte[]` 195/// * [Int32List]\: `int[]` 196/// * [Int64List]\: `long[]` 197/// * [Float64List]\: `double[]` 198/// * [List]\: `java.util.ArrayList` 199/// * [Map]\: `java.util.HashMap` 200/// 201/// On iOS, messages are represented as follows: 202/// 203/// * null: nil 204/// * [bool]\: `NSNumber numberWithBool:` 205/// * [int]\: `NSNumber numberWithInt:` for values that are representable using 206/// 32-bit two's complement; `NSNumber numberWithLong:` otherwise 207/// * [double]\: `NSNumber numberWithDouble:` 208/// * [String]\: `NSString` 209/// * [Uint8List], [Int32List], [Int64List], [Float64List]\: 210/// `FlutterStandardTypedData` 211/// * [List]\: `NSArray` 212/// * [Map]\: `NSDictionary` 213/// 214/// The codec is extensible by subclasses overriding [writeValue] and 215/// [readValueOfType]. 216class StandardMessageCodec implements MessageCodec<dynamic> { 217 // The codec serializes messages as outlined below. This format must 218 // match the Android and iOS counterparts. 219 // 220 // * A single byte with one of the constant values below determines the 221 // type of the value. 222 // * The serialization of the value itself follows the type byte. 223 // * Numbers are represented using the host endianness throughout. 224 // * Lengths and sizes of serialized parts are encoded using an expanding 225 // format optimized for the common case of small non-negative integers: 226 // * values 0..253 inclusive using one byte with that value; 227 // * values 254..2^16 inclusive using three bytes, the first of which is 228 // 254, the next two the usual unsigned representation of the value; 229 // * values 2^16+1..2^32 inclusive using five bytes, the first of which is 230 // 255, the next four the usual unsigned representation of the value. 231 // * null, true, and false have empty serialization; they are encoded directly 232 // in the type byte (using _kNull, _kTrue, _kFalse) 233 // * Integers representable in 32 bits are encoded using 4 bytes two's 234 // complement representation. 235 // * Larger integers are encoded using 8 bytes two's complement 236 // representation. 237 // * doubles are encoded using the IEEE 754 64-bit double-precision binary 238 // format. 239 // * Strings are encoded using their UTF-8 representation. First the length 240 // of that in bytes is encoded using the expanding format, then follows the 241 // UTF-8 encoding itself. 242 // * Uint8Lists, Int32Lists, Int64Lists, and Float64Lists are encoded by first 243 // encoding the list's element count in the expanding format, then the 244 // smallest number of zero bytes needed to align the position in the full 245 // message with a multiple of the number of bytes per element, then the 246 // encoding of the list elements themselves, end-to-end with no additional 247 // type information, using two's complement or IEEE 754 as applicable. 248 // * Lists are encoded by first encoding their length in the expanding format, 249 // then follows the recursive encoding of each element value, including the 250 // type byte (Lists are assumed to be heterogeneous). 251 // * Maps are encoded by first encoding their length in the expanding format, 252 // then follows the recursive encoding of each key/value pair, including the 253 // type byte for both (Maps are assumed to be heterogeneous). 254 static const int _valueNull = 0; 255 static const int _valueTrue = 1; 256 static const int _valueFalse = 2; 257 static const int _valueInt32 = 3; 258 static const int _valueInt64 = 4; 259 static const int _valueLargeInt = 5; 260 static const int _valueFloat64 = 6; 261 static const int _valueString = 7; 262 static const int _valueUint8List = 8; 263 static const int _valueInt32List = 9; 264 static const int _valueInt64List = 10; 265 static const int _valueFloat64List = 11; 266 static const int _valueList = 12; 267 static const int _valueMap = 13; 268 269 /// Creates a [MessageCodec] using the Flutter standard binary encoding. 270 const StandardMessageCodec(); 271 272 @override 273 ByteData encodeMessage(dynamic message) { 274 if (message == null) return null; 275 final WriteBuffer buffer = WriteBuffer(); 276 writeValue(buffer, message); 277 return buffer.done(); 278 } 279 280 @override 281 dynamic decodeMessage(ByteData message) { 282 if (message == null) return null; 283 final ReadBuffer buffer = ReadBuffer(message); 284 final dynamic result = readValue(buffer); 285 if (buffer.hasRemaining) throw const FormatException('Message corrupted'); 286 return result; 287 } 288 289 /// Writes [value] to [buffer] by first writing a type discriminator 290 /// byte, then the value itself. 291 /// 292 /// This method may be called recursively to serialize container values. 293 /// 294 /// Type discriminators 0 through 127 inclusive are reserved for use by the 295 /// base class. 296 /// 297 /// The codec can be extended by overriding this method, calling super 298 /// for values that the extension does not handle. Type discriminators 299 /// used by extensions must be greater than or equal to 128 in order to avoid 300 /// clashes with any later extensions to the base class. 301 void writeValue(WriteBuffer buffer, dynamic value) { 302 if (value == null) { 303 buffer.putUint8(_valueNull); 304 } else if (value is bool) { 305 buffer.putUint8(value ? _valueTrue : _valueFalse); 306 // TODO(flutter_web): upstream double/int if/else swap. 307 } else if (value is double) { 308 buffer.putUint8(_valueFloat64); 309 buffer.putFloat64(value); 310 } else if (value is int) { 311 if (-0x7fffffff - 1 <= value && value <= 0x7fffffff) { 312 buffer.putUint8(_valueInt32); 313 buffer.putInt32(value); 314 } else { 315 buffer.putUint8(_valueInt64); 316 buffer.putInt64(value); 317 } 318 } else if (value is String) { 319 buffer.putUint8(_valueString); 320 final List<int> bytes = utf8.encoder.convert(value); 321 writeSize(buffer, bytes.length); 322 buffer.putUint8List(bytes); 323 } else if (value is Uint8List) { 324 buffer.putUint8(_valueUint8List); 325 writeSize(buffer, value.length); 326 buffer.putUint8List(value); 327 } else if (value is Int32List) { 328 buffer.putUint8(_valueInt32List); 329 writeSize(buffer, value.length); 330 buffer.putInt32List(value); 331 } else if (value is Int64List) { 332 buffer.putUint8(_valueInt64List); 333 writeSize(buffer, value.length); 334 buffer.putInt64List(value); 335 } else if (value is Float64List) { 336 buffer.putUint8(_valueFloat64List); 337 writeSize(buffer, value.length); 338 buffer.putFloat64List(value); 339 } else if (value is List) { 340 buffer.putUint8(_valueList); 341 writeSize(buffer, value.length); 342 for (final dynamic item in value) { 343 writeValue(buffer, item); 344 } 345 } else if (value is Map) { 346 buffer.putUint8(_valueMap); 347 writeSize(buffer, value.length); 348 value.forEach((dynamic key, dynamic value) { 349 writeValue(buffer, key); 350 writeValue(buffer, value); 351 }); 352 } else { 353 throw new ArgumentError.value(value); 354 } 355 } 356 357 /// Reads a value from [buffer] as written by [writeValue]. 358 /// 359 /// This method is intended for use by subclasses overriding 360 /// [readValueOfType]. 361 dynamic readValue(ReadBuffer buffer) { 362 if (!buffer.hasRemaining) throw const FormatException('Message corrupted'); 363 final int type = buffer.getUint8(); 364 return readValueOfType(type, buffer); 365 } 366 367 /// Reads a value of the indicated [type] from [buffer]. 368 /// 369 /// The codec can be extended by overriding this method, calling super 370 /// for types that the extension does not handle. 371 dynamic readValueOfType(int type, ReadBuffer buffer) { 372 dynamic result; 373 switch (type) { 374 case _valueNull: 375 result = null; 376 break; 377 case _valueTrue: 378 result = true; 379 break; 380 case _valueFalse: 381 result = false; 382 break; 383 case _valueInt32: 384 result = buffer.getInt32(); 385 break; 386 case _valueInt64: 387 result = buffer.getInt64(); 388 break; 389 case _valueLargeInt: 390 // Flutter Engine APIs to use large ints have been deprecated on 391 // 2018-01-09 and will be made unavailable. 392 // TODO(mravn): remove this case once the APIs are unavailable. 393 final int length = readSize(buffer); 394 final String hex = utf8.decoder.convert(buffer.getUint8List(length)); 395 result = int.parse(hex, radix: 16); 396 break; 397 case _valueFloat64: 398 result = buffer.getFloat64(); 399 break; 400 case _valueString: 401 final int length = readSize(buffer); 402 result = utf8.decoder.convert(buffer.getUint8List(length)); 403 break; 404 case _valueUint8List: 405 final int length = readSize(buffer); 406 result = buffer.getUint8List(length); 407 break; 408 case _valueInt32List: 409 final int length = readSize(buffer); 410 result = buffer.getInt32List(length); 411 break; 412 case _valueInt64List: 413 final int length = readSize(buffer); 414 result = buffer.getInt64List(length); 415 break; 416 case _valueFloat64List: 417 final int length = readSize(buffer); 418 result = buffer.getFloat64List(length); 419 break; 420 case _valueList: 421 final int length = readSize(buffer); 422 result = List<dynamic>(length); 423 for (int i = 0; i < length; i++) { 424 result[i] = readValue(buffer); 425 } 426 break; 427 case _valueMap: 428 final int length = readSize(buffer); 429 result = <dynamic, dynamic>{}; 430 for (int i = 0; i < length; i++) { 431 result[readValue(buffer)] = readValue(buffer); 432 } 433 break; 434 default: 435 throw const FormatException('Message corrupted'); 436 } 437 return result; 438 } 439 440 /// Writes a non-negative 32-bit integer [value] to [buffer] 441 /// using an expanding 1-5 byte encoding that optimizes for small values. 442 /// 443 /// This method is intended for use by subclasses overriding 444 /// [writeValue]. 445 void writeSize(WriteBuffer buffer, int value) { 446 assert(0 <= value && value <= 0xffffffff); 447 if (value < 254) { 448 buffer.putUint8(value); 449 } else if (value <= 0xffff) { 450 buffer.putUint8(254); 451 buffer.putUint16(value); 452 } else { 453 buffer.putUint8(255); 454 buffer.putUint32(value); 455 } 456 } 457 458 /// Reads a non-negative int from [buffer] as written by [writeSize]. 459 /// 460 /// This method is intended for use by subclasses overriding 461 /// [readValueOfType]. 462 int readSize(ReadBuffer buffer) { 463 final int value = buffer.getUint8(); 464 switch (value) { 465 case 254: 466 return buffer.getUint16(); 467 case 255: 468 return buffer.getUint32(); 469 default: 470 return value; 471 } 472 } 473} 474 475/// [MethodCodec] using the Flutter standard binary encoding. 476/// 477/// The standard codec is guaranteed to be compatible with the corresponding 478/// standard codec for FlutterMethodChannels on the host platform. These parts 479/// of the Flutter SDK are evolved synchronously. 480/// 481/// Values supported as method arguments and result payloads are those supported 482/// by [StandardMessageCodec]. 483class StandardMethodCodec implements MethodCodec { 484 // The codec method calls, and result envelopes as outlined below. This format 485 // must match the Android and iOS counterparts. 486 // 487 // * Individual values are encoded using [StandardMessageCodec]. 488 // * Method calls are encoded using the concatenation of the encoding 489 // of the method name String and the arguments value. 490 // * Reply envelopes are encoded using first a single byte to distinguish the 491 // success case (0) from the error case (1). Then follows: 492 // * In the success case, the encoding of the result value. 493 // * In the error case, the concatenation of the encoding of the error code 494 // string, the error message string, and the error details value. 495 496 /// Creates a [MethodCodec] using the Flutter standard binary encoding. 497 const StandardMethodCodec([this.messageCodec = const StandardMessageCodec()]); 498 499 /// The message codec that this method codec uses for encoding values. 500 final StandardMessageCodec messageCodec; 501 502 @override 503 ByteData encodeMethodCall(MethodCall call) { 504 final WriteBuffer buffer = WriteBuffer(); 505 messageCodec.writeValue(buffer, call.method); 506 messageCodec.writeValue(buffer, call.arguments); 507 return buffer.done(); 508 } 509 510 @override 511 MethodCall decodeMethodCall(ByteData methodCall) { 512 final ReadBuffer buffer = ReadBuffer(methodCall); 513 final dynamic method = messageCodec.readValue(buffer); 514 final dynamic arguments = messageCodec.readValue(buffer); 515 if (method is String && !buffer.hasRemaining) 516 return MethodCall(method, arguments); 517 else 518 throw const FormatException('Invalid method call'); 519 } 520 521 @override 522 ByteData encodeSuccessEnvelope(dynamic result) { 523 final WriteBuffer buffer = WriteBuffer(); 524 buffer.putUint8(0); 525 messageCodec.writeValue(buffer, result); 526 return buffer.done(); 527 } 528 529 @override 530 ByteData encodeErrorEnvelope( 531 {@required String code, String message, dynamic details}) { 532 final WriteBuffer buffer = WriteBuffer(); 533 buffer.putUint8(1); 534 messageCodec.writeValue(buffer, code); 535 messageCodec.writeValue(buffer, message); 536 messageCodec.writeValue(buffer, details); 537 return buffer.done(); 538 } 539 540 @override 541 dynamic decodeEnvelope(ByteData envelope) { 542 // First byte is zero in success case, and non-zero otherwise. 543 if (envelope.lengthInBytes == 0) 544 throw const FormatException('Expected envelope, got nothing'); 545 final ReadBuffer buffer = ReadBuffer(envelope); 546 if (buffer.getUint8() == 0) return messageCodec.readValue(buffer); 547 final dynamic errorCode = messageCodec.readValue(buffer); 548 final dynamic errorMessage = messageCodec.readValue(buffer); 549 final dynamic errorDetails = messageCodec.readValue(buffer); 550 if (errorCode is String && 551 (errorMessage == null || errorMessage is String) && 552 !buffer.hasRemaining) 553 throw PlatformException( 554 code: errorCode, message: errorMessage, details: errorDetails); 555 else 556 throw const FormatException('Invalid envelope'); 557 } 558} 559