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 5 package io.flutter.plugin.common; 6 7 import java.nio.ByteBuffer; 8 import org.json.JSONArray; 9 import org.json.JSONException; 10 import org.json.JSONObject; 11 12 /** 13 * A {@link MethodCodec} using UTF-8 encoded JSON method calls and result envelopes. 14 * 15 * <p>This codec is guaranteed to be compatible with the corresponding 16 * <a href="https://docs.flutter.io/flutter/services/JSONMethodCodec-class.html">JSONMethodCodec</a> 17 * on the Dart side. These parts of the Flutter SDK are evolved synchronously.</p> 18 * 19 * <p>Values supported as methods arguments and result payloads are those supported by 20 * {@link JSONMessageCodec}.</p> 21 */ 22 public final class JSONMethodCodec implements MethodCodec { 23 // This codec must match the Dart codec of the same name in package flutter/services. 24 public static final JSONMethodCodec INSTANCE = new JSONMethodCodec(); 25 JSONMethodCodec()26 private JSONMethodCodec() { 27 } 28 29 @Override encodeMethodCall(MethodCall methodCall)30 public ByteBuffer encodeMethodCall(MethodCall methodCall) { 31 try { 32 final JSONObject map = new JSONObject(); 33 map.put("method", methodCall.method); 34 map.put("args", JSONUtil.wrap(methodCall.arguments)); 35 return JSONMessageCodec.INSTANCE.encodeMessage(map); 36 } catch (JSONException e) { 37 throw new IllegalArgumentException("Invalid JSON", e); 38 } 39 } 40 41 @Override decodeMethodCall(ByteBuffer message)42 public MethodCall decodeMethodCall(ByteBuffer message) { 43 try { 44 final Object json = JSONMessageCodec.INSTANCE.decodeMessage(message); 45 if (json instanceof JSONObject) { 46 final JSONObject map = (JSONObject) json; 47 final Object method = map.get("method"); 48 final Object arguments = unwrapNull(map.opt("args")); 49 if (method instanceof String) { 50 return new MethodCall((String) method, arguments); 51 } 52 } 53 throw new IllegalArgumentException("Invalid method call: " + json); 54 } catch (JSONException e) { 55 throw new IllegalArgumentException("Invalid JSON", e); 56 } 57 } 58 59 @Override encodeSuccessEnvelope(Object result)60 public ByteBuffer encodeSuccessEnvelope(Object result) { 61 return JSONMessageCodec.INSTANCE 62 .encodeMessage(new JSONArray().put(JSONUtil.wrap(result))); 63 } 64 65 @Override encodeErrorEnvelope(String errorCode, String errorMessage, Object errorDetails)66 public ByteBuffer encodeErrorEnvelope(String errorCode, String errorMessage, 67 Object errorDetails) { 68 return JSONMessageCodec.INSTANCE.encodeMessage(new JSONArray() 69 .put(errorCode) 70 .put(JSONUtil.wrap(errorMessage)) 71 .put(JSONUtil.wrap(errorDetails))); 72 } 73 74 @Override decodeEnvelope(ByteBuffer envelope)75 public Object decodeEnvelope(ByteBuffer envelope) { 76 try { 77 final Object json = JSONMessageCodec.INSTANCE.decodeMessage(envelope); 78 if (json instanceof JSONArray) { 79 final JSONArray array = (JSONArray) json; 80 if (array.length() == 1) { 81 return unwrapNull(array.opt(0)); 82 } 83 if (array.length() == 3) { 84 final Object code = array.get(0); 85 final Object message = unwrapNull(array.opt(1)); 86 final Object details = unwrapNull(array.opt(2)); 87 if (code instanceof String && (message == null || message instanceof String)) { 88 throw new FlutterException((String) code, (String) message, details); 89 } 90 } 91 } 92 throw new IllegalArgumentException("Invalid envelope: " + json); 93 } catch (JSONException e) { 94 throw new IllegalArgumentException("Invalid JSON", e); 95 } 96 } 97 unwrapNull(Object value)98 Object unwrapNull(Object value) { 99 return (value == JSONObject.NULL) ? null : value; 100 } 101 } 102