// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package io.flutter.plugin.common; import io.flutter.plugin.common.StandardMessageCodec.ExposedByteArrayOutputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; /** * A {@link MethodCodec} using the Flutter standard binary encoding. * *

This codec is guaranteed to be compatible with the corresponding * StandardMethodCodec * on the Dart side. These parts of the Flutter SDK are evolved synchronously.

* *

Values supported as method arguments and result payloads are those supported by * {@link StandardMessageCodec}.

*/ public final class StandardMethodCodec implements MethodCodec { public static final StandardMethodCodec INSTANCE = new StandardMethodCodec(StandardMessageCodec.INSTANCE); private final StandardMessageCodec messageCodec; /** * Creates a new method codec based on the specified message codec. */ public StandardMethodCodec(StandardMessageCodec messageCodec) { this.messageCodec = messageCodec; } @Override public ByteBuffer encodeMethodCall(MethodCall methodCall) { final ExposedByteArrayOutputStream stream = new ExposedByteArrayOutputStream(); messageCodec.writeValue(stream, methodCall.method); messageCodec.writeValue(stream, methodCall.arguments); final ByteBuffer buffer = ByteBuffer.allocateDirect(stream.size()); buffer.put(stream.buffer(), 0, stream.size()); return buffer; } @Override public MethodCall decodeMethodCall(ByteBuffer methodCall) { methodCall.order(ByteOrder.nativeOrder()); final Object method = messageCodec.readValue(methodCall); final Object arguments = messageCodec.readValue(methodCall); if (method instanceof String && !methodCall.hasRemaining()) { return new MethodCall((String) method, arguments); } throw new IllegalArgumentException("Method call corrupted"); } @Override public ByteBuffer encodeSuccessEnvelope(Object result) { final ExposedByteArrayOutputStream stream = new ExposedByteArrayOutputStream(); stream.write(0); messageCodec.writeValue(stream, result); final ByteBuffer buffer = ByteBuffer.allocateDirect(stream.size()); buffer.put(stream.buffer(), 0, stream.size()); return buffer; } @Override public ByteBuffer encodeErrorEnvelope(String errorCode, String errorMessage, Object errorDetails) { final ExposedByteArrayOutputStream stream = new ExposedByteArrayOutputStream(); stream.write(1); messageCodec.writeValue(stream, errorCode); messageCodec.writeValue(stream, errorMessage); messageCodec.writeValue(stream, errorDetails); final ByteBuffer buffer = ByteBuffer.allocateDirect(stream.size()); buffer.put(stream.buffer(), 0, stream.size()); return buffer; } @Override public Object decodeEnvelope(ByteBuffer envelope) { envelope.order(ByteOrder.nativeOrder()); final byte flag = envelope.get(); switch (flag) { case 0: { final Object result = messageCodec.readValue(envelope); if (!envelope.hasRemaining()) { return result; } } // Falls through intentionally. case 1: { final Object code = messageCodec.readValue(envelope); final Object message = messageCodec.readValue(envelope); final Object details = messageCodec.readValue(envelope); if (code instanceof String && (message == null || message instanceof String) && !envelope.hasRemaining()) { throw new FlutterException((String) code, (String) message, details); } } } throw new IllegalArgumentException("Envelope corrupted"); } }