diff --git a/bin/jni/data_view.i b/bin/jni/data_view.i
new file mode 100644
index 0000000000000000000000000000000000000000..2debc8309143fcc0e745c7b0a211d6f8f481ee27
--- /dev/null
+++ b/bin/jni/data_view.i
@@ -0,0 +1,50 @@
+%header %{
+
+namespace jami {
+struct DataView {
+  const uint8_t* data;
+  size_t size;
+};
+struct Data {
+    std::vector<uint8_t> data;
+};
+}
+
+%}
+
+namespace jami {
+struct DataView;
+struct Data;
+
+%typemap(jni) DataView "jbyteArray"
+%typemap(jtype) DataView "byte[]"
+%typemap(jstype) DataView "byte[]"
+
+%typemap(jni) Data "jbyteArray"
+%typemap(jtype) Data "byte[]"
+%typemap(jstype) Data "byte[]"
+
+%typemap(javadirectorin) Data "$jniinput"
+%typemap(javadirectorout) DataView "$javacall"
+
+%typemap(in) Data
+%{ if(!$input) {
+     SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null string");
+     return $null;
+    }
+    jsize len = jenv->GetArrayLength($input);
+    $1.data.resize(len);
+    jenv->GetByteArrayRegion($input, 0, len, (jbyte*)$1.data.data()); %}
+
+%typemap(out) DataView {
+  jbyteArray r = jenv->NewByteArray($1.size);
+  jenv->SetByteArrayRegion(r, 0, $1.size, (const jbyte *)$1.data);
+  $result = r;
+}
+
+%typemap(javain) Data "$javainput"
+
+%typemap(javaout) DataView {
+  return $jnicall;
+}
+}
diff --git a/bin/jni/jni_interface.i b/bin/jni/jni_interface.i
index 8f2159d888d49d1d9ab1faa54d07ae81db37d910..7d532408022a83b817ae4a663b029fea3f333ea6 100644
--- a/bin/jni/jni_interface.i
+++ b/bin/jni/jni_interface.i
@@ -30,6 +30,7 @@
 %include "std_map.i";
 %include "std_vector.i";
 %include "stdint.i";
+%include "data_view.i";
 %header %{
 
 #include <android/log.h>
@@ -117,9 +118,11 @@ namespace std {
 
   public java.util.HashMap<String,String> toNativeFromUtf8() {
       java.util.HashMap<String,String> out = new java.util.HashMap<>((int)size());
-      StringVect keys = keys();
-      for (String s : keys) {
-        out.put(s, getRaw(s).toJavaString());
+      for (String s : keys()) {
+          try {
+              out.put(s, new String(getRaw(s), "utf-8"));
+          } catch (java.io.UnsupportedEncodingException e) {
+          }
       }
       return out;
   }
@@ -137,9 +140,9 @@ namespace std {
     void setRaw(const std::string& key, const vector<uint8_t>& value) {
         (*$self)[key] = std::string(value.data(), value.data()+value.size());
     }
-    std::vector<uint8_t> getRaw(const std::string& key) {
+    jami::DataView getRaw(const std::string& key) {
         auto& v = $self->at(key);
-        return {v.begin(), v.end()};
+        return {(const uint8_t*)v.data(), v.size()};
     }
 }
 
@@ -169,24 +172,19 @@ namespace std {
       dat = in.getBytes();
     }
     Blob n = new Blob();
-    n.reserve(dat.length);
-    for (int i=0; i<dat.length; i++) {
-      n.add(dat[i]);
-    }
+    n.setBytes(dat);
     return n;
   }
-  public String toJavaString() {
-    byte[] dat = new byte[(int)size()];
-    for (int i=0; i<dat.length; i++) {
-        dat[i] = (byte)get(i);
+%}
+
+%extend vector<uint8_t> {
+    jami::DataView getBytes() {
+      return {self->data(), self->size()};
     }
-    try {
-        return new String(dat, "utf-8");
-    } catch (java.io.UnsupportedEncodingException e) {
-        return "";
+    void setBytes(jami::Data data) {
+      *self = std::move(data.data);
     }
-  }
-%}
+}
 %template(Blob) vector<uint8_t>;
 %template(FloatVect) vector<float>;
 }