From 57ec6fff885e25b32e749d3e63788ff010b1fdfd Mon Sep 17 00:00:00 2001
From: pedroGitt <pedro.contreiras@amdocs.com>
Date: Thu, 13 Oct 2016 19:21:01 +0200
Subject: Temp commit.. adding group session API in progress

---
 .../java/org/matrix/olm/OlmAccountTest.java        |   5 +-
 .../java/org/matrix/olm/OlmGroupTest.java          |  95 +++++++
 .../java/org/matrix/olm/OlmSessionTest.java        |   4 +-
 .../src/main/java/org/matrix/olm/OlmAccount.java   |  72 +++--
 .../org/matrix/olm/OlmInboundGroupSession.java     | 126 +++++++++
 .../org/matrix/olm/OlmOutboundGroupSession.java    | 135 ++++++++++
 .../src/main/java/org/matrix/olm/OlmSession.java   |  13 +-
 .../OlmLibSdk/olm-sdk/src/main/jni/Android.mk      |   3 +-
 .../OlmLibSdk/olm-sdk/src/main/jni/olm_account.cpp |  48 ++--
 .../OlmLibSdk/olm-sdk/src/main/jni/olm_account.h   |   7 +-
 .../src/main/jni/olm_inbound_group_session.cpp     | 262 ++++++++++++++++++
 .../src/main/jni/olm_inbound_group_session.h       |  43 +++
 .../OlmLibSdk/olm-sdk/src/main/jni/olm_jni.h       |   8 -
 .../src/main/jni/olm_outbound_group_session.cpp    | 293 +++++++++++++++++++++
 .../src/main/jni/olm_outbound_group_session.h      |  43 +++
 .../OlmLibSdk/olm-sdk/src/main/jni/olm_session.cpp |  10 +-
 .../OlmLibSdk/olm-sdk/src/main/jni/olm_session.h   |   1 +
 .../OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp |  82 +++++-
 .../OlmLibSdk/olm-sdk/src/main/jni/olm_utility.h   |   1 +
 19 files changed, 1182 insertions(+), 69 deletions(-)
 create mode 100644 java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmGroupTest.java
 create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmInboundGroupSession.java
 create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmOutboundGroupSession.java
 create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_inbound_group_session.cpp
 create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_inbound_group_session.h
 create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_outbound_group_session.cpp
 create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_outbound_group_session.h

diff --git a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java
index ec1d49c..ad875c5 100644
--- a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java
+++ b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java
@@ -152,9 +152,8 @@ public class OlmAccountTest {
         long sessionId = olmSession.getOlmSessionId();
         assertTrue(0 != sessionId);
 
-        int sessionRetCode = mOlmAccount.removeOneTimeKeysForSession(sessionId);
-        // no one time key has been use in the session, so removeOneTimeKeysForSession() returns an error
-        assertTrue(0 != sessionRetCode);
+        int sessionRetCode = mOlmAccount.removeOneTimeKeysForSession(olmSession);
+        assertTrue(0 == sessionRetCode);
 
         olmSession.releaseSession();
         sessionId = olmSession.getOlmSessionId();
diff --git a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmGroupTest.java b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmGroupTest.java
new file mode 100644
index 0000000..482ae0f
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmGroupTest.java
@@ -0,0 +1,95 @@
+package org.matrix.olm;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(AndroidJUnit4.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class OlmGroupTest {
+    private static final String LOG_TAG = "OlmSessionTest";
+
+    private static OlmManager mOlmManager;
+
+    @BeforeClass
+    public static void setUpClass(){
+        // load native lib
+        mOlmManager = new OlmManager();
+
+        String version = mOlmManager.getOlmLibVersion();
+        assertNotNull(version);
+        Log.d(LOG_TAG, "## setUpClass(): lib version="+version);
+    }
+
+    @Test
+    public void test00AliceToBob() {
+        // TBD
+    }
+
+    /**
+     * Basic test:
+     * - alice creates an account
+     * - bob creates an account
+     * - alice creates an outbound group session
+     * - bob creates an inbound group session with alice's outbound session key
+     * - alice encrypts a message with its session
+     * - bob decrypts the encrypted message with its session
+     */
+    //@Test
+    public void test01AliceToBob() {
+        // creates alice outbound session
+        OlmOutboundGroupSession aliceOutboundSession = new OlmOutboundGroupSession();
+
+        // test accounts creation
+        String aliceSessionIdentifier = aliceOutboundSession.sessionIdentifier();
+        assertNotNull(aliceSessionIdentifier);
+        assertTrue(aliceSessionIdentifier.length()>0);
+
+        String aliceOutboundSessionKey = aliceOutboundSession.sessionKey();
+        assertNotNull(aliceOutboundSessionKey);
+        assertTrue(aliceOutboundSessionKey.length()>0);
+
+        long messageIndex = aliceOutboundSession.messageIndex();
+        assertTrue(0==messageIndex);
+
+        String clearMessage = "Hello!";
+        String encryptedMessage = aliceOutboundSession.encryptMessage(clearMessage);
+        assertNotNull(encryptedMessage);
+
+        messageIndex = aliceOutboundSession.messageIndex();
+        assertTrue(1==messageIndex);
+
+        assertTrue(encryptedMessage.length()>=0);
+
+        OlmInboundGroupSession bobInboundSession = new OlmInboundGroupSession();
+        bobInboundSession.initInboundGroupSessionWithSessionKey(aliceOutboundSessionKey);
+        // check session identifiers are equals
+        aliceSessionIdentifier = aliceOutboundSession.sessionIdentifier();
+        String bobSessionIdentifier = aliceOutboundSession.sessionIdentifier();
+        assertTrue(aliceSessionIdentifier.equals(bobSessionIdentifier ));
+
+        String decryptedMessage = bobInboundSession.decryptMessage(encryptedMessage);
+        assertTrue(decryptedMessage.equals(bobSessionIdentifier ));
+    }
+
+    //@Test
+    public void test02InboundGroupSession() {
+        // creates alice outbound session
+        OlmInboundGroupSession aliceInboundSession = new OlmInboundGroupSession();
+
+        // test session identifier
+        String sessionIdentifier = aliceInboundSession.sessionIdentifier();
+        assertNotNull(sessionIdentifier);
+        assertTrue(sessionIdentifier.length()>0);
+    }
+
+}
diff --git a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSessionTest.java b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSessionTest.java
index 0b8e6b4..6056466 100644
--- a/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSessionTest.java
+++ b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSessionTest.java
@@ -105,7 +105,7 @@ public class OlmSessionTest {
         assertTrue(clearMsg.equals(decryptedMsg));
 
         // clean objects..
-        assertTrue(0==bobAccount.removeOneTimeKeysForSession(bobSession.getOlmSessionId()));
+        assertTrue(0==bobAccount.removeOneTimeKeysForSession(bobSession));
         // release accounts
         bobAccount.releaseAccount();
         aliceAccount.releaseAccount();
@@ -191,7 +191,7 @@ public class OlmSessionTest {
         // MESSAGE COMPARISON: decrypted vs encrypted
         assertTrue(helloClearMsg.equals(decryptedMsg01));
 
-        assertTrue(0==bobAccount.removeOneTimeKeysForSession(bobSession.getOlmSessionId()));
+        assertTrue(0==bobAccount.removeOneTimeKeysForSession(bobSession));
 
         // BACK/FORTH MESSAGE COMPARISON
         String clearMsg1 = "Hello I'm Bob!";
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmAccount.java b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmAccount.java
index 963f0a4..95a6eb5 100644
--- a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmAccount.java
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmAccount.java
@@ -21,7 +21,9 @@ import android.util.Log;
 import org.json.JSONException;
 import org.json.JSONObject;
 
-public class OlmAccount {
+import java.io.Serializable;
+
+public class OlmAccount implements Serializable {
     private static final String LOG_TAG = "OlmAccount";
 
     // JSON keys used in the JSON objects returned by JNI
@@ -67,7 +69,6 @@ public class OlmAccount {
 
     /**
      * Create the corresponding OLM account in native side.<br>
-     * The return value is a long casted C ptr on the OlmAccount.
      * Do not forget to call {@link #releaseAccount()} when JAVA side is done.
      * @return native account instance identifier (see {@link #mNativeOlmAccountId})
      */
@@ -87,14 +88,6 @@ public class OlmAccount {
         return retCode;
     }
 
-    /**
-     * Get the public identity keys (Ed25519 fingerprint key and Curve25519 identity key).<br>
-     * Keys are Base64 encoded.
-     * These keys must be published on the server.
-     * @return byte array containing the identity keys if operation succeed, null otherwise
-     */
-    private native byte[] identityKeysJni();
-
     /**
      * Return the identity keys (identity & fingerprint keys) in a JSON array.<br>
      * Public API for {@link #identityKeysJni()}.<br>
@@ -123,6 +116,13 @@ public class OlmAccount {
 
         return identityKeysJsonObj;
     }
+    /**
+     * Get the public identity keys (Ed25519 fingerprint key and Curve25519 identity key).<br>
+     * Keys are Base64 encoded.
+     * These keys must be published on the server.
+     * @return byte array containing the identity keys if operation succeed, null otherwise
+     */
+    private native byte[] identityKeysJni();
 
     /**
      * Return the largest number of "one time keys" this account can store.
@@ -139,15 +139,6 @@ public class OlmAccount {
      */
     public native int generateOneTimeKeys(int aNumberOfKeys);
 
-    /**
-     * Get the public parts of the unpublished "one time keys" for the account.<br>
-     * The returned data is a JSON-formatted object with the single property
-     * <tt>curve25519</tt>, which is itself an object mapping key id to
-     * base64-encoded Curve25519 key.<br>
-     * @return byte array containing the one time keys if operation succeed, null otherwise
-     */
-    private native byte[] oneTimeKeysJni();
-
     /**
      * Return the "one time keys" in a JSON array.<br>
      * The number of "one time keys", is specified by {@link #generateOneTimeKeys(int)}<br>
@@ -181,24 +172,61 @@ public class OlmAccount {
 
         return identityKeysJsonObj;
     }
+    /**
+     * Get the public parts of the unpublished "one time keys" for the account.<br>
+     * The returned data is a JSON-formatted object with the single property
+     * <tt>curve25519</tt>, which is itself an object mapping key id to
+     * base64-encoded Curve25519 key.<br>
+     * @return byte array containing the one time keys if operation succeed, null otherwise
+     */
+    private native byte[] oneTimeKeysJni();
+
+    /**
+     * Remove the "one time keys" that the session used from the account.
+     * @param aSession session instance
+     * @return 0 if operation succeed, -1 otherwise
+     */
+    public int removeOneTimeKeysForSession(OlmSession aSession) {
+        int retCode = 0;
+
+        if(null != aSession) {
+            int result = removeOneTimeKeysForSessionJni(aSession.getOlmSessionId());
+            Log.d(LOG_TAG,"## removeOneTimeKeysForSession(): result="+result);
+            if(-1 == result) {
+                retCode = -1;
+            }
+        }
 
+        return retCode;
+    }
     /**
      * Remove the "one time keys" that the session used from the account.
      * @param aNativeOlmSessionId native session instance identifier
      * @return 0 if operation succeed, 1 if no matching keys in the sessions to be removed, -1 if operation failed
      */
-    public native int removeOneTimeKeysForSession(long aNativeOlmSessionId);
+    private native int removeOneTimeKeysForSessionJni(long aNativeOlmSessionId);
 
     /**
      * Marks the current set of "one time keys" as being published.
      * @return 0 if operation succeed, -1 otherwise
      */
-    public native int markOneTimeKeysAsPublished();
+    public int markOneTimeKeysAsPublished() {
+        return markOneTimeKeysAsPublishedJni();
+    }
+    private native int markOneTimeKeysAsPublishedJni();
 
     /**
      * Sign a message with the ed25519 fingerprint key for this account.
      * @param aMessage message to sign
      * @return the signed message if operation succeed, null otherwise
      */
-    public native String signMessage(String aMessage);
+    public String signMessage(String aMessage){
+        return signMessageJni(aMessage);
+    }
+    private native String signMessageJni(String aMessage);
+
+    // TODO missing API: initWithSerializedData
+    // TODO missing API: serializeDataWithKey
+    // TODO missing API: initWithCoder
+    // TODO missing API: encodeWithCoder
 }
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmInboundGroupSession.java b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmInboundGroupSession.java
new file mode 100644
index 0000000..53ef7a3
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmInboundGroupSession.java
@@ -0,0 +1,126 @@
+/**
+ * Created by pedrocon on 13/10/2016.
+ */
+/*
+ * Copyright 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.olm;
+
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.Serializable;
+
+public class OlmInboundGroupSession implements Serializable {
+
+    private static final String LOG_TAG = "OlmInboundGroupSession";
+
+    /** session raw pointer value returned by JNI.<br>
+     * this value uniquely identifies the native inbound group session instance.
+     */
+    private long mNativeOlmInboundGroupSessionId;
+
+
+    public OlmInboundGroupSession() {
+        initNewSession();
+    }
+
+    /**
+     * Getter on the native inbound group session ID.
+     * @return native inbound group session ID
+     */
+    public long getOlmInboundGroupSessionId(){
+        return mNativeOlmInboundGroupSessionId;
+    }
+
+    /**
+     * Release native session and invalid its JAVA reference counter part.<br>
+     * Public API for {@link #releaseSessionJni()}.
+     * To be called before any other API call.
+     */
+    public void releaseSession(){
+        releaseSessionJni();
+
+        mNativeOlmInboundGroupSessionId = 0;
+    }
+
+    /**
+     * Destroy the corresponding OLM inbound group session native object.<br>
+     * This method must ALWAYS be called when this JAVA instance
+     * is destroyed (ie. garbage collected) to prevent memory leak in native side.
+     * See {@link #initNewSessionJni()}.
+     */
+    private native void releaseSessionJni();
+
+    /**
+     * Create and save the session native instance ID.
+     * Wrapper for {@link #initNewSessionJni()}.<br>
+     * To be called before any other API call.
+     * @return true if init succeed, false otherwise.
+     */
+    private boolean initNewSession() {
+        boolean retCode = false;
+        if(0 != (mNativeOlmInboundGroupSessionId = initNewSessionJni())){
+            retCode = true;
+        }
+        return retCode;
+    }
+
+    /**
+     * Create the corresponding OLM inbound group session in native side.<br>
+     * Do not forget to call {@link #releaseSession()} when JAVA side is done.
+     * @return native session instance identifier (see {@link #mNativeOlmInboundGroupSessionId})
+     */
+    private native long initNewSessionJni();
+
+    /**
+     * Creates a new inbound group session.<br>
+     * The session key parameter is retrieved from a outbound group session.
+     * @param aSessionKey session key
+     * @return 0 if operation succeed, -1 otherwise
+     */
+    public int initInboundGroupSessionWithSessionKey(String aSessionKey) {
+        int retCode = -1;
+
+        if(TextUtils.isEmpty(aSessionKey)){
+            Log.e(LOG_TAG, "## initInboundGroupSessionWithSessionKey(): invalid session key");
+        } else {
+            retCode = initInboundGroupSessionWithSessionKeyJni(aSessionKey);
+        }
+
+        return retCode;
+    }
+    private native int initInboundGroupSessionWithSessionKeyJni(String aSessionKey);
+
+
+    public String sessionIdentifier() {
+        return sessionIdentifierJni();
+    }
+    private native String sessionIdentifierJni();
+
+
+    public String decryptMessage(String aEncryptedMsg) {
+        return decryptMessageJni(aEncryptedMsg);
+    }
+    private native String decryptMessageJni(String aEncryptedMsg);
+
+
+    // TODO missing API: initWithSerializedData
+    // TODO missing API: serializeDataWithKey
+    // TODO missing API: initWithCoder
+    // TODO missing API: encodeWithCoder
+}
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmOutboundGroupSession.java b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmOutboundGroupSession.java
new file mode 100644
index 0000000..63c0c36
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmOutboundGroupSession.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.olm;
+
+
+import android.text.TextUtils;
+import android.util.Log;
+
+public class OlmOutboundGroupSession {
+    private static final String LOG_TAG = "OlmOutboundGroupSession";
+
+    /** session raw pointer value returned by JNI.<br>
+     * this value uniquely identifies the native inbound group session instance.
+     */
+    private long mNativeOlmOutboundGroupSessionId;
+
+    public OlmOutboundGroupSession() {
+        initNewSession();
+    }
+
+    /**
+     * Getter on the native outbound group session ID.
+     * @return native outbound group session ID
+     */
+    public long getOlmInboundGroupSessionId(){
+        return mNativeOlmInboundGroupSessionId;
+    }
+
+    /**
+     * Release native session and invalid its JAVA reference counter part.<br>
+     * Public API for {@link #releaseSessionJni()}.
+     * To be called before any other API call.
+     */
+    public void releaseSession(){
+        releaseSessionJni();
+
+        mNativeOlmOutboundGroupSessionId = 0;
+    }
+
+    /**
+     * Destroy the corresponding OLM outbound group session native object.<br>
+     * This method must ALWAYS be called when this JAVA instance
+     * is destroyed (ie. garbage collected) to prevent memory leak in native side.
+     * See {@link #initNewSessionJni()}.
+     */
+    private native void releaseSessionJni();
+
+    /**
+     * Create and save the session native instance ID.
+     * Wrapper for {@link #initNewSessionJni()}.<br>
+     * To be called before any other API call.
+     * @return true if init succeed, false otherwise.
+     */
+    private boolean initNewSession() {
+        boolean retCode = false;
+        if(0 != (mNativeOlmOutboundGroupSessionId = initNewSessionJni())){
+            retCode = true;
+        }
+        return retCode;
+    }
+
+    /**
+     * Create the corresponding OLM outbound group session in native side.<br>
+     * Do not forget to call {@link #releaseSession()} when JAVA side is done.
+     * @return native session instance identifier (see {@link #mNativeOlmOutboundGroupSessionId})
+     */
+    private native long initNewSessionJni();
+
+
+    /**
+     * Creates a new outbound group session.<br>
+     * The session key parameter is retrieved from a outbound group session.
+     * @return 0 if operation succeed, -1 otherwise
+     */
+    public int initOutboundGroupSession() {
+        return initOutboundGroupSessionJni();
+    }
+    public native int initOutboundGroupSessionJni();
+
+
+
+
+    public String sessionIdentifier() {
+        String retValue = null;
+        //retValue = sessionIdentifierJni();
+
+        return retValue;
+    }
+    public native String sessionIdentifierJni();
+
+
+
+
+    public long messageIndex() {
+        long retValue =0;
+        //retValue = messageIndexJni();
+
+        return retValue;
+    }
+    private native long messageIndexJni();
+
+
+
+
+    public String sessionKey() {
+        String retValue = null;
+        //retValue = sessionKeyJni();
+
+        return retValue;
+    }
+    private native String sessionKeyJni();
+
+
+    public String encryptMessage(String aClearMsg) {
+        String retValue = null;
+        //retValue = encryptMessageJni(aClearMsg);
+
+        return retValue;
+    }
+    private native String encryptMessageJni(String aClearMsg);
+}
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmSession.java b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmSession.java
index fc1f76f..2df5ea7 100644
--- a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmSession.java
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmSession.java
@@ -19,7 +19,9 @@ package org.matrix.olm;
 import android.text.TextUtils;
 import android.util.Log;
 
-public class OlmSession {
+import java.io.Serializable;
+
+public class OlmSession implements Serializable {
     private static final String LOG_TAG = "OlmSession";
 
     /** session raw pointer value (OlmSession*) returned by JNI.
@@ -85,7 +87,6 @@ public class OlmSession {
 
     /**
      * Create the corresponding OLM session in native side.<br>
-     * The return value is a long casted C ptr on the OlmSession.
      * Do not forget to call {@link #releaseSession()} when JAVA side is done.
      * @return native session instance identifier (see {@link #mNativeOlmSessionId})
      */
@@ -159,6 +160,7 @@ public class OlmSession {
      * @param aTheirIdentityKey the sender identity key
      * @param aOneTimeKeyMsg PRE KEY message
      * @return this if operation succeed, null otherwise
+     * TODO unit test missing: initInboundSessionWithAccountFrom
      */
     public OlmSession initInboundSessionWithAccountFrom(OlmAccount aAccount, String aTheirIdentityKey, String aOneTimeKeyMsg) {
         OlmSession retObj=null;
@@ -198,6 +200,7 @@ public class OlmSession {
      * Public API for {@link #matchesInboundSessionJni(String)}.
      * @param aOneTimeKeyMsg PRE KEY message
      * @return this if operation succeed, null otherwise
+     * TODO unit test missing: matchesInboundSession
      */
     public boolean matchesInboundSession(String aOneTimeKeyMsg) {
         boolean retCode = false;
@@ -218,6 +221,7 @@ public class OlmSession {
      * @param aTheirIdentityKey the sender identity key
      * @param aOneTimeKeyMsg PRE KEY message
      * @return this if operation succeed, null otherwise
+     * TODO unit test missing: matchesInboundSessionFrom
      */
     public boolean matchesInboundSessionFrom(String aTheirIdentityKey, String aOneTimeKeyMsg) {
         boolean retCode = false;
@@ -261,5 +265,10 @@ public class OlmSession {
     }
 
     private native String decryptMessageJni(OlmMessage aEncryptedMsg);
+
+    // TODO missing API: initWithSerializedData
+    // TODO missing API: serializeDataWithKey
+    // TODO missing API: initWithCoder
+    // TODO missing API: encodeWithCoder
 }
 
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/Android.mk b/java/android/OlmLibSdk/olm-sdk/src/main/jni/Android.mk
index 26a6a90..01c0dc9 100644
--- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/Android.mk
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/Android.mk
@@ -45,7 +45,8 @@ $(SRC_ROOT_DIR)/lib/crypto-algorithms/aes.c \
 $(SRC_ROOT_DIR)/lib/curve25519-donna/curve25519-donna.c \
 olm_account.cpp \
 olm_session.cpp \
-olm_utility.cpp
+olm_utility.cpp \
+olm_inbound_group_session.cpp
 
 LOCAL_LDLIBS := -llog
 
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.cpp b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.cpp
index 2a5ab6f..ba15c13 100644
--- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.cpp
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.cpp
@@ -19,7 +19,7 @@
 
 /**
 * Init memory allocation for account creation.
-* @return valid memory alocation, NULL otherwise
+* @return valid memory allocation, NULL otherwise
 **/
 OlmAccount* initializeAccountMemory()
 {
@@ -68,7 +68,7 @@ JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(releaseAccountJni)(JNIEnv *env, jobject thiz
 /**
 * Initialize a new account and return it to JAVA side.<br>
 * Since a C prt is returned as a jlong, special care will be taken
-* to make the cast (OlmAccount* => jlong) platform independant.
+* to make the cast (OlmAccount* => jlong) platform independent.
 * @return the initialized OlmAccount* instance if init succeed, NULL otherwise
 **/
 JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(initNewAccountJni)(JNIEnv *env, jobject thiz)
@@ -308,7 +308,7 @@ JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(oneTimeKeysJni)(JNIEnv *env, jobject t
  * @param aNativeOlmSessionId session instance
  * @return ERROR_CODE_OK if operation succeed, ERROR_CODE_NO_MATCHING_ONE_TIME_KEYS if no matching keys, ERROR_CODE_KO otherwise
 **/
-JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(removeOneTimeKeysForSession)(JNIEnv *env, jobject thiz, jlong aNativeOlmSessionId)
+JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(removeOneTimeKeysForSessionJni)(JNIEnv *env, jobject thiz, jlong aNativeOlmSessionId)
 {
     jint retCode = ERROR_CODE_KO;
     OlmAccount* accountPtr = NULL;
@@ -317,11 +317,11 @@ JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(removeOneTimeKeysForSession)(JNIEnv *env, jo
 
     if(NULL == sessionPtr)
     {
-        LOGE("## removeOneTimeKeysForSession(): failure - invalid session ptr");
+        LOGE("## removeOneTimeKeysForSessionJni(): failure - invalid session ptr");
     }
     else if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz)))
     {
-        LOGE("## removeOneTimeKeysForSession(): failure - invalid account ptr");
+        LOGE("## removeOneTimeKeysForSessionJni(): failure - invalid account ptr");
     }
     else
     {
@@ -329,14 +329,14 @@ JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(removeOneTimeKeysForSession)(JNIEnv *env, jo
         if(result == olm_error())
         {   // the account doesn't have any matching "one time keys"..
             const char *errorMsgPtr = olm_account_last_error(accountPtr);
-            LOGW("## removeOneTimeKeysForSession(): failure - removing one time keys Msg=%s",errorMsgPtr);
+            LOGW("## removeOneTimeKeysForSessionJni(): failure - removing one time keys Msg=%s",errorMsgPtr);
 
             retCode = ERROR_CODE_NO_MATCHING_ONE_TIME_KEYS;
         }
         else
         {
             retCode = ERROR_CODE_OK;
-            LOGD("## removeOneTimeKeysForSession(): success");
+            LOGD("## removeOneTimeKeysForSessionJni(): success");
         }
     }
 
@@ -347,7 +347,7 @@ JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(removeOneTimeKeysForSession)(JNIEnv *env, jo
  * Mark the current set of "one time keys" as being published.
  * @return ERROR_CODE_OK if operation succeed, ERROR_CODE_KO otherwise
 **/
-JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(markOneTimeKeysAsPublished)(JNIEnv *env, jobject thiz)
+JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(markOneTimeKeysAsPublishedJni)(JNIEnv *env, jobject thiz)
 {
     jint retCode = ERROR_CODE_OK;
     OlmAccount* accountPtr = NULL;
@@ -355,7 +355,7 @@ JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(markOneTimeKeysAsPublished)(JNIEnv *env, job
 
     if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz)))
     {
-        LOGE("## markOneTimeKeysPublished(): failure - invalid account ptr");
+        LOGE("## markOneTimeKeysAsPublishedJni(): failure - invalid account ptr");
         retCode = ERROR_CODE_KO;
     }
     else
@@ -364,12 +364,12 @@ JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(markOneTimeKeysAsPublished)(JNIEnv *env, job
         if(result == olm_error())
         {
             const char *errorMsgPtr = olm_account_last_error(accountPtr);
-            LOGW("## markOneTimeKeysPublished(): failure - Msg=%s",errorMsgPtr);
+            LOGW("## markOneTimeKeysAsPublishedJni(): failure - Msg=%s",errorMsgPtr);
             retCode = ERROR_CODE_KO;
         }
         else
         {
-            LOGD("## markOneTimeKeysPublished(): success - retCode=%ld",result);
+            LOGD("## markOneTimeKeysAsPublishedJni(): success - retCode=%ld",result);
         }
     }
 
@@ -382,7 +382,7 @@ JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(markOneTimeKeysAsPublished)(JNIEnv *env, job
  * @param aMessage message to sign
  * @return the signed message, null otherwise
 **/
-JNIEXPORT jstring OLM_ACCOUNT_FUNC_DEF(signMessage)(JNIEnv *env, jobject thiz, jstring aMessage)
+JNIEXPORT jstring OLM_ACCOUNT_FUNC_DEF(signMessageJni)(JNIEnv *env, jobject thiz, jstring aMessage)
 {
     OlmAccount* accountPtr = NULL;
     size_t signatureLength;
@@ -392,11 +392,11 @@ JNIEXPORT jstring OLM_ACCOUNT_FUNC_DEF(signMessage)(JNIEnv *env, jobject thiz, j
 
     if(NULL == aMessage)
     {
-        LOGE("## signMessage(): failure - invalid aMessage param");
+        LOGE("## signMessageJni(): failure - invalid aMessage param");
     }
     else if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz)))
     {
-        LOGE("## signMessage(): failure - invalid account ptr");
+        LOGE("## signMessageJni(): failure - invalid account ptr");
     }
     else
     {
@@ -404,7 +404,7 @@ JNIEXPORT jstring OLM_ACCOUNT_FUNC_DEF(signMessage)(JNIEnv *env, jobject thiz, j
         const char* messageToSign = env->GetStringUTFChars(aMessage, 0);
         if(NULL == messageToSign)
         {
-            LOGE("## signMessage(): failure - message JNI allocation OOM");
+            LOGE("## signMessageJni(): failure - message JNI allocation OOM");
         }
         else
         {
@@ -414,22 +414,26 @@ JNIEXPORT jstring OLM_ACCOUNT_FUNC_DEF(signMessage)(JNIEnv *env, jobject thiz, j
             signatureLength = olm_account_signature_length(accountPtr);
             if(NULL == (signedMsgPtr = (void*)malloc(signatureLength*sizeof(uint8_t))))
             {
-                LOGE("## signMessage(): failure - signature allocation OOM");
+                LOGE("## signMessageJni(): failure - signature allocation OOM");
             }
             else
             {   // sign message
-                resultSign = olm_account_sign(accountPtr, (void*)messageToSign, (size_t)messageLength, signedMsgPtr, signatureLength);
+                resultSign = olm_account_sign(accountPtr,
+                                             (void*)messageToSign,
+                                             (size_t)messageLength,
+                                             signedMsgPtr,
+                                             signatureLength);
                 if(resultSign == olm_error())
                 {
                     const char *errorMsgPtr = olm_account_last_error(accountPtr);
-                    LOGE("## signMessage(): failure - error signing message Msg=%s",errorMsgPtr);
+                    LOGE("## signMessageJni(): failure - error signing message Msg=%s",errorMsgPtr);
                 }
                 else
-                {   // convert to jstring
-                    // TODO check how UTF conversion can impact the content?
-                    // why not consider return jbyteArray? and convert in JAVA side..
+                {
+                    // TODO check if signedMsgPtr needs to be null ended: signedMsgPtr[resultSign]='\0'
+                    // convert to jstring
                     signedMsgRetValue = env->NewStringUTF((const char*)signedMsgPtr); // UTF8
-                    LOGD("## signMessage(): success - retCode=%ld",resultSign);
+                    LOGD("## signMessageJni(): success - retCode=%ld",resultSign);
                 }
 
                 free(signedMsgPtr);
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.h b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.h
index 23c9687..63ec3ef 100644
--- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.h
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.h
@@ -18,6 +18,7 @@
 #define _OMLACCOUNT_H
 
 #include "olm_jni.h"
+#include "olm/olm.h"
 
 #define OLM_ACCOUNT_FUNC_DEF(func_name) FUNC_DEF(OlmAccount,func_name)
 #define OLM_MANAGER_FUNC_DEF(func_name) FUNC_DEF(OlmManager,func_name)
@@ -39,11 +40,11 @@ JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(identityKeysJni)(JNIEnv *env, jobject
 JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(oneTimeKeysJni)(JNIEnv *env, jobject thiz);
 JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(maxOneTimeKeys)(JNIEnv *env, jobject thiz);
 JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(generateOneTimeKeys)(JNIEnv *env, jobject thiz, jint aNumberOfKeys);
-JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(removeOneTimeKeysForSession)(JNIEnv *env, jobject thiz, jlong aNativeOlmSessionId);
-JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(markOneTimeKeysAsPublished)(JNIEnv *env, jobject thiz);
+JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(removeOneTimeKeysForSessionJni)(JNIEnv *env, jobject thiz, jlong aNativeOlmSessionId);
+JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(markOneTimeKeysAsPublishedJni)(JNIEnv *env, jobject thiz);
 
 // signing
-JNIEXPORT jstring OLM_ACCOUNT_FUNC_DEF(signMessage)(JNIEnv *env, jobject thiz, jstring aMessage);
+JNIEXPORT jstring OLM_ACCOUNT_FUNC_DEF(signMessageJni)(JNIEnv *env, jobject thiz, jstring aMessage);
 
 #ifdef __cplusplus
 }
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_inbound_group_session.cpp b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_inbound_group_session.cpp
new file mode 100644
index 0000000..43e9e20
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_inbound_group_session.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "olm_inbound_group_session.h"
+#include "olm_utility.h"
+
+
+/**
+ * Release the session allocation made by initializeInboundGroupSessionMemory().<br>
+ * This method MUST be called when java counter part account instance is done.
+ *
+ */
+JNIEXPORT void OLM_INBOUND_GROUP_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz)
+{
+  OlmInboundGroupSession* sessionPtr = NULL;
+
+  LOGD("## releaseSessionJni(): sessionPtr=%p",sessionPtr);
+
+  if(NULL == (sessionPtr = (OlmInboundGroupSession*)getInboundGroupSessionInstanceId(env,thiz)))
+  {
+      LOGE("## releaseSessionJni(): failure - invalid inbound group session instance");
+  }
+  else
+  {
+    size_t retCode = olm_clear_inbound_group_session(sessionPtr);
+    LOGD("## releaseSessionJni(): clear_inbound_group_session=%lu",retCode);
+
+    LOGD("## releaseSessionJni(): IN");
+    free(sessionPtr);
+    LOGD("## releaseSessionJni(): OUT");
+  }
+}
+
+/**
+* Initialize a new inbound group session and return it to JAVA side.<br>
+* Since a C prt is returned as a jlong, special care will be taken
+* to make the cast (OlmInboundGroupSession* => jlong) platform independent.
+* @return the initialized OlmInboundGroupSession* instance if init succeed, NULL otherwise
+**/
+JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(initNewSessionJni)(JNIEnv *env, jobject thiz)
+{
+    OlmInboundGroupSession* sessionPtr = NULL;
+    size_t sessionSize = olm_inbound_group_session_size();
+
+    if(0 == sessionSize)
+    {
+        LOGE("## initNewSessionJni(): failure - inbound group session size = 0");
+    }
+    else if(NULL != (sessionPtr=(OlmInboundGroupSession*)malloc(sessionSize)))
+    {
+      sessionPtr = olm_inbound_group_session(sessionPtr);
+      LOGD("## initNewSessionJni(): success - inbound group session size=%lu",sessionSize);
+    }
+    else
+    {
+      LOGE("## initNewSessionJni(): failure - inbound group session OOM");
+    }
+
+    return (jlong)(intptr_t)sessionPtr;
+}
+
+/**
+ * Create a new in-bound session.<br>
+ * @param aSessionKey session key from an outbound session
+ * @return ERROR_CODE_OK if operation succeed, ERROR_CODE_KO otherwise
+ */
+JNIEXPORT jint OLM_INBOUND_GROUP_SESSION_FUNC_DEF(initInboundGroupSessionWithSessionKeyJni)(JNIEnv *env, jobject thiz, jstring aSessionKey)
+{
+    jint retCode = ERROR_CODE_KO;
+    OlmInboundGroupSession *sessionPtr = NULL;
+    const uint8_t *sessionKeyPtr = NULL;
+    size_t sessionResult;
+
+    if(NULL == (sessionPtr = (OlmInboundGroupSession*)getInboundGroupSessionInstanceId(env,thiz)))
+    {
+        LOGE("## initInboundGroupSessionWithSessionKeyJni(): failure - invalid inbound group session instance");
+    }
+    else if(0 == aSessionKey)
+    {
+        LOGE("## initInboundGroupSessionWithSessionKeyJni(): failure - invalid aSessionKey");
+    }
+    else if(NULL == (sessionKeyPtr = (const uint8_t *)env->GetStringUTFChars(aSessionKey, 0)))
+    {
+        LOGE("## initInboundSessionFromIdKeyJni(): failure - session key JNI allocation OOM");
+    }
+    else
+    {
+        size_t sessionKeyLength = (size_t)env->GetStringUTFLength(aSessionKey);
+        LOGD("## initInboundSessionFromIdKeyJni(): sessionKeyLength=%lu",sessionKeyLength);
+
+        sessionResult = olm_init_inbound_group_session(sessionPtr, sessionKeyPtr, sessionKeyLength);
+        if(sessionResult == olm_error()) {
+            const char *errorMsgPtr = olm_inbound_group_session_last_error(sessionPtr);
+            LOGE("## initInboundSessionFromIdKeyJni(): failure - init inbound session creation  Msg=%s",errorMsgPtr);
+        }
+        else
+        {
+            retCode = ERROR_CODE_OK;
+            LOGD("## initInboundSessionFromIdKeyJni(): success - result=%lu", sessionResult);
+        }
+     }
+
+     // free local alloc
+     if(NULL!= sessionKeyPtr)
+     {
+         env->ReleaseStringUTFChars(aSessionKey, (const char*)sessionKeyPtr);
+     }
+
+    return retCode;
+}
+
+
+JNIEXPORT jstring OLM_INBOUND_GROUP_SESSION_FUNC_DEF(sessionIdentifierJni)(JNIEnv *env, jobject thiz)
+{
+    OlmInboundGroupSession *sessionPtr = NULL;
+    uint8_t *sessionIdPtr = NULL;
+    jstring returnValueStr=0;
+
+    // get the size to alloc to contain the id
+    size_t lengthSessionId = olm_inbound_group_session_id_length(sessionPtr);
+
+    if(NULL == (sessionPtr = (OlmInboundGroupSession*)getInboundGroupSessionInstanceId(env,thiz)))
+    {
+        LOGE("## sessionIdentifierJni(): failure - invalid inbound group session instance");
+    }
+    else if(NULL == (sessionIdPtr = (uint8_t*)malloc(lengthSessionId*sizeof(uint8_t))))
+    {
+       LOGE("## sessionIdentifierJni(): failure - identifier allocation OOM");
+    }
+    else
+    {
+        size_t result = olm_inbound_group_session_id(sessionPtr, sessionIdPtr, lengthSessionId);
+        if (result == olm_error())
+        {
+            const char *errorMsgPtr = olm_inbound_group_session_last_error(sessionPtr);
+            LOGE("## sessionIdentifierJni(): failure - get session identifier failure Msg=%s",errorMsgPtr);
+        }
+        else
+        {
+            // update length
+            sessionIdPtr[result] = static_cast<char>('\0');
+
+            LOGD("## sessionIdentifierJni(): success - result=%lu sessionId=%s",result, (char*)sessionIdPtr);
+            returnValueStr = env->NewStringUTF((const char*)sessionIdPtr);
+        }
+        free(sessionIdPtr);
+    }
+
+    return returnValueStr;
+}
+
+JNIEXPORT jstring OLM_INBOUND_GROUP_SESSION_FUNC_DEF(decryptMessageJni)(JNIEnv *env, jobject thiz, jstring aEncryptedMsg)
+{
+    jstring decryptedMsgRetValue = 0;
+    OlmInboundGroupSession *sessionPtr = NULL;
+    const char *encryptedMsgPtr = NULL;
+    uint8_t *plainTextMsgPtr = NULL;
+    uint8_t *tempEncryptedPtr = NULL;
+
+    LOGD("## decryptMessageJni(): IN");
+
+    if(NULL == (sessionPtr = (OlmInboundGroupSession*)getInboundGroupSessionInstanceId(env,thiz)))
+    {
+        LOGE("##  decryptMessageJni(): failure - invalid inbound group session ptr=NULL");
+    }
+    else if(0 == aEncryptedMsg)
+    {
+        LOGE("##  decryptMessageJni(): failure - invalid clear message");
+    }
+    else if(0 == (encryptedMsgPtr = env->GetStringUTFChars(aEncryptedMsg, 0)))
+    {
+        LOGE("##  decryptMessageJni(): failure - encrypted message JNI allocation OOM");
+    }
+    else
+    {
+        // get encrypted message length
+        size_t encryptedMsgLength = (size_t)env->GetStringUTFLength(aEncryptedMsg);
+
+        // create a dedicated temp buffer to be used in next Olm API calls
+        if(NULL == (tempEncryptedPtr = static_cast<uint8_t*>(malloc(encryptedMsgLength*sizeof(uint8_t)))))
+        {
+            LOGE("##  decryptMessageJni(): failure - tempEncryptedPtr allocation OOM");
+        }
+        else
+        {
+            memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength);
+            LOGD("##  decryptMessageJni(): encryptedMsgLength=%lu encryptedMsg=%s",encryptedMsgLength,encryptedMsgPtr);
+
+            // get max plaintext length
+            size_t maxPlainTextLength = olm_group_decrypt_max_plaintext_length(sessionPtr,
+                                                                               tempEncryptedPtr,
+                                                                               encryptedMsgLength);
+            if(maxPlainTextLength == olm_error())
+            {
+                const char *errorMsgPtr = olm_inbound_group_session_last_error(sessionPtr);
+                LOGE("##  decryptMessageJni(): failure - olm_group_decrypt_max_plaintext_length Msg=%s",errorMsgPtr);
+            }
+            else
+            {
+                LOGD("##  decryptMessageJni(): maxPlaintextLength=%lu",maxPlainTextLength);
+
+                // allocate output decrypted message
+                plainTextMsgPtr = static_cast<uint8_t*>(malloc(maxPlainTextLength*sizeof(uint8_t)));
+
+                // decrypt, but before reload encrypted buffer (previous one was destroyed)
+                memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength);
+                size_t plaintextLength = olm_group_decrypt(sessionPtr,
+                                                           tempEncryptedPtr,
+                                                           encryptedMsgLength,
+                                                           plainTextMsgPtr,
+                                                           maxPlainTextLength);
+                if(plaintextLength == olm_error())
+                {
+                    const char *errorMsgPtr = olm_inbound_group_session_last_error(sessionPtr);
+                    LOGE("##  decryptMessageJni(): failure - olm_group_decrypt Msg=%s",errorMsgPtr);
+                }
+                else
+                {
+                    // update decrypted buffer size
+                    plainTextMsgPtr[plaintextLength] = static_cast<char>('\0');
+
+                    LOGD("##  decryptMessageJni(): decrypted returnedLg=%lu plainTextMsgPtr=%s",plaintextLength, (char*)plainTextMsgPtr);
+                    decryptedMsgRetValue = env->NewStringUTF((const char*)plainTextMsgPtr);
+                }
+            }
+        }
+    }
+
+    // free alloc
+    if(NULL != encryptedMsgPtr)
+    {
+        env->ReleaseStringUTFChars(aEncryptedMsg, encryptedMsgPtr);
+    }
+
+    if(NULL != tempEncryptedPtr)
+    {
+        free(tempEncryptedPtr);
+    }
+
+    if(NULL != plainTextMsgPtr)
+    {
+        free(plainTextMsgPtr);
+    }
+
+    return decryptedMsgRetValue;
+}
+
+
+
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_inbound_group_session.h b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_inbound_group_session.h
new file mode 100644
index 0000000..1eb8238
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_inbound_group_session.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _OMLINBOUND_GROUP_SESSION_H
+#define _OMLINBOUND_GROUP_SESSION_H
+
+#include "olm_jni.h"
+#include "olm/olm.h"
+#include "olm/inbound_group_session.h"
+
+#define OLM_INBOUND_GROUP_SESSION_FUNC_DEF(func_name) FUNC_DEF(OlmInboundGroupSession,func_name)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// session creation/destruction
+JNIEXPORT void OLM_INBOUND_GROUP_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz);
+JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(initNewSessionJni)(JNIEnv *env, jobject thiz);
+
+JNIEXPORT jint OLM_INBOUND_GROUP_SESSION_FUNC_DEF(initInboundGroupSessionWithSessionKeyJni)(JNIEnv *env, jobject thiz, jstring aSessionKey);
+JNIEXPORT jstring OLM_INBOUND_GROUP_SESSION_FUNC_DEF(sessionIdentifierJni)(JNIEnv *env, jobject thiz);
+JNIEXPORT jstring OLM_INBOUND_GROUP_SESSION_FUNC_DEF(decryptMessageJni)(JNIEnv *env, jobject thiz, jstring aEncryptedMsg);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_jni.h b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_jni.h
index 38269f9..8f1555d 100644
--- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_jni.h
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_jni.h
@@ -25,7 +25,6 @@
 #include <jni.h>
 #include <android/log.h>
 
-#include "olm/olm.h"
 
 #define TAG "OlmJniNative"
 
@@ -54,11 +53,4 @@ static const int ERROR_CODE_KO = -1;
 // constants
 static const int ACCOUNT_CREATION_RANDOM_MODULO = 256;
 
-
-typedef struct _AccountContext
-{
-  OlmAccount* mAccountPtr;
-  _AccountContext(): mAccountPtr(NULL){}
-} AccountContext;
-
 #endif
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_outbound_group_session.cpp b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_outbound_group_session.cpp
new file mode 100644
index 0000000..40af39d
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_outbound_group_session.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "olm_outbound_group_session.h"
+#include "olm_utility.h"
+
+
+/**
+ * Release the session allocation made by initializeOutboundGroupSessionMemory().<br>
+ * This method MUST be called when java counter part account instance is done.
+ *
+ */
+JNIEXPORT void OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz)
+{
+  OlmOutboundGroupSession* sessionPtr = NULL;
+
+  LOGD("## releaseSessionJni(): sessionPtr=%p",sessionPtr);
+
+  if(NULL == (sessionPtr = (OlmOutboundGroupSession*)getOutboundGroupSessionInstanceId(env,thiz)))
+  {
+      LOGE("## releaseSessionJni(): failure - invalid inbound group session instance");
+  }
+  else
+  {
+    size_t retCode = olm_clear_outbound_group_session(sessionPtr);
+    LOGD("## releaseSessionJni(): clear_inbound_group_session=%lu",retCode);
+
+    LOGD("## releaseSessionJni(): IN");
+    free(sessionPtr);
+    LOGD("## releaseSessionJni(): OUT");
+  }
+}
+
+/**
+* Initialize a new outbound group session and return it to JAVA side.<br>
+* Since a C prt is returned as a jlong, special care will be taken
+* to make the cast (OlmOutboundGroupSession* => jlong) platform independent.
+* @return the initialized OlmOutboundGroupSession* instance if init succeed, NULL otherwise
+**/
+JNIEXPORT jlong OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(initNewSessionJni)(JNIEnv *env, jobject thiz)
+{
+    OlmOutboundGroupSession* sessionPtr = NULL;
+    size_t sessionSize = olm_outbound_group_session_size();
+
+    if(0 == sessionSize)
+    {
+        LOGE("## initNewSessionJni(): failure - outbound group session size = 0");
+    }
+    else if(NULL != (sessionPtr=(OlmOutboundGroupSession*)malloc(sessionSize)))
+    {
+      sessionPtr = olm_outbound_group_session(sessionPtr);
+      LOGD("## initNewSessionJni(): success - outbound group session size=%lu",sessionSize);
+    }
+    else
+    {
+      LOGE("## initNewSessionJni(): failure - outbound group session OOM");
+    }
+
+    return (jlong)(intptr_t)sessionPtr;
+}
+
+/**
+ * Create a new outbound session.<br>
+ * @return ERROR_CODE_OK if operation succeed, ERROR_CODE_KO otherwise
+ */
+JNIEXPORT jint OLM_INBOUND_GROUP_SESSION_FUNC_DEF(initOutboundGroupSessionJni)(JNIEnv *env, jobject thiz)
+{
+    jint retCode = ERROR_CODE_KO;
+    OlmOutboundGroupSession *sessionPtr = NULL;
+    uint8_t *randomBuffPtr = NULL;
+    size_t sessionResult;
+
+    if(NULL == (sessionPtr = (OlmOutboundGroupSession*)getOutboundGroupSessionInstanceId(env,thiz)))
+    {
+        LOGE("## initOutboundGroupSessionJni(): failure - invalid inbound group session instance");
+    }
+    else
+    {
+        // compute random buffer
+        size_t randomLength = olm_init_outbound_group_session_random_length(sessionPtr);
+
+        if((0!=randomLength) && !setRandomInBuffer(&randomBuffPtr, randomLength))
+        {
+            LOGE("## initOutboundGroupSessionJni(): failure - random buffer init");
+        }
+        else
+        {
+            if(0==randomLength)
+            {
+                LOGW("## initOutboundGroupSessionJni(): random buffer is not required");
+            }
+
+            size_t sessionResult = olm_init_outbound_group_session(sessionPtr, sessionKeyPtr, sessionKeyLength);
+            if(sessionResult == olm_error()) {
+                const char *errorMsgPtr = olm_inbound_group_session_last_error(sessionPtr);
+                LOGE("## initInboundSessionFromIdKeyJni(): failure - init inbound session creation  Msg=%s",errorMsgPtr);
+            }
+            else
+            {
+                retCode = ERROR_CODE_OK;
+                LOGD("## initInboundSessionFromIdKeyJni(): success - result=%lu", sessionResult);
+            }
+
+
+        }
+      }
+
+
+    else if(0 == aSessionKey)
+    {
+        LOGE("## initInboundGroupSessionWithSessionKeyJni(): failure - invalid aSessionKey");
+    }
+    else if(NULL == (sessionKeyPtr = (const uint8_t *)env->GetStringUTFChars(aSessionKey, 0)))
+    {
+        LOGE("## initInboundSessionFromIdKeyJni(): failure - session key JNI allocation OOM");
+    }
+    else
+    {
+        size_t sessionKeyLength = (size_t)env->GetStringUTFLength(aSessionKey);
+        LOGD("## initInboundSessionFromIdKeyJni(): sessionKeyLength=%lu",sessionKeyLength);
+
+        sessionResult = olm_init_inbound_group_session(sessionPtr, sessionKeyPtr, sessionKeyLength);
+        if(sessionResult == olm_error()) {
+            const char *errorMsgPtr = olm_inbound_group_session_last_error(sessionPtr);
+            LOGE("## initInboundSessionFromIdKeyJni(): failure - init inbound session creation  Msg=%s",errorMsgPtr);
+        }
+        else
+        {
+            retCode = ERROR_CODE_OK;
+            LOGD("## initInboundSessionFromIdKeyJni(): success - result=%lu", sessionResult);
+        }
+     }
+
+     // free local alloc
+     if(NULL!= sessionKeyPtr)
+     {
+         env->ReleaseStringUTFChars(aSessionKey, (const char*)sessionKeyPtr);
+     }
+
+    return retCode;
+}
+
+
+JNIEXPORT jstring OLM_INBOUND_GROUP_SESSION_FUNC_DEF(sessionIdentifierJni)(JNIEnv *env, jobject thiz)
+{
+    OlmInboundGroupSession *sessionPtr = NULL;
+    uint8_t *sessionIdPtr = NULL;
+    jstring returnValueStr=0;
+
+    // get the size to alloc to contain the id
+    size_t lengthSessionId = olm_inbound_group_session_id_length(sessionPtr);
+
+    if(NULL == (sessionPtr = (OlmInboundGroupSession*)getInboundGroupSessionInstanceId(env,thiz)))
+    {
+        LOGE("## sessionIdentifierJni(): failure - invalid inbound group session instance");
+    }
+    else if(NULL == (sessionIdPtr = (uint8_t*)malloc(lengthSessionId*sizeof(uint8_t))))
+    {
+       LOGE("## sessionIdentifierJni(): failure - identifier allocation OOM");
+    }
+    else
+    {
+        size_t result = olm_inbound_group_session_id(sessionPtr, sessionIdPtr, lengthSessionId);
+        if (result == olm_error())
+        {
+            const char *errorMsgPtr = olm_inbound_group_session_last_error(sessionPtr);
+            LOGE("## sessionIdentifierJni(): failure - get session identifier failure Msg=%s",errorMsgPtr);
+        }
+        else
+        {
+            // update length
+            sessionIdPtr[result] = static_cast<char>('\0');
+
+            LOGD("## sessionIdentifierJni(): success - result=%lu sessionId=%s",result, (char*)sessionIdPtr);
+            returnValueStr = env->NewStringUTF((const char*)sessionIdPtr);
+        }
+        free(sessionIdPtr);
+    }
+
+    return returnValueStr;
+}
+
+JNIEXPORT jstring OLM_INBOUND_GROUP_SESSION_FUNC_DEF(decryptMessageJni)(JNIEnv *env, jobject thiz, jstring aEncryptedMsg)
+{
+    jstring decryptedMsgRetValue = 0;
+    OlmInboundGroupSession *sessionPtr = NULL;
+    const char *encryptedMsgPtr = NULL;
+    uint8_t *plainTextMsgPtr = NULL;
+    uint8_t *tempEncryptedPtr = NULL;
+
+    LOGD("## decryptMessageJni(): IN");
+
+    if(NULL == (sessionPtr = (OlmInboundGroupSession*)getInboundGroupSessionInstanceId(env,thiz)))
+    {
+        LOGE("##  decryptMessageJni(): failure - invalid inbound group session ptr=NULL");
+    }
+    else if(0 == aEncryptedMsg)
+    {
+        LOGE("##  decryptMessageJni(): failure - invalid clear message");
+    }
+    else if(0 == (encryptedMsgPtr = env->GetStringUTFChars(aEncryptedMsg, 0)))
+    {
+        LOGE("##  decryptMessageJni(): failure - encrypted message JNI allocation OOM");
+    }
+    else
+    {
+        // get encrypted message length
+        size_t encryptedMsgLength = (size_t)env->GetStringUTFLength(aEncryptedMsg);
+
+        // create a dedicated temp buffer to be used in next Olm API calls
+        if(NULL == (tempEncryptedPtr = static_cast<uint8_t*>(malloc(encryptedMsgLength*sizeof(uint8_t)))))
+        {
+            LOGE("##  decryptMessageJni(): failure - tempEncryptedPtr allocation OOM");
+        }
+        else
+        {
+            memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength);
+            LOGD("##  decryptMessageJni(): encryptedMsgLength=%lu encryptedMsg=%s",encryptedMsgLength,encryptedMsgPtr);
+
+            // get max plaintext length
+            size_t maxPlainTextLength = olm_group_decrypt_max_plaintext_length(sessionPtr,
+                                                                               tempEncryptedPtr,
+                                                                               encryptedMsgLength);
+            if(maxPlainTextLength == olm_error())
+            {
+                const char *errorMsgPtr = olm_inbound_group_session_last_error(sessionPtr);
+                LOGE("##  decryptMessageJni(): failure - olm_group_decrypt_max_plaintext_length Msg=%s",errorMsgPtr);
+            }
+            else
+            {
+                LOGD("##  decryptMessageJni(): maxPlaintextLength=%lu",maxPlainTextLength);
+
+                // allocate output decrypted message
+                plainTextMsgPtr = static_cast<uint8_t*>(malloc(maxPlainTextLength*sizeof(uint8_t)));
+
+                // decrypt, but before reload encrypted buffer (previous one was destroyed)
+                memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength);
+                size_t plaintextLength = olm_group_decrypt(sessionPtr,
+                                                           tempEncryptedPtr,
+                                                           encryptedMsgLength,
+                                                           plainTextMsgPtr,
+                                                           maxPlainTextLength);
+                if(plaintextLength == olm_error())
+                {
+                    const char *errorMsgPtr = olm_inbound_group_session_last_error(sessionPtr);
+                    LOGE("##  decryptMessageJni(): failure - olm_group_decrypt Msg=%s",errorMsgPtr);
+                }
+                else
+                {
+                    // update decrypted buffer size
+                    plainTextMsgPtr[plaintextLength] = static_cast<char>('\0');
+
+                    LOGD("##  decryptMessageJni(): decrypted returnedLg=%lu plainTextMsgPtr=%s",plaintextLength, (char*)plainTextMsgPtr);
+                    decryptedMsgRetValue = env->NewStringUTF((const char*)plainTextMsgPtr);
+                }
+            }
+        }
+    }
+
+    // free alloc
+    if(NULL != encryptedMsgPtr)
+    {
+        env->ReleaseStringUTFChars(aEncryptedMsg, encryptedMsgPtr);
+    }
+
+    if(NULL != tempEncryptedPtr)
+    {
+        free(tempEncryptedPtr);
+    }
+
+    if(NULL != plainTextMsgPtr)
+    {
+        free(plainTextMsgPtr);
+    }
+
+    return decryptedMsgRetValue;
+}
+
+
+
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_outbound_group_session.h b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_outbound_group_session.h
new file mode 100644
index 0000000..1270dc2
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_outbound_group_session.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _OMLOUTBOUND_GROUP_SESSION_H
+#define _OMLOUTBOUND_GROUP_SESSION_H
+
+#include "olm_jni.h"
+#include "olm/olm.h"
+#include "olm/outbound_group_session.h"
+
+#define OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(func_name) FUNC_DEF(OlmOutboundGroupSession,func_name)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// session creation/destruction
+JNIEXPORT void OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz);
+JNIEXPORT jlong OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(initNewSessionJni)(JNIEnv *env, jobject thiz);
+
+JNIEXPORT jint OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(initOutboundGroupSessionJni)(JNIEnv *env, jobject thiz, jstring aSessionKey);
+JNIEXPORT jstring OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(sessionIdentifierJni)(JNIEnv *env, jobject thiz);
+JNIEXPORT jstring OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(decryptMessageJni)(JNIEnv *env, jobject thiz, jstring aEncryptedMsg);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.cpp b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.cpp
index d33ab72..435540c 100644
--- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.cpp
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.cpp
@@ -458,7 +458,7 @@ JNIEXPORT jint OLM_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobject thiz
     }
     else if(0 == aEncryptedMsg)
     {
-        LOGE("## encryptMessageJni(): failure - invalid clear message");
+        LOGE("## encryptMessageJni(): failure - invalid encrypted message");
     }
     else if(NULL == (clearMsgPtr = env->GetStringUTFChars(aClearMsg, 0)))
     {
@@ -587,11 +587,11 @@ JNIEXPORT jstring OLM_SESSION_FUNC_DEF(decryptMessageJni)(JNIEnv *env, jobject t
     }
     else if(0 == aEncryptedMsg)
     {
-        LOGE("##  decryptMessageJni(): failure - invalid clear message");
+        LOGE("##  decryptMessageJni(): failure - invalid encrypted message");
     }
     else if(0 == (encryptedMsgJclass = env->GetObjectClass(aEncryptedMsg)))
     {
-        LOGE("##  decryptMessageJni(): failure - unable to get crypted message class");
+        LOGE("##  decryptMessageJni(): failure - unable to get encrypted message class");
     }
     else if(0 == (encryptedMsgFieldId = env->GetFieldID(encryptedMsgJclass,"mCipherText","Ljava/lang/String;")))
     {
@@ -644,7 +644,7 @@ JNIEXPORT jstring OLM_SESSION_FUNC_DEF(decryptMessageJni)(JNIEnv *env, jobject t
             memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength);
             size_t plaintextLength = olm_decrypt(sessionPtr,
                                                  encryptedMsgType,
-                                                 (void*)encryptedMsgPtr,
+                                                 (void*)tempEncryptedPtr,
                                                  encryptedMsgLength,
                                                  plainTextMsgPtr,
                                                  maxPlainTextLength);
@@ -717,7 +717,7 @@ JNIEXPORT jstring OLM_SESSION_FUNC_DEF(getSessionIdentifierJni)(JNIEnv *env, job
         }
         else
         {
-            // update decrypted buffer size
+            // update length
             (static_cast<char*>(sessionIdPtr))[result] = static_cast<char>('\0');
 
             LOGD("## getSessionIdentifierJni(): success - result=%lu sessionId=%s",result, (char*)sessionIdPtr);
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.h b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.h
index babb3bd..f79dcaa 100644
--- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.h
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.h
@@ -18,6 +18,7 @@
 #define _OMLSESSION_H
 
 #include "olm_jni.h"
+#include "olm/olm.h"
 
 #define OLM_SESSION_FUNC_DEF(func_name) FUNC_DEF(OlmSession,func_name)
 
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp
index c27fe7c..bd920fe 100644
--- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp
@@ -101,7 +101,7 @@ jlong getAccountInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
 }
 
 /**
-* Read the account instance ID of the calling object (aJavaObject).<br>
+* Read the session instance ID of the calling object (aJavaObject).<br>
 * @param aJniEnv pointer pointing on the JNI function table
 * @param aJavaObject reference to the object on which the method is invoked
 * @return the instance ID if read succeed, -1 otherwise.
@@ -139,3 +139,83 @@ jlong getSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
   //LOGD("## getSessionInstanceId() success - instanceId=%lld",instanceId);
   return instanceId;
 }
+
+
+/**
+* Read the inbound group session instance ID of the calling object (aJavaObject).<br>
+* @param aJniEnv pointer pointing on the JNI function table
+* @param aJavaObject reference to the object on which the method is invoked
+* @return the instance ID if read succeed, -1 otherwise.
+**/
+jlong getInboundGroupSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
+{
+  jlong instanceId=-1;
+  jfieldID instanceIdField;
+  jclass loaderClass;
+
+  if(NULL!=aJniEnv)
+  {
+    if(0 != (loaderClass=aJniEnv->GetObjectClass(aJavaObject)))
+    {
+      if(0 != (instanceIdField=aJniEnv->GetFieldID(loaderClass, "mNativeOlmInboundGroupSessionId", "J")))
+      {
+        instanceId = aJniEnv->GetLongField(aJavaObject, instanceIdField);
+        aJniEnv->DeleteLocalRef(loaderClass);
+      }
+      else
+      {
+        LOGD("## getSessionInstanceId() ERROR! GetFieldID=null");
+      }
+    }
+    else
+    {
+      LOGD("## getSessionInstanceId() ERROR! GetObjectClass=null");
+    }
+  }
+  else
+  {
+    LOGD("## getSessionInstanceId() ERROR! aJniEnv=NULL");
+  }
+
+  return instanceId;
+}
+
+
+/**
+* Read the outbound group session instance ID of the calling object (aJavaObject).<br>
+* @param aJniEnv pointer pointing on the JNI function table
+* @param aJavaObject reference to the object on which the method is invoked
+* @return the instance ID if read succeed, -1 otherwise.
+**/
+jlong getOutboundGroupSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
+{
+  jlong instanceId=-1;
+  jfieldID instanceIdField;
+  jclass loaderClass;
+
+  if(NULL!=aJniEnv)
+  {
+    if(0 != (loaderClass=aJniEnv->GetObjectClass(aJavaObject)))
+    {
+      if(0 != (instanceIdField=aJniEnv->GetFieldID(loaderClass, "mNativeOlmOutboundGroupSessionId", "J")))
+      {
+        instanceId = aJniEnv->GetLongField(aJavaObject, instanceIdField);
+        aJniEnv->DeleteLocalRef(loaderClass);
+      }
+      else
+      {
+        LOGD("## getSessionInstanceId() ERROR! GetFieldID=null");
+      }
+    }
+    else
+    {
+      LOGD("## getSessionInstanceId() ERROR! GetObjectClass=null");
+    }
+  }
+  else
+  {
+    LOGD("## getSessionInstanceId() ERROR! aJniEnv=NULL");
+  }
+
+  return instanceId;
+}
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.h b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.h
index bf35955..b16e915 100644
--- a/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.h
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.h
@@ -25,6 +25,7 @@ extern "C" {
 bool setRandomInBuffer(uint8_t **aBuffer2Ptr, size_t aRandomSize);
 jlong getSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
 jlong getAccountInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
+jlong getInboundGroupSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
 
 #ifdef __cplusplus
 }
-- 
cgit v1.2.3-70-g09d2