diff --git a/java/android/OlmLibSdk/build.gradle b/java/android/OlmLibSdk/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..77ce66ea36eed3ec5c10abb7b2618ed4748e0081
--- /dev/null
+++ b/java/android/OlmLibSdk/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+    repositories {
+        jcenter()
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:2.1.3'
+
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        jcenter()
+    }
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}
diff --git a/java/android/OlmLibSdk/gradle.properties b/java/android/OlmLibSdk/gradle.properties
new file mode 100644
index 0000000000000000000000000000000000000000..13d64a501b95cf8bad5399a4c683a69966d1f235
--- /dev/null
+++ b/java/android/OlmLibSdk/gradle.properties
@@ -0,0 +1,19 @@
+## Project-wide Gradle settings.
+#
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+#
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+#
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+#Wed Oct 05 11:49:34 CEST 2016
+systemProp.https.proxyPort=8080
+systemProp.http.proxyHost=batproxy
+systemProp.https.proxyHost=batproxy
+systemProp.http.proxyPort=8080
diff --git a/java/android/OlmLibSdk/gradlew b/java/android/OlmLibSdk/gradlew
new file mode 100644
index 0000000000000000000000000000000000000000..9d82f78915133e1c35a6ea51252590fb38efac2f
--- /dev/null
+++ b/java/android/OlmLibSdk/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/java/android/OlmLibSdk/gradlew.bat b/java/android/OlmLibSdk/gradlew.bat
new file mode 100644
index 0000000000000000000000000000000000000000..8a0b282aa6885fb573c106b3551f7275c5f17e8e
--- /dev/null
+++ b/java/android/OlmLibSdk/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/java/android/OlmLibSdk/olm-sdk/build.gradle b/java/android/OlmLibSdk/olm-sdk/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..2f92151be087319e87a1e041730062cce3eb37bb
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/build.gradle
@@ -0,0 +1,90 @@
+import org.apache.tools.ant.taskdefs.condition.Os
+
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion '21.1.2'
+
+    defaultConfig {
+        minSdkVersion 11
+        targetSdkVersion 21
+        versionCode 1
+        versionName "1.0"
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+    sourceSets.main {
+        jniLibs.srcDir 'src/main/libs'
+        jni.srcDirs = []
+    }
+
+    task ndkBuildNativeRelease(type: Exec, description: 'NDK building..') {
+        println 'ndkBuildNativeRelease starts..'
+        workingDir file('src/main')
+        commandLine getNdkBuildCmd(), 'NDK_DEBUG=0'
+    }
+
+    task ndkBuildNativeDebug(type: Exec, description: 'NDK building..') {
+        println 'ndkBuildNativeDebug starts..'
+        workingDir file('src/main')
+        commandLine getNdkBuildCmd(), 'NDK_DEBUG=1'
+    }
+
+    task cleanNative(type: Exec, description: 'Clean NDK build') {
+        workingDir file('src/main')
+        commandLine getNdkBuildCmd(), 'clean'
+    }
+
+    /*tasks.withType(JavaCompile) {
+        compileTask -> compileTask.dependsOn ndkBuildNative
+    }*/
+
+    tasks.withType(JavaCompile) {
+        compileTask -> if (compileTask.name.startsWith('compileDebugJava')) {
+            println 'test compile: Debug'
+            compileTask.dependsOn ndkBuildNativeDebug
+        } else if (compileTask.name.startsWith('compileReleaseJava')) {
+            println 'test compile: Release'
+            compileTask.dependsOn ndkBuildNativeRelease
+        }
+    }
+
+
+    clean.dependsOn cleanNative
+
+}
+
+def getNdkFolder() {
+    Properties properties = new Properties()
+    properties.load(project.rootProject.file('local.properties').newDataInputStream())
+    def ndkFolder = properties.getProperty('ndk.dir', null)
+    if (ndkFolder == null)
+        throw new GradleException("NDK location missing. Define it with ndk.dir in the local.properties file")
+
+    return ndkFolder
+}
+
+def getNdkBuildCmd() {
+    def ndkBuildCmd = getNdkFolder() + "/ndk-build"
+    if (Os.isFamily(Os.FAMILY_WINDOWS))
+        ndkBuildCmd += ".cmd"
+
+    return ndkBuildCmd
+}
+
+dependencies {
+    compile fileTree(include: ['*.jar'], dir: 'libs')
+    compile 'com.android.support:appcompat-v7:21.+'
+
+    testCompile 'junit:junit:4.12'
+    androidTestCompile 'junit:junit:4.12'
+    androidTestCompile 'com.android.support:support-annotations:21.0.0'
+    androidTestCompile 'com.android.support.test:runner:0.5'
+    androidTestCompile 'com.android.support.test:rules:0.5'
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..05480fcbd3618574e3cd7e07d728a114118702bd
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java
@@ -0,0 +1,220 @@
+package org.matrix.olm;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.text.TextUtils;
+import android.util.Log;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+
+import java.util.Iterator;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(AndroidJUnit4.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class OlmAccountTest {
+    private static final String LOG_TAG = "OlmAccountTest";
+    private static final int GENERATION_ONE_TIME_KEYS_NUMBER = 50;
+
+    private static OlmAccount mOlmAccount;
+    private static OlmManager mOlmManager;
+    private boolean mIsAccountCreated;
+
+    @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);
+    }
+
+    @AfterClass
+    public static void tearDownClass() {
+        // TBD
+    }
+
+    @Before
+    public void setUp() {
+        if(mIsAccountCreated) {
+            assertNotNull(mOlmAccount);
+        }
+    }
+
+    @After
+    public void tearDown() {
+        // TBD
+    }
+
+    @Test
+    public void test01CreateReleaseAccount() {
+        mOlmAccount = new OlmAccount();
+        assertNotNull(mOlmAccount);
+
+        mOlmAccount.releaseAccount();
+        assertTrue(0 == mOlmAccount.getOlmAccountId());
+    }
+
+    @Test
+    public void test02CreateAccount() {
+        mOlmAccount = new OlmAccount();
+        assertNotNull(mOlmAccount);
+    }
+
+    @Test
+    public void test03InitNewAccount() {
+        assertTrue(mOlmAccount.initNewAccount());
+        mIsAccountCreated = true;
+    }
+
+    @Test
+    public void test04GetOlmAccountId() {
+        long olmNativeInstance = mOlmAccount.getOlmAccountId();
+        Log.d(LOG_TAG,"## testGetOlmAccountId olmNativeInstance="+olmNativeInstance);
+        assertTrue(0!=olmNativeInstance);
+    }
+
+    @Test
+    public void test05IdentityKeys() {
+        JSONObject identityKeysJson = mOlmAccount.identityKeys();
+        assertNotNull(identityKeysJson);
+        Log.d(LOG_TAG,"## testIdentityKeys Keys="+identityKeysJson);
+
+        try {
+            String fingerPrintKey = identityKeysJson.getString(OlmAccount.JSON_KEY_FINGER_PRINT_KEY);
+            assertTrue("fingerprint key missing",!TextUtils.isEmpty(fingerPrintKey));
+        } catch (JSONException e) {
+            e.printStackTrace();
+            assertTrue("Exception MSg="+e.getMessage(), false);
+        }
+
+        try {
+            String identityKey = identityKeysJson.getString(OlmAccount.JSON_KEY_IDENTITY_KEY);
+            assertTrue("identity key missing",!TextUtils.isEmpty(identityKey));
+        } catch (JSONException e) {
+            e.printStackTrace();
+            assertTrue("Exception MSg="+e.getMessage(), false);
+        }
+
+
+    }
+
+    //****************************************************
+    //** ************** One time keys TESTS **************
+    //****************************************************
+    @Test
+    public void test06MaxOneTimeKeys() {
+        long maxOneTimeKeys = mOlmAccount.maxOneTimeKeys();
+        Log.d(LOG_TAG,"## testMaxOneTimeKeys(): maxOneTimeKeys="+maxOneTimeKeys);
+
+        assertTrue(maxOneTimeKeys>0);
+    }
+
+    @Test
+    public void test07GenerateOneTimeKeys() {
+        int retValue = mOlmAccount.generateOneTimeKeys(GENERATION_ONE_TIME_KEYS_NUMBER);
+        assertTrue(0==retValue);
+    }
+
+    @Test
+    public void test08OneTimeKeysJsonFormat() {
+        int oneTimeKeysCount = 0;
+        JSONObject generatedKeysJsonObj;
+        JSONObject oneTimeKeysJson = mOlmAccount.oneTimeKeys();
+        assertNotNull(oneTimeKeysJson);
+
+        try {
+            generatedKeysJsonObj = oneTimeKeysJson.getJSONObject(OlmAccount.JSON_KEY_ONE_TIME_KEY);
+            assertTrue(OlmAccount.JSON_KEY_ONE_TIME_KEY +" object is missing", null!=generatedKeysJsonObj);
+
+            // test the count of the generated one time keys:
+            Iterator<String> generatedKeysIt = generatedKeysJsonObj.keys();
+            while(generatedKeysIt.hasNext()){
+                generatedKeysIt.next();
+                oneTimeKeysCount++;
+            }
+            assertTrue("Expected count="+GENERATION_ONE_TIME_KEYS_NUMBER+" found="+oneTimeKeysCount,GENERATION_ONE_TIME_KEYS_NUMBER==oneTimeKeysCount);
+
+        } catch (JSONException e) {
+            assertTrue("Exception MSg="+e.getMessage(), false);
+        }
+    }
+
+    @Test
+    public void test10RemoveOneTimeKeysForSession() {
+        OlmSession olmSession = new OlmSession();
+        olmSession.initNewSession();
+        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);
+
+        olmSession.releaseSession();
+        sessionId = olmSession.getOlmSessionId();
+        assertTrue("sessionRetCode="+sessionRetCode,0 == sessionId);
+    }
+
+    @Test
+    public void test11MarkOneTimeKeysAsPublished() {
+        int retCode = mOlmAccount.markOneTimeKeysAsPublished();
+        // if OK => retCode=0
+        assertTrue(0 == retCode);
+    }
+
+    @Test
+    public void test12SignMessage() {
+        String clearMsg = "String to be signed by olm";
+        String signedMsg  = mOlmAccount.signMessage(clearMsg);
+        assertNotNull(signedMsg);
+        // TODO add test to unsign the signedMsg and compare it ot clearMsg
+    }
+
+
+    private void testJni(){
+        OlmManager mgr = new OlmManager();
+        String versionLib = mgr.getOlmLibVersion();
+        Log.d(LOG_TAG, "## testJni(): lib version="+versionLib);
+
+        OlmAccount account = new OlmAccount();
+        boolean initStatus = account.initNewAccount();
+
+        long accountNativeId = account.getOlmAccountId();
+        Log.d(LOG_TAG, "## testJni(): lib accountNativeId="+accountNativeId);
+
+        JSONObject identityKeys = account.identityKeys();
+        Log.d(LOG_TAG, "## testJni(): identityKeysJson="+identityKeys.toString());
+
+        long maxOneTimeKeys = account.maxOneTimeKeys();
+        Log.d(LOG_TAG, "## testJni(): lib maxOneTimeKeys="+maxOneTimeKeys);
+
+        int generateRetCode = account.generateOneTimeKeys(50);
+        Log.d(LOG_TAG, "## testJni(): generateRetCode="+generateRetCode);
+
+        JSONObject oneTimeKeysKeysJson = account.oneTimeKeys();
+        Log.d(LOG_TAG, "## testJni(): oneTimeKeysKeysJson="+oneTimeKeysKeysJson.toString());
+
+        int asPublishedRetCode = account.markOneTimeKeysAsPublished();
+        Log.d(LOG_TAG, "## testJni(): asPublishedRetCode="+asPublishedRetCode);
+
+        String clearMsg ="My clear message";
+        String signedMsg = account.signMessage(clearMsg);
+        Log.d(LOG_TAG, "## testJni(): signedMsg="+signedMsg);
+
+        account.releaseAccount();
+    }
+
+
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..56048cce6ab7eb6a9176bfaa20e5039e07962539
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmSessionTest.java
@@ -0,0 +1,269 @@
+package org.matrix.olm;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+
+import java.util.Iterator;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(AndroidJUnit4.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class OlmSessionTest {
+    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);
+    }
+
+    /**
+     * Basic test:
+     * - alice creates an account
+     * - bob creates an account
+     * - alice creates an outbound session with bob (bobIdentityKey & bobOneTimeKey)
+     * - alice encrypts a message with its session
+     * - bob creates an inbound session based on alice's encrypted message
+     * - bob decrypts the encrypted message with its session
+     */
+    @Test
+    public void test01AliceToBob() {
+        final int ONE_TIME_KEYS_NUMBER = 5;
+        String bobIdentityKey = null;
+        String bobOneTimeKey=null;
+
+        // creates alice & bob accounts
+        OlmAccount aliceAccount = new OlmAccount();
+        aliceAccount.initNewAccount();
+
+        OlmAccount bobAccount = new OlmAccount();
+        bobAccount.initNewAccount();
+
+        // test accounts creation
+        assertTrue(0!=bobAccount.getOlmAccountId());
+        assertTrue(0!=aliceAccount.getOlmAccountId());
+
+        // get bob identity key
+        JSONObject bobIdentityKeysJson = bobAccount.identityKeys();
+        assertNotNull(bobIdentityKeysJson);
+        try {
+            bobIdentityKey = bobIdentityKeysJson.getString(OlmAccount.JSON_KEY_IDENTITY_KEY);
+            assertTrue(null!=bobIdentityKey);
+        } catch (JSONException e) {
+            assertTrue("Exception MSg="+e.getMessage(), false);
+        }
+
+        // get bob one time keys
+        assertTrue(0==bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER));
+        JSONObject bobOneTimeKeysJsonObj = bobAccount.oneTimeKeys();
+        assertNotNull(bobOneTimeKeysJsonObj);
+        try {
+            JSONObject generatedKeys = bobOneTimeKeysJsonObj.getJSONObject(OlmAccount.JSON_KEY_ONE_TIME_KEY);
+            assertNotNull(OlmAccount.JSON_KEY_ONE_TIME_KEY +" object is missing", generatedKeys);
+
+            Iterator<String> generatedKeysIt = generatedKeys.keys();
+            if(generatedKeysIt.hasNext()) {
+                bobOneTimeKey = generatedKeys.getString(generatedKeysIt.next());
+            }
+            assertNotNull(bobOneTimeKey);
+        } catch (JSONException e) {
+            assertTrue("Exception MSg="+e.getMessage(), false);
+        }
+
+        // CREATE ALICE SESSION
+        OlmSession aliceSession = new OlmSession();
+        aliceSession.initNewSession();
+        assertTrue(0!=aliceSession.getOlmSessionId());
+
+        // CREATE ALICE OUTBOUND SESSION and encrypt message to bob
+        assertNotNull(aliceSession.initOutboundSessionWithAccount(aliceAccount, bobIdentityKey, bobOneTimeKey));
+        String clearMsg = "Heloo bob , this is alice!";
+        OlmMessage encryptedMsgToBob = aliceSession.encryptMessage(clearMsg);
+        assertNotNull(encryptedMsgToBob);
+        Log.d(LOG_TAG,"## test01AliceToBob(): encryptedMsg="+encryptedMsgToBob.mCipherText);
+
+        // CREATE BOB INBOUND SESSION and decrypt message from alice
+        OlmSession bobSession = new OlmSession();
+        bobSession.initNewSession();
+        assertTrue(0!=bobSession.getOlmSessionId());
+        assertNotNull(bobSession.initInboundSessionWithAccount(bobAccount, encryptedMsgToBob.mCipherText));
+        String decryptedMsg = bobSession.decryptMessage(encryptedMsgToBob);
+        assertNotNull(decryptedMsg);
+
+        // MESSAGE COMPARISON: decrypted vs encrypted
+        assertTrue(clearMsg.equals(decryptedMsg));
+
+        // clean objects..
+        assertTrue(0==bobAccount.removeOneTimeKeysForSession(bobSession.getOlmSessionId()));
+        // release accounts
+        bobAccount.releaseAccount();
+        aliceAccount.releaseAccount();
+        // release sessions
+        bobSession.releaseSession();
+        aliceSession.releaseSession();
+    }
+
+
+    /**
+     * Same as test01AliceToBob but with bob who's encrypting messages
+     * to alice and alice decrypt them.<br>
+     * - alice creates an account
+     * - bob creates an account
+     * - alice creates an outbound session with bob (bobIdentityKey & bobOneTimeKey)
+     * - alice encrypts a message with its own session
+     * - bob creates an inbound session based on alice's encrypted message
+     * - bob decrypts the encrypted message with its own session
+     * - bob encrypts messages with its own session
+     * - alice decrypts bob's messages with its own message
+     */
+    @Test
+    public void test02AliceToBobBackAndForth() {
+        final int ONE_TIME_KEYS_NUMBER = 1;
+        String bobIdentityKey = null;
+        String bobOneTimeKey=null;
+
+        // creates alice & bob accounts
+        OlmAccount aliceAccount = new OlmAccount();
+        aliceAccount.initNewAccount();
+
+        OlmAccount bobAccount = new OlmAccount();
+        bobAccount.initNewAccount();
+
+        // test accounts creation
+        assertTrue(0!=bobAccount.getOlmAccountId());
+        assertTrue(0!=aliceAccount.getOlmAccountId());
+
+        // get bob identity key
+        JSONObject bobIdentityKeysJson = bobAccount.identityKeys();
+        assertNotNull(bobIdentityKeysJson);
+        try {
+            bobIdentityKey = bobIdentityKeysJson.getString(OlmAccount.JSON_KEY_IDENTITY_KEY);
+            assertTrue(null!=bobIdentityKey);
+        } catch (JSONException e) {
+            assertTrue("Exception MSg="+e.getMessage(), false);
+        }
+
+        // get bob one time keys
+        assertTrue(0==bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER));
+        JSONObject bobOneTimeKeysJsonObj = bobAccount.oneTimeKeys();
+        assertNotNull(bobOneTimeKeysJsonObj);
+        try {
+            JSONObject generatedKeys = bobOneTimeKeysJsonObj.getJSONObject(OlmAccount.JSON_KEY_ONE_TIME_KEY);
+            assertNotNull(OlmAccount.JSON_KEY_ONE_TIME_KEY +" object is missing", generatedKeys);
+
+            Iterator<String> generatedKeysIt = generatedKeys.keys();
+            if(generatedKeysIt.hasNext()) {
+                bobOneTimeKey = generatedKeys.getString(generatedKeysIt.next());
+            }
+            assertNotNull(bobOneTimeKey);
+        } catch (JSONException e) {
+            assertTrue("Exception MSg="+e.getMessage(), false);
+        }
+
+        // CREATE ALICE SESSION
+        OlmSession aliceSession = new OlmSession();
+        aliceSession.initNewSession();
+        assertTrue(0!=aliceSession.getOlmSessionId());
+
+        // CREATE ALICE OUTBOUND SESSION and encrypt message to bob
+        assertNotNull(aliceSession.initOutboundSessionWithAccount(aliceAccount, bobIdentityKey, bobOneTimeKey));
+        String helloClearMsg = "Hello I'm Alice!";
+
+        OlmMessage encryptedAliceToBobMsg1 = aliceSession.encryptMessage(helloClearMsg);
+        assertNotNull(encryptedAliceToBobMsg1);
+
+        // CREATE BOB INBOUND SESSION and decrypt message from alice
+        OlmSession bobSession = new OlmSession();
+        bobSession.initNewSession();
+        assertTrue(0!=bobSession.getOlmSessionId());
+        assertNotNull(bobSession.initInboundSessionWithAccount(bobAccount, encryptedAliceToBobMsg1.mCipherText));
+
+        // DECRYPT MESSAGE FROM ALICE
+        String decryptedMsg01 = bobSession.decryptMessage(encryptedAliceToBobMsg1);
+        assertNotNull(decryptedMsg01);
+
+        // MESSAGE COMPARISON: decrypted vs encrypted
+        assertTrue(helloClearMsg.equals(decryptedMsg01));
+
+        assertTrue(0==bobAccount.removeOneTimeKeysForSession(bobSession.getOlmSessionId()));
+
+        // BACK/FORTH MESSAGE COMPARISON
+        String clearMsg1 = "Hello I'm Bob!";
+        String clearMsg2 = "Isn't life grand?";
+        String clearMsg3 = "Let's go to the opera.";
+
+        OlmMessage encryptedMsg1 = bobSession.encryptMessage(clearMsg1);
+        assertNotNull(encryptedMsg1);
+        OlmMessage encryptedMsg2 = bobSession.encryptMessage(clearMsg2);
+        assertNotNull(encryptedMsg2);
+        OlmMessage encryptedMsg3 = bobSession.encryptMessage(clearMsg3);
+        assertNotNull(encryptedMsg3);
+
+        String decryptedMsg1 = aliceSession.decryptMessage(encryptedMsg1);
+        assertNotNull(decryptedMsg1);
+        String decryptedMsg2 = aliceSession.decryptMessage(encryptedMsg2);
+        assertNotNull(decryptedMsg2);
+        String decryptedMsg3 = aliceSession.decryptMessage(encryptedMsg3);
+        assertNotNull(decryptedMsg3);
+
+        assertTrue(clearMsg1.equals(decryptedMsg1));
+        assertTrue(clearMsg2.equals(decryptedMsg2));
+        assertTrue(clearMsg3.equals(decryptedMsg3));
+
+        // clean objects..
+        bobAccount.releaseAccount();
+        aliceAccount.releaseAccount();
+        bobSession.releaseSession();
+        aliceSession.releaseSession();
+    }
+
+    @Test
+    public void test03AliceBobSessionId() {
+        // creates alice & bob accounts
+        OlmAccount aliceAccount = new OlmAccount();
+        aliceAccount.initNewAccount();
+
+        OlmAccount bobAccount = new OlmAccount();
+        bobAccount.initNewAccount();
+
+        // test accounts creation
+        assertTrue(0!=bobAccount.getOlmAccountId());
+        assertTrue(0!=aliceAccount.getOlmAccountId());
+
+        // CREATE ALICE SESSION
+        OlmSession aliceSession = new OlmSession();
+        aliceSession.initNewSession();
+        assertTrue(0!=aliceSession.getOlmSessionId());
+
+        // CREATE BOB INBOUND SESSION and decrypt message from alice
+        OlmSession bobSession = new OlmSession();
+        bobSession.initNewSession();
+        assertTrue(0!=bobSession.getOlmSessionId());
+
+        String aliceSessionId = aliceSession.sessionIdentifier();
+        assertNotNull(aliceSessionId);
+
+        String bobSessionId = bobSession.sessionIdentifier();
+        assertNotNull(bobSessionId);
+
+        // must be the same for both ends of the conversation
+        assertTrue(aliceSessionId.equals(bobSessionId));
+    }
+
+}
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/AndroidManifest.xml b/java/android/OlmLibSdk/olm-sdk/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8a8747f03964c164f5e0dd4481d2113035450936
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/AndroidManifest.xml
@@ -0,0 +1,11 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="org.matrix.olm">
+
+    <application
+        android:allowBackup="true"
+        android:label="@string/app_name"
+        android:supportsRtl="true">
+
+    </application>
+
+</manifest>
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
new file mode 100644
index 0000000000000000000000000000000000000000..e9eddc785c86fbc9b32cdd48e7e43ac6b29ee89d
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmAccount.java
@@ -0,0 +1,204 @@
+/*
+ * 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.util.Log;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class OlmAccount {
+    private static final String LOG_TAG = "OlmAccount";
+
+    // JSON keys used in the JSON objects returned by JNI
+    public static final String JSON_KEY_ONE_TIME_KEY = "curve25519";
+    public static final String JSON_KEY_IDENTITY_KEY = "curve25519";
+    public static final String JSON_KEY_FINGER_PRINT_KEY = "ed25519";
+
+    /** account raw pointer value (OlmAccount*) returned by JNI.
+     * this value identifies uniquely the native account instance.
+     */
+    private long mNativeOlmAccountId;
+
+    public OlmAccount() {
+        //initNewAccount();
+    }
+
+    /**
+     * Getter on the account ID.
+     * @return native account ID
+     */
+    public long getOlmAccountId(){
+        return mNativeOlmAccountId;
+    }
+
+    /**
+     * Destroy the corresponding OLM account 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 #initNewAccountJni()}.
+     */
+    private native void releaseAccountJni();
+
+    /**
+     * Release native account and invalid its JAVA reference counter part.<br>
+     * Public API for {@link #releaseAccountJni()}.
+     * To be called before any other API call.
+     */
+    public void releaseAccount(){
+        releaseAccountJni();
+
+        mNativeOlmAccountId = 0;
+    }
+
+    /**
+     * 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})
+     */
+    private native long initNewAccountJni();
+
+    /**
+     * Create and save the account native instance ID.
+     * Wrapper for {@link #initNewAccountJni()}.<br>
+     * To be called before any other API call.
+     * @return true if init succeed, false otherwise.
+     */
+    public boolean initNewAccount() {
+        boolean retCode = false;
+        if(0 != (mNativeOlmAccountId = initNewAccountJni())){
+            retCode = true;
+        }
+        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>
+     * Ex:<tt>
+     * {
+     *  "curve25519":"Vam++zZPMqDQM6ANKpO/uAl5ViJSHxV9hd+b0/fwRAg",
+     *  "ed25519":"+v8SOlOASFTMrX3MCKBM4iVnYoZ+JIjpNt1fi8Z9O2I"
+     * }</tt>
+     * @return identity keys in JSON array if operation succeed, null otherwise
+     */
+    public JSONObject identityKeys() {
+        JSONObject identityKeysJsonObj = null;
+        byte identityKeysBuffer[];
+
+        if( null != (identityKeysBuffer = identityKeysJni())) {
+            try {
+                identityKeysJsonObj = new JSONObject(new String(identityKeysBuffer));
+                Log.d(LOG_TAG, "## identityKeys(): Identity Json keys=" + identityKeysJsonObj.toString());
+            } catch (JSONException e) {
+                identityKeysJsonObj = null;
+                Log.e(LOG_TAG, "## identityKeys(): Exception - Msg=" + e.getMessage());
+            }
+        } else {
+            Log.e(LOG_TAG, "## identityKeys(): Failure - identityKeysJni()=null");
+        }
+
+        return identityKeysJsonObj;
+    }
+
+    /**
+     * Return the largest number of "one time keys" this account can store.
+     * @return the max number of "one time keys", -1 otherwise
+     */
+    public native long maxOneTimeKeys();
+
+    /**
+     * Generate a number of new one time keys.<br> If total number of keys stored
+     * by this account exceeds {@link #maxOneTimeKeys()}, the old keys are discarded.<br>
+     * The corresponding keys are retrieved by {@link #oneTimeKeys()}.
+     * @param aNumberOfKeys number of keys to generate
+     * @return 0 if operation succeed, -1 otherwise
+     */
+    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>
+     * Ex:<tt>
+     * { "curve25519":
+     *  {
+     *      "AAAABQ":"qefVZd8qvjOpsFzoKSAdfUnJVkIreyxWFlipCHjSQQg",
+     *      "AAAABA":"/X8szMU+p+lsTnr56wKjaLgjTMQQkCk8EIWEAilZtQ8",
+     *      "AAAAAw":"qxNxxFHzevFntaaPdT0fhhO7tc7pco4+xB/5VRG81hA",
+     *  }
+     * }</tt><br>
+     * Public API for {@link #oneTimeKeysJni()}.<br>
+     * Note: these keys are to be published on the server.
+     * @return one time keys in JSON array format if operation succeed, null otherwise
+     */
+    public JSONObject oneTimeKeys() {
+        byte identityKeysBuffer[];
+        JSONObject identityKeysJsonObj = null;
+
+        if( null != (identityKeysBuffer = oneTimeKeysJni())) {
+            try {
+                identityKeysJsonObj = new JSONObject(new String(identityKeysBuffer));
+                Log.d(LOG_TAG, "## oneTimeKeys(): Identity Json keys=" + identityKeysJsonObj.toString());
+            } catch (JSONException e) {
+                identityKeysJsonObj = null;
+                Log.e(LOG_TAG, "## oneTimeKeys(): Exception - Msg=" + e.getMessage());
+            }
+        } else {
+            Log.e(LOG_TAG, "## oneTimeKeys(): Failure - identityKeysJni()=null");
+        }
+
+        return identityKeysJsonObj;
+    }
+
+    /**
+     * 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);
+
+    /**
+     * Marks the current set of "one time keys" as being published.
+     * @return 0 if operation succeed, -1 otherwise
+     */
+    public native int markOneTimeKeysAsPublished();
+
+    /**
+     * 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);
+}
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmManager.java b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..55b9910ebc51f8f5f9226b8f62ce8f6b850cecd7
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmManager.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+
+public class OlmManager {
+
+    static {
+        java.lang.System.loadLibrary("olm");
+    }
+
+    /**
+     * Get the OLM lib version.
+     * @return the lib version as a string
+     */
+    public native String getOlmLibVersion();
+}
+
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmMessage.java b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmMessage.java
new file mode 100644
index 0000000000000000000000000000000000000000..047db97400a2f941443dab2679d7a54563e4950e
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmMessage.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+public class OlmMessage {
+    /** PRE KEY message type (used to establish new Olm session) **/
+    public final static int MESSAGE_TYPE_PRE_KEY = 0;
+    /** normal message type **/
+    public final static int MESSAGE_TYPE_MESSAGE = 1;
+
+    /** the encrypted message (ie. )**/
+    public String mCipherText;
+
+    /** defined by {@link #MESSAGE_TYPE_MESSAGE} or {@link #MESSAGE_TYPE_PRE_KEY}**/
+    public long mType;
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..0c2888c295245a06ecd62b935c6c8946a1714570
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmSession.java
@@ -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.
+ */
+
+package org.matrix.olm;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+public class OlmSession {
+    private static final String LOG_TAG = "OlmSession";
+
+    /** session raw pointer value (OlmSession*) returned by JNI.
+     * this value uniquely identifies the native session instance.
+     **/
+    private long mNativeOlmSessionId;
+
+    /** account instance associated with this session. **/
+    private OlmAccount mOlmAccount;
+
+    public OlmSession() {
+        //initNewSession();
+    }
+
+    /**
+     * Getter on the session ID.
+     * @return native session ID
+     */
+    public long getOlmSessionId(){
+        return mNativeOlmSessionId;
+    }
+
+    /**
+     * Getter on the session ID.
+     * @return native session ID
+     */
+    public OlmAccount getOlmAccountId(){
+        return mOlmAccount;
+    }
+
+    /**
+     * Destroy the corresponding OLM 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();
+
+    /**
+     * 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();
+
+        mNativeOlmSessionId = 0;
+    }
+
+
+    /**
+     * 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.
+     */
+    public boolean initNewSession() {
+        boolean retCode = false;
+        if(0 != (mNativeOlmSessionId = initNewSessionJni())){
+            retCode = true;
+        }
+        return retCode;
+    }
+
+    /**
+     * 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})
+     */
+    private native long initNewSessionJni();
+
+
+    /**
+     * Creates a new out-bound session for sending messages to a recipient
+     * identified by an identity key and a one time key.<br>
+     * Public API for {@link #initOutboundSessionWithAccount(OlmAccount, String, String)}.
+     * @param aAccount the account to associate with this session
+     * @param aTheirIdentityKey the identity key of the recipient
+     * @param aTheirOneTimeKey the one time key of the recipient
+     * @return this if operation succeed, null otherwise
+     */
+    public OlmSession initOutboundSessionWithAccount(OlmAccount aAccount, String aTheirIdentityKey, String aTheirOneTimeKey) {
+        OlmSession retObj=null;
+
+        if((null==aAccount) || TextUtils.isEmpty(aTheirIdentityKey) || TextUtils.isEmpty(aTheirOneTimeKey)){
+            Log.e(LOG_TAG, "## initOutboundSession(): invalid input parameters");
+        } else {
+            // set the account of this session
+            mOlmAccount = aAccount;
+
+            if(0 == initOutboundSessionJni(mOlmAccount.getOlmAccountId(), aTheirIdentityKey, aTheirOneTimeKey)) {
+                retObj = this;
+            }
+        }
+
+        return retObj;
+    }
+
+    private native int initOutboundSessionJni(long aOlmAccountId, String aTheirIdentityKey, String aTheirOneTimeKey);
+
+
+    /**
+     * Create a new in-bound session for sending/receiving messages from an
+     * incoming PRE_KEY message.<br>
+     * Public API for {@link #initInboundSessionJni(long, String)}.
+     * This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
+     * @param aAccount the account to associate with this session
+     * @param aOneTimeKeyMsg PRE KEY message
+     * @return this if operation succeed, null otherwise
+     */
+    public OlmSession initInboundSessionWithAccount(OlmAccount aAccount, String aOneTimeKeyMsg) {
+        OlmSession retObj=null;
+
+        if((null==aAccount) || TextUtils.isEmpty(aOneTimeKeyMsg)){
+            Log.e(LOG_TAG, "## initInboundSessionWithAccount(): invalid input parameters");
+        } else {
+            // set the account of this session
+            mOlmAccount = aAccount;
+
+            if( 0 == initInboundSessionJni(mOlmAccount.getOlmAccountId(), aOneTimeKeyMsg)) {
+                retObj = this;
+            }
+        }
+
+        return retObj;
+    }
+
+    private native int initInboundSessionJni(long aOlmAccountId, String aOneTimeKeyMsg);
+
+
+    /**
+     * Create a new in-bound session for sending/receiving messages from an
+     * incoming PRE_KEY message based on the sender identity key.<br>
+     * Public API for {@link #initInboundSessionFromIdKeyJni(long, String, String)}.
+     * This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
+     * @param aAccount the account to associate with this session
+     * @param aTheirIdentityKey the sender identity key
+     * @param aOneTimeKeyMsg PRE KEY message
+     * @return this if operation succeed, null otherwise
+     */
+    public OlmSession initInboundSessionWithAccountFrom(OlmAccount aAccount, String aTheirIdentityKey, String aOneTimeKeyMsg) {
+        OlmSession retObj=null;
+
+        if((null==aAccount) || TextUtils.isEmpty(aOneTimeKeyMsg)){
+            Log.e(LOG_TAG, "## initInboundSessionWithAccount(): invalid input parameters");
+        } else {
+            // set the account of this session
+            mOlmAccount = aAccount;
+
+            if(0 == initInboundSessionFromIdKeyJni(mOlmAccount.getOlmAccountId(), aTheirIdentityKey, aOneTimeKeyMsg)){
+                retObj = this;
+            }
+        }
+
+        return retObj;
+    }
+
+    private native int initInboundSessionFromIdKeyJni(long aOlmAccountId, String aTheirIdentityKey, String aOneTimeKeyMsg);
+
+    /**
+     * Get the session identifier.<br> Will be the same for both ends of the
+     * conversation. The session identifier is returned as a String object.
+     * Session Id sample: "session_id":"M4fOVwD6AABrkTKl"
+     * Public API for {@link #getSessionIdentifierJni()}.
+     * @return the session ID as a String if operation succeed, null otherwise
+     */
+    public String sessionIdentifier() {
+        return getSessionIdentifierJni();
+    }
+
+    private native String getSessionIdentifierJni();
+
+    /**
+     * Checks if the PRE_KEY message is for this in-bound session.<br>
+     * This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
+     * Public API for {@link #matchesInboundSessionJni(String)}.
+     * @param aOneTimeKeyMsg PRE KEY message
+     * @return this if operation succeed, null otherwise
+     */
+    public boolean matchesInboundSession(String aOneTimeKeyMsg) {
+        boolean retCode = false;
+
+        if(0 == matchesInboundSessionJni(aOneTimeKeyMsg)){
+            retCode = true;
+        }
+        return retCode;
+    }
+
+    private native int matchesInboundSessionJni(String aOneTimeKeyMsg);
+
+
+    /**
+     * Checks if the PRE_KEY message is for this in-bound session based on the sender identity key.<br>
+     * This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
+     * Public API for {@link #matchesInboundSessionJni(String)}.
+     * @param aTheirIdentityKey the sender identity key
+     * @param aOneTimeKeyMsg PRE KEY message
+     * @return this if operation succeed, null otherwise
+     */
+    public boolean matchesInboundSessionFrom(String aTheirIdentityKey, String aOneTimeKeyMsg) {
+        boolean retCode = false;
+
+        if(0 == matchesInboundSessionFromIdKeyJni(aTheirIdentityKey, aOneTimeKeyMsg)){
+            retCode = true;
+        }
+        return retCode;
+    }
+
+    private native int matchesInboundSessionFromIdKeyJni(String aTheirIdentityKey, String aOneTimeKeyMsg);
+
+
+    /**
+     * Encrypt a message using the session.<br>
+     * The encrypted message is returned in a OlmMessage object.
+     * Public API for {@link #encryptMessageJni(String, OlmMessage)}.
+     * @param aClearMsg message to encrypted
+     * @return the encrypted message if operation succeed, null otherwise
+     */
+    public OlmMessage encryptMessage(String aClearMsg) {
+        OlmMessage encryptedMsgRetValue = new OlmMessage();
+
+        if(0 != encryptMessageJni(aClearMsg, encryptedMsgRetValue)){
+            encryptedMsgRetValue = null;
+        }
+
+        return encryptedMsgRetValue;
+    }
+
+    private native int encryptMessageJni(String aClearMsg, OlmMessage aEncryptedMsg);
+
+    /**
+     * Decrypt a message using the session.<br>
+     * The encrypted message is given as a OlmMessage object.
+     * @param aEncryptedMsg message to decrypt
+     * @return the decrypted message if operation succeed, null otherwise
+     */
+    public native String decryptMessage(OlmMessage aEncryptedMsg);
+}
+
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/Android.mk b/java/android/OlmLibSdk/olm-sdk/src/main/jni/Android.mk
new file mode 100644
index 0000000000000000000000000000000000000000..26a6a900e64bc17c95a4185fe56d06a08c40b14b
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/Android.mk
@@ -0,0 +1,53 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := olm
+MAJOR := 1
+MINOR := 3
+PATCH := 0
+OLM_VERSION := $(MAJOR).$(MINOR).$(PATCH)
+SRC_ROOT_DIR := ../../../../../../..
+
+$(info LOCAL_PATH=$(LOCAL_PATH))
+$(info SRC_ROOT_DIR=$(SRC_ROOT_DIR))
+$(info OLM_VERSION=$(OLM_VERSION))
+
+LOCAL_CPPFLAGS+= -std=c++11 -Wall
+LOCAL_CONLYFLAGS+= -std=c99
+LOCAL_CFLAGS+= -DOLMLIB_VERSION_MAJOR=$(MAJOR) \
+-DOLMLIB_VERSION_MINOR=$(MINOR) \
+-DOLMLIB_VERSION_PATCH=$(PATCH)
+
+LOCAL_C_INCLUDES+= $(LOCAL_PATH)/$(SRC_ROOT_DIR)/include/ \
+$(LOCAL_PATH)/$(SRC_ROOT_DIR)/lib
+
+$(info LOCAL_C_INCLUDES=$(LOCAL_C_INCLUDES))
+
+LOCAL_SRC_FILES := $(SRC_ROOT_DIR)/src/account.cpp \
+$(SRC_ROOT_DIR)/src/base64.cpp \
+$(SRC_ROOT_DIR)/src/cipher.cpp \
+$(SRC_ROOT_DIR)/src/crypto.cpp \
+$(SRC_ROOT_DIR)/src/memory.cpp \
+$(SRC_ROOT_DIR)/src/message.cpp \
+$(SRC_ROOT_DIR)/src/olm.cpp \
+$(SRC_ROOT_DIR)/src/pickle.cpp \
+$(SRC_ROOT_DIR)/src/ratchet.cpp \
+$(SRC_ROOT_DIR)/src/session.cpp \
+$(SRC_ROOT_DIR)/src/utility.cpp \
+$(SRC_ROOT_DIR)/src/ed25519.c \
+$(SRC_ROOT_DIR)/src/error.c \
+$(SRC_ROOT_DIR)/src/inbound_group_session.c \
+$(SRC_ROOT_DIR)/src/megolm.c \
+$(SRC_ROOT_DIR)/src/outbound_group_session.c \
+$(SRC_ROOT_DIR)/src/pickle_encoding.c \
+$(SRC_ROOT_DIR)/lib/crypto-algorithms/sha256.c \
+$(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
+
+LOCAL_LDLIBS := -llog
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/jni/Application.mk b/java/android/OlmLibSdk/olm-sdk/src/main/jni/Application.mk
new file mode 100644
index 0000000000000000000000000000000000000000..29a4296f64cd6043463ba93d4ba447123a8ca99b
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/Application.mk
@@ -0,0 +1,3 @@
+APP_PLATFORM := android-21
+APP_ABI := arm64-v8a #armeabi-v7a armeabi x86 x86_64
+APP_STL := gnustl_static
\ No newline at end of file
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
new file mode 100644
index 0000000000000000000000000000000000000000..2a5ab6fb226588baafcc5832a748caae6431b6df
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.cpp
@@ -0,0 +1,462 @@
+/*
+ * 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_account.h"
+#include "olm_utility.h"
+
+/**
+* Init memory allocation for account creation.
+* @return valid memory alocation, NULL otherwise
+**/
+OlmAccount* initializeAccountMemory()
+{
+    OlmAccount* accountPtr = NULL;
+    size_t accountSize = olm_account_size();
+
+    if(NULL != (accountPtr=(OlmAccount*)malloc(accountSize)))
+    { // init account object
+      accountPtr = olm_account(accountPtr);
+      LOGD("## initializeAccountMemory(): success - OLM account size=%lu",accountSize);
+    }
+    else
+    {
+      LOGE("## initializeAccountMemory(): failure - OOM");
+    }
+
+    return accountPtr;
+}
+
+/**
+ * Release the account allocation made by initializeAccountMemory().<br>
+ * This method MUST be called when java counter part account instance is done.
+ *
+ */
+JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(releaseAccountJni)(JNIEnv *env, jobject thiz)
+{
+  OlmAccount* accountPtr = NULL;
+
+  LOGD("## releaseAccountJni(): accountPtr=%p",accountPtr);
+
+  if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz)))
+  {
+      LOGE("## releaseAccountJni(): failure - invalid Account ptr=NULL");
+  }
+  else
+  {
+    olm_clear_account(accountPtr);
+
+    LOGD("## releaseAccountJni(): IN");
+    // even if free(NULL) does not crash, logs are performed for debug purpose
+    free(accountPtr);
+    LOGD("## releaseAccountJni(): OUT");
+  }
+}
+
+/**
+* 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.
+* @return the initialized OlmAccount* instance if init succeed, NULL otherwise
+**/
+JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(initNewAccountJni)(JNIEnv *env, jobject thiz)
+{
+    OlmAccount *accountPtr = NULL;
+    uint8_t *randomBuffPtr = NULL;
+    size_t accountRetCode;
+    size_t randomSize;
+
+    // init account memory allocation
+    if(NULL == (accountPtr = initializeAccountMemory()))
+    {
+        LOGE("## initNewAccount(): failure - init account OOM");
+    }
+    else
+    {
+        // allocate random buffer
+        randomSize = olm_create_account_random_length(accountPtr);
+        if(!setRandomInBuffer(&randomBuffPtr, randomSize))
+        {
+            LOGE("## initNewAccount(): failure - random buffer init");
+        }
+        else
+        {
+            // create account
+            accountRetCode = olm_create_account(accountPtr, (void*)randomBuffPtr, randomSize);
+            if(accountRetCode == olm_error()) {
+                const char *errorMsgPtr = olm_account_last_error(accountPtr);
+                LOGE("## initNewAccount(): failure - account creation failed Msg=%s", errorMsgPtr);
+             }
+
+            LOGD("## initNewAccount(): success - OLM account created");
+            LOGD("## initNewAccount(): success - accountPtr=%p (jlong)(intptr_t)accountPtr=%lld",accountPtr,(jlong)(intptr_t)accountPtr);
+        }
+    }
+
+    if(NULL != randomBuffPtr)
+    {
+        free(randomBuffPtr);
+    }
+
+    return (jlong)(intptr_t)accountPtr;
+}
+
+
+
+// *********************************************************************
+// ************************* IDENTITY KEYS API *************************
+// *********************************************************************
+/**
+* Get identity keys: Ed25519 fingerprint key and Curve25519 identity key.<br>
+* The keys are returned in the byte array.
+* @return a valid byte array if operation succeed, null otherwise
+**/
+JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(identityKeysJni)(JNIEnv *env, jobject thiz)
+{
+    OlmAccount* accountPtr = NULL;
+    size_t identityKeysLength;
+    uint8_t *identityKeysBytesPtr;
+    size_t keysResult;
+    jbyteArray byteArrayRetValue = NULL;
+
+    LOGD("## identityKeys(): accountPtr =%p",accountPtr);
+
+    if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz)))
+    {
+        LOGE("## identityKeys(): failure - invalid Account ptr=NULL");
+    }
+    else
+    {   // identity keys allocation
+        identityKeysLength = olm_account_identity_keys_length(accountPtr);
+        if(NULL == (identityKeysBytesPtr=(uint8_t*)malloc(identityKeysLength*sizeof(uint8_t))))
+        {
+            LOGE("## identityKeys(): failure - identity keys array OOM");
+        }
+        else
+        {   // retrieve key pairs in identityKeysBytesPtr
+            keysResult = olm_account_identity_keys(accountPtr, identityKeysBytesPtr, identityKeysLength);
+            if(keysResult == olm_error()) {
+                const char *errorMsgPtr = olm_account_last_error(accountPtr);
+                LOGE("## identityKeys(): failure - error getting identity keys Msg=%s",errorMsgPtr);
+            }
+            else
+            {   // allocate the byte array to be returned to java
+                if(NULL == (byteArrayRetValue=env->NewByteArray(identityKeysLength)))
+                {
+                    LOGE("## identityKeys(): failure - return byte array OOM");
+                }
+                else
+                {
+                    env->SetByteArrayRegion(byteArrayRetValue, 0/*offset*/, identityKeysLength, (const jbyte*)identityKeysBytesPtr);
+                    LOGD("## identityKeys(): success - result=%ld", keysResult);
+                }
+            }
+
+            free(identityKeysBytesPtr);
+        }
+    }
+
+    return byteArrayRetValue;
+}
+
+// *********************************************************************
+// ************************* ONE TIME KEYS API *************************
+// *********************************************************************
+/**
+ * Get the maximum number of "one time keys" the account can store.
+ *
+**/
+JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(maxOneTimeKeys)(JNIEnv *env, jobject thiz)
+{
+    OlmAccount* accountPtr = NULL;
+    size_t maxKeys = -1;
+
+    if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz)))
+    {
+        LOGE("## maxOneTimeKey(): failure - invalid Account ptr=NULL");
+    }
+    else
+    {
+        maxKeys = olm_account_max_number_of_one_time_keys(accountPtr);
+    }
+    LOGD("## maxOneTimeKey(): Max keys=%ld", maxKeys);
+
+    return (jlong)maxKeys;
+}
+
+/**
+ * Generate "one time keys".
+ * @param aNumberOfKeys number of keys to generate
+ * @return ERROR_CODE_OK if operation succeed, ERROR_CODE_KO otherwise
+**/
+JNIEXPORT jint OLM_ACCOUNT_FUNC_DEF(generateOneTimeKeys)(JNIEnv *env, jobject thiz, jint aNumberOfKeys)
+{
+    OlmAccount *accountPtr = NULL;
+    uint8_t *randomBufferPtr = NULL;
+    jint retCode = ERROR_CODE_KO;
+    size_t randomLength;
+    size_t result;
+
+
+    if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz)))
+    {
+        LOGE("## generateOneTimeKeys(): failure - invalid Account ptr");
+    }
+    else
+    {   // keys memory allocation
+        randomLength = olm_account_generate_one_time_keys_random_length(accountPtr, (size_t)aNumberOfKeys);
+        LOGD("## generateOneTimeKeys(): randomLength=%ld", randomLength);
+
+        if(!setRandomInBuffer(&randomBufferPtr, randomLength))
+        {
+            LOGE("## generateOneTimeKeys(): failure - random buffer init");
+        }
+        else
+        {
+            LOGD("## generateOneTimeKeys(): accountPtr =%p aNumberOfKeys=%d",accountPtr, aNumberOfKeys);
+
+            // retrieve key pairs in keysBytesPtr
+            result = olm_account_generate_one_time_keys(accountPtr, (size_t)aNumberOfKeys, (void*)randomBufferPtr, randomLength);
+            if(result == olm_error()) {
+                const char *errorMsgPtr = olm_account_last_error(accountPtr);
+                LOGE("## generateOneTimeKeys(): failure - error generating one time keys Msg=%s",errorMsgPtr);
+            }
+            else
+            {
+                retCode = ERROR_CODE_OK;
+                LOGD("## generateOneTimeKeys(): success - result=%ld", result);
+            }
+        }
+    }
+
+    if(NULL != randomBufferPtr)
+    {
+        free(randomBufferPtr);
+    }
+
+    return retCode;
+}
+
+/**
+ * Get "one time keys".<br>
+ * Return the public parts of the unpublished "one time keys" for the account
+ * @return a valid byte array if operation succeed, null otherwise
+**/
+JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(oneTimeKeysJni)(JNIEnv *env, jobject thiz)
+{
+    OlmAccount* accountPtr = NULL;
+    size_t keysLength;
+    uint8_t *keysBytesPtr;
+    size_t keysResult;
+    jbyteArray byteArrayRetValue = NULL;
+
+    LOGD("## oneTimeKeys(): accountPtr =%p",accountPtr);
+
+    if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz)))
+    {
+        LOGE("## oneTimeKeys(): failure - invalid Account ptr");
+    }
+    else
+    {   // keys memory allocation
+        keysLength = olm_account_one_time_keys_length(accountPtr);
+        if(NULL == (keysBytesPtr=(uint8_t *)malloc(keysLength*sizeof(uint8_t))))
+        {
+            LOGE("## oneTimeKeys(): failure - one time keys array OOM");
+        }
+        else
+        {   // retrieve key pairs in keysBytesPtr
+            keysResult = olm_account_one_time_keys(accountPtr, keysBytesPtr, keysLength);
+            if(keysResult == olm_error()) {
+                const char *errorMsgPtr = olm_account_last_error(accountPtr);
+                LOGE("## oneTimeKeys(): failure - error getting one time keys Msg=%s",errorMsgPtr);
+            }
+            else
+            {   // allocate the byte array to be returned to java
+                if(NULL == (byteArrayRetValue=env->NewByteArray(keysLength)))
+                {
+                    LOGE("## oneTimeKeys(): failure - return byte array OOM");
+                }
+                else
+                {
+                    env->SetByteArrayRegion(byteArrayRetValue, 0/*offset*/, keysLength, (const jbyte*)keysBytesPtr);
+                    LOGD("## oneTimeKeys(): success");
+                }
+            }
+
+            free(keysBytesPtr);
+        }
+    }
+
+    return byteArrayRetValue;
+}
+
+/**
+ * Remove the "one time keys"  that the session used from the account.
+ * Return the public parts of the unpublished "one time keys" for the account
+ * @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)
+{
+    jint retCode = ERROR_CODE_KO;
+    OlmAccount* accountPtr = NULL;
+    OlmSession* sessionPtr = (OlmSession*)aNativeOlmSessionId;
+    size_t result;
+
+    if(NULL == sessionPtr)
+    {
+        LOGE("## removeOneTimeKeysForSession(): failure - invalid session ptr");
+    }
+    else if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz)))
+    {
+        LOGE("## removeOneTimeKeysForSession(): failure - invalid account ptr");
+    }
+    else
+    {
+        result = olm_remove_one_time_keys(accountPtr, sessionPtr);
+        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);
+
+            retCode = ERROR_CODE_NO_MATCHING_ONE_TIME_KEYS;
+        }
+        else
+        {
+            retCode = ERROR_CODE_OK;
+            LOGD("## removeOneTimeKeysForSession(): success");
+        }
+    }
+
+    return retCode;
+}
+
+/**
+ * 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)
+{
+    jint retCode = ERROR_CODE_OK;
+    OlmAccount* accountPtr = NULL;
+    size_t result;
+
+    if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz)))
+    {
+        LOGE("## markOneTimeKeysPublished(): failure - invalid account ptr");
+        retCode = ERROR_CODE_KO;
+    }
+    else
+    {
+        result = olm_account_mark_keys_as_published(accountPtr);
+        if(result == olm_error())
+        {
+            const char *errorMsgPtr = olm_account_last_error(accountPtr);
+            LOGW("## markOneTimeKeysPublished(): failure - Msg=%s",errorMsgPtr);
+            retCode = ERROR_CODE_KO;
+        }
+        else
+        {
+            LOGD("## markOneTimeKeysPublished(): success - retCode=%ld",result);
+        }
+    }
+
+    return retCode;
+}
+
+/**
+ * Sign a message with the ed25519 key (fingerprint) for this account.<br>
+ * The signed message is returned by the function.
+ * @param aMessage message to sign
+ * @return the signed message, null otherwise
+**/
+JNIEXPORT jstring OLM_ACCOUNT_FUNC_DEF(signMessage)(JNIEnv *env, jobject thiz, jstring aMessage)
+{
+    OlmAccount* accountPtr = NULL;
+    size_t signatureLength;
+    void* signedMsgPtr;
+    size_t resultSign;
+    jstring signedMsgRetValue = NULL;
+
+    if(NULL == aMessage)
+    {
+        LOGE("## signMessage(): failure - invalid aMessage param");
+    }
+    else if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz)))
+    {
+        LOGE("## signMessage(): failure - invalid account ptr");
+    }
+    else
+    {
+        // convert message from JAVA to C string
+        const char* messageToSign = env->GetStringUTFChars(aMessage, 0);
+        if(NULL == messageToSign)
+        {
+            LOGE("## signMessage(): failure - message JNI allocation OOM");
+        }
+        else
+        {
+            int messageLength = env->GetStringUTFLength(aMessage);
+
+            // signature memory allocation
+            signatureLength = olm_account_signature_length(accountPtr);
+            if(NULL == (signedMsgPtr = (void*)malloc(signatureLength*sizeof(uint8_t))))
+            {
+                LOGE("## signMessage(): failure - signature allocation OOM");
+            }
+            else
+            {   // sign message
+                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);
+                }
+                else
+                {   // convert to jstring
+                    // TODO check how UTF conversion can impact the content?
+                    // why not consider return jbyteArray? and convert in JAVA side..
+                    signedMsgRetValue = env->NewStringUTF((const char*)signedMsgPtr); // UTF8
+                    LOGD("## signMessage(): success - retCode=%ld",resultSign);
+                }
+
+                free(signedMsgPtr);
+            }
+
+            // release messageToSign
+            env->ReleaseStringUTFChars(aMessage, messageToSign);
+        }
+    }
+
+    return signedMsgRetValue;
+}
+
+
+JNIEXPORT jstring OLM_MANAGER_FUNC_DEF(getOlmLibVersion)(JNIEnv* env, jobject thiz)
+{
+  uint8_t majorVer=0, minorVer=0, patchVer=0;
+  jstring returnValueStr=0;
+  char buff[150];
+
+  olm_get_library_version(&majorVer, &minorVer, &patchVer);
+  LOGD("## getOlmLibVersion(): Major=%d Minor=%d Patch=%d", majorVer, minorVer, patchVer);
+
+  snprintf(buff, sizeof(buff), " V%d.%d.%d", majorVer, minorVer, patchVer);
+  returnValueStr = env->NewStringUTF((const char*)buff);
+
+  return returnValueStr;
+}
+
+
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
new file mode 100644
index 0000000000000000000000000000000000000000..33fc65c12c3e5e0a81999c3020a5f4530b6b2a11
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.h
@@ -0,0 +1,36 @@
+#ifndef _OMLACCOUNT_H
+#define _OMLACCOUNT_H
+
+#include "olm_jni.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)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmManager_getOlmLibVersion(JNIEnv *env, jobject thiz);
+
+// account creation/destruction
+JNIEXPORT void JNICALL Java_org_matrix_olm_OlmAccount_releaseAccountJni(JNIEnv *env, jobject thiz);
+JNIEXPORT jlong JNICALL Java_org_matrix_olm_OlmAccount_initNewAccountJni(JNIEnv *env, jobject thiz);
+
+// identity keys
+JNIEXPORT jbyteArray JNICALL Java_org_matrix_olm_OlmAccount_identityKeysJni(JNIEnv *env, jobject thiz);
+
+// one time keys
+JNIEXPORT jbyteArray JNICALL Java_org_matrix_olm_OlmAccount_oneTimeKeysJni(JNIEnv *env, jobject thiz);
+JNIEXPORT jlong JNICALL Java_org_matrix_olm_OlmAccount_maxOneTimeKeys(JNIEnv *env, jobject thiz);
+JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmAccount_generateOneTimeKeys(JNIEnv *env, jobject thiz, jint aNumberOfKeys);
+JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmAccount_removeOneTimeKeysForSession(JNIEnv *env, jobject thiz, jlong aNativeOlmSessionId);
+JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmAccount_markOneTimeKeysAsPublished(JNIEnv *env, jobject thiz);
+
+// signing
+JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmAccount_signMessage(JNIEnv *env, jobject thiz, jstring aMessage);
+
+#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
new file mode 100644
index 0000000000000000000000000000000000000000..d811c3f728b540914fad0913746cd5c44e37b206
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_jni.h
@@ -0,0 +1,48 @@
+#ifndef _OMLJNI_H
+#define _OMLJNI_H
+
+#include <cstdlib>
+#include <cstdio>
+#include <string>
+#include <sstream>
+#include <map>
+#include <jni.h>
+#include <android/log.h>
+
+#include "olm/olm.h"
+
+#define TAG "OlmJniNative"
+
+/* logging macros */
+#define ENABLE_LOGS
+
+#ifdef ENABLE_LOGS
+    #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
+    #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
+    #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
+    #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
+#else
+    #define LOGV(...)
+    #define LOGD(...)
+    #define LOGW(...)
+    #define LOGE(...)
+#endif
+
+#define FUNC_DEF(class_name,func_name) JNICALL Java_org_matrix_olm_##class_name##_##func_name
+
+// Error codes definition
+static const int ERROR_CODE_OK = 0;
+static const int ERROR_CODE_NO_MATCHING_ONE_TIME_KEYS = ERROR_CODE_OK+1;
+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_session.cpp b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bdc22390d731448f4e0848dff0cca4589394abec
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.cpp
@@ -0,0 +1,731 @@
+/*
+ * 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_session.h"
+#include "olm_utility.h"
+
+
+/**
+* Init memory allocation for a session creation.<br>
+* Make sure releaseSessionJni() is called when one is done with the session instance.
+* @return valid memory allocation, NULL otherwise
+**/
+OlmSession* initializeSessionMemory()
+{
+    OlmSession* sessionPtr = NULL;
+    size_t sessionSize = olm_session_size();
+
+    if(NULL != (sessionPtr=(OlmSession*)malloc(sessionSize)))
+    { // init session object
+      sessionPtr = olm_session(sessionPtr);
+      LOGD("## initializeSessionMemory(): success - OLM session size=%lu",sessionSize);
+    }
+    else
+    {
+      LOGE("## initializeSessionMemory(): failure - OOM");
+    }
+
+    return sessionPtr;
+}
+
+JNIEXPORT void OLM_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz)
+{
+  OlmSession* sessionPtr = NULL;
+
+  if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
+  {
+      LOGE("## releaseSessionJni(): failure - invalid Session ptr=NULL");
+  }
+  else
+  {
+    olm_clear_session(sessionPtr);
+
+    LOGD("## releaseSessionJni(): IN");
+    // even if free(NULL) does not crash, logs are performed for debug purpose
+    free(sessionPtr);
+    LOGD("## releaseSessionJni(): OUT");
+  }
+}
+
+/**
+* Initialize a new 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 (OlmSession* => jlong) platform independent.
+* @return the initialized OlmSession* instance if init succeed, NULL otherwise
+**/
+JNIEXPORT jlong OLM_SESSION_FUNC_DEF(initNewSessionJni)(JNIEnv *env, jobject thiz)
+{
+    OlmSession* sessionPtr = NULL;
+
+    // init account memory allocation
+    if(NULL == (sessionPtr = initializeSessionMemory()))
+    {
+        LOGE("## initNewSessionJni(): failure - init session OOM");
+    }
+    else
+    {
+       LOGD("## initNewSessionJni(): success - OLM session created");
+    }
+
+    return (jlong)(intptr_t)sessionPtr;
+}
+
+// *********************************************************************
+// ********************** OUTBOUND SESSION *****************************
+// *********************************************************************
+/**
+* Create a new in-bound session for sending/receiving messages from an
+* incoming PRE_KEY message.<br> The recipient is defined as the entity
+* with whom the session is established.
+* @param aOlmAccountId account instance
+* @param aTheirIdentityKey the identity key of the recipient
+* @param aTheirOneTimeKey the one time key of the recipient
+* @return ERROR_CODE_OK if operation succeed, ERROR_CODE_KO otherwise
+**/
+JNIEXPORT jint OLM_SESSION_FUNC_DEF(initOutboundSessionJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aTheirIdentityKey, jstring aTheirOneTimeKey)
+{
+    jint retCode = ERROR_CODE_KO;
+    OlmSession* sessionPtr = NULL;
+    OlmAccount* accountPtr = NULL;
+    const char* theirIdentityKeyPtr = NULL;
+    const char* theirOneTimeKeyPtr = NULL;
+    uint8_t *randomBuffPtr = NULL;
+    size_t sessionResult;
+
+    if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
+    {
+        LOGE("## initOutboundSessionJni(): failure - invalid Session ptr=NULL");
+    }
+    else if(NULL == (accountPtr = (OlmAccount*)aOlmAccountId))
+    {
+        LOGE("## initOutboundSessionJni(): failure - invalid Account ptr=NULL");
+    }
+    else if((0==aTheirIdentityKey) || (0==aTheirOneTimeKey))
+    {
+        LOGE("## initOutboundSessionJni(): failure - invalid keys");
+    }
+    else
+    {   // allocate random buffer
+        size_t randomSize = olm_create_outbound_session_random_length(sessionPtr);
+        if((0!=randomSize) && !setRandomInBuffer(&randomBuffPtr, randomSize))
+        {
+            LOGE("## initOutboundSessionJni(): failure - random buffer init");
+        }
+        else
+        {   // convert identity & one time keys to C strings
+            if(NULL == (theirIdentityKeyPtr = env->GetStringUTFChars(aTheirIdentityKey, 0)))
+            {
+                LOGE("## initOutboundSessionJni(): failure - identityKey JNI allocation OOM");
+            }
+            else if(NULL == (theirOneTimeKeyPtr = env->GetStringUTFChars(aTheirOneTimeKey, 0)))
+            {
+                LOGE("## initOutboundSessionJni(): failure - one time Key JNI allocation OOM");
+            }
+            else
+            {
+                size_t theirIdentityKeyLength = (size_t)env->GetStringUTFLength(aTheirIdentityKey);
+                size_t theirOneTimeKeyLength  = (size_t)env->GetStringUTFLength(aTheirOneTimeKey);
+                LOGD("## initOutboundSessionJni(): identityKey=%s oneTimeKey=%s",theirIdentityKeyPtr,theirOneTimeKeyPtr);
+
+                sessionResult = olm_create_outbound_session(sessionPtr,
+                                                            accountPtr,
+                                                            theirIdentityKeyPtr,
+                                                            theirIdentityKeyLength,
+                                                            theirOneTimeKeyPtr,
+                                                            theirOneTimeKeyLength,
+                                                            (void*)randomBuffPtr,
+                                                            randomSize);
+                if(sessionResult == olm_error()) {
+                    const char *errorMsgPtr = olm_session_last_error(sessionPtr);
+                    LOGE("## initOutboundSessionJni(): failure - session creation  Msg=%s",errorMsgPtr);
+                }
+                else
+                {
+                    retCode = ERROR_CODE_OK;
+                    LOGD("## initOutboundSessionJni(): success - result=%ld", sessionResult);
+                }
+            }
+        }
+    }
+
+     // **** free mem alloc ***
+     if(NULL!= randomBuffPtr)
+     {
+         free(randomBuffPtr);
+     }
+
+     if(NULL!= theirIdentityKeyPtr)
+     {
+         env->ReleaseStringUTFChars(aTheirIdentityKey, theirIdentityKeyPtr);
+     }
+
+     if(NULL!= theirOneTimeKeyPtr)
+     {
+         env->ReleaseStringUTFChars(aTheirOneTimeKey, theirOneTimeKeyPtr);
+     }
+
+    return retCode;
+}
+
+
+// *********************************************************************
+// *********************** INBOUND SESSION *****************************
+// *********************************************************************
+/**
+ * Create a new in-bound session for sending/receiving messages from an
+ * incoming PRE_KEY message.<br>
+ * @param aOlmAccountId account instance
+ * @param aOneTimeKeyMsg PRE_KEY message
+ * @return ERROR_CODE_OK if operation succeed, ERROR_CODE_KO otherwise
+ */
+JNIEXPORT jint OLM_SESSION_FUNC_DEF(initInboundSessionJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aOneTimeKeyMsg)
+{
+    jint retCode = ERROR_CODE_KO;
+    OlmSession *sessionPtr = NULL;
+    OlmAccount *accountPtr = NULL;
+    const char *messagePtr = NULL;
+    size_t sessionResult;
+
+    if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
+    {
+        LOGE("## initInboundSessionJni(): failure - invalid Session ptr=NULL");
+    }
+    else if(NULL == (accountPtr = (OlmAccount*)aOlmAccountId))
+    {
+        LOGE("## initInboundSessionJni(): failure - invalid Account ptr=NULL");
+    }
+    else if(0==aOneTimeKeyMsg)
+    {
+        LOGE("## initInboundSessionJni(): failure - invalid message");
+    }
+    else
+    {   // convert message to C strings
+        if(NULL == (messagePtr = env->GetStringUTFChars(aOneTimeKeyMsg, 0)))
+        {
+            LOGE("## initInboundSessionJni(): failure - message JNI allocation OOM");
+        }
+        else
+        {
+            size_t messageLength = (size_t)env->GetStringUTFLength(aOneTimeKeyMsg);
+            LOGD("## initInboundSessionJni(): messageLength=%d message=%s", messageLength, messagePtr);
+
+            sessionResult = olm_create_inbound_session(sessionPtr, accountPtr, (void*)messagePtr , messageLength);
+            if(sessionResult == olm_error()) {
+                const char *errorMsgPtr = olm_session_last_error(sessionPtr);
+                LOGE("## initInboundSessionJni(): failure - init inbound session creation  Msg=%s",errorMsgPtr);
+            }
+            else
+            {
+                retCode = ERROR_CODE_OK;
+                LOGD("## initInboundSessionJni(): success - result=%ld", sessionResult);
+            }
+
+            // free local alloc
+            env->ReleaseStringUTFChars(aOneTimeKeyMsg, messagePtr);
+        }
+    }
+    return retCode;
+}
+
+/**
+ * Create a new in-bound session for sending/receiving messages from an
+ * incoming PRE_KEY message based on the recipient identity key.<br>
+ * @param aOlmAccountId account instance
+ * @param aTheirIdentityKey the identity key of the recipient
+ * @param aOneTimeKeyMsg encrypted message
+ * @return ERROR_CODE_OK if operation succeed, ERROR_CODE_KO otherwise
+ */
+JNIEXPORT jint OLM_SESSION_FUNC_DEF(initInboundSessionFromIdKeyJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aTheirIdentityKey, jstring aOneTimeKeyMsg)
+{
+    jint retCode = ERROR_CODE_KO;
+    OlmSession *sessionPtr = NULL;
+    OlmAccount *accountPtr = NULL;
+    const char *messagePtr = NULL;
+    const char *theirIdentityKeyPtr = NULL;
+    size_t sessionResult;
+
+    if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
+    {
+        LOGE("## initInboundSessionFromIdKeyJni(): failure - invalid Session ptr=NULL");
+    }
+    else if(NULL == (accountPtr = (OlmAccount*)aOlmAccountId))
+    {
+        LOGE("## initInboundSessionFromIdKeyJni(): failure - invalid Account ptr=NULL");
+    }
+    else if(0 == aTheirIdentityKey)
+    {
+        LOGE("## initInboundSessionFromIdKeyJni(): failure - invalid theirIdentityKey");
+    }
+    else if(0==aOneTimeKeyMsg)
+    {
+        LOGE("## initInboundSessionJni(): failure - invalid one time key message");
+    }
+    else if(NULL == (messagePtr = env->GetStringUTFChars(aOneTimeKeyMsg, 0)))
+    {
+        LOGE("## initInboundSessionFromIdKeyJni(): failure - message JNI allocation OOM");
+    }
+    else if(NULL == (theirIdentityKeyPtr = env->GetStringUTFChars(aTheirIdentityKey, 0)))
+    {
+        LOGE("## initInboundSessionFromIdKeyJni(): failure - theirIdentityKey JNI allocation OOM");
+    }
+    else
+    {
+        size_t messageLength = (size_t)env->GetStringUTFLength(aOneTimeKeyMsg);
+        size_t theirIdentityKeyLength = (size_t)env->GetStringUTFLength(aTheirIdentityKey);
+
+        LOGD("## initInboundSessionFromIdKeyJni(): message=%s messageLength=%lu",messagePtr,messageLength);
+
+        sessionResult = olm_create_inbound_session_from(sessionPtr, accountPtr, theirIdentityKeyPtr, theirIdentityKeyLength, (void*)messagePtr , messageLength);
+        if(sessionResult == olm_error()) {
+            const char *errorMsgPtr = olm_session_last_error(sessionPtr);
+            LOGE("## initInboundSessionFromIdKeyJni(): failure - init inbound session creation  Msg=%s",errorMsgPtr);
+        }
+        else
+        {
+            retCode = ERROR_CODE_OK;
+            LOGD("## initInboundSessionFromIdKeyJni(): success - result=%ld", sessionResult);
+        }
+     }
+
+     // free local alloc
+     if(NULL!= messagePtr)
+     {
+         env->ReleaseStringUTFChars(aOneTimeKeyMsg, messagePtr);
+     }
+     if(NULL!= theirIdentityKeyPtr)
+     {
+         env->ReleaseStringUTFChars(aTheirIdentityKey, theirIdentityKeyPtr);
+     }
+
+    return retCode;
+}
+
+/**
+ * Checks if the PRE_KEY message is for this in-bound session.<br>
+ * This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
+ * @param aOneTimeKeyMsg PRE KEY message
+ * @return ERROR_CODE_OK if match, ERROR_CODE_KO otherwise
+ */
+JNIEXPORT jint OLM_SESSION_FUNC_DEF(matchesInboundSessionJni)(JNIEnv *env, jobject thiz, jstring aOneTimeKeyMsg)
+{
+    jint retCode = ERROR_CODE_KO;
+    OlmSession *sessionPtr = NULL;
+    const char *messagePtr = NULL;
+
+    if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
+    {
+        LOGE("## matchesInboundSessionJni(): failure - invalid Session ptr=NULL");
+    }
+    else if(0==aOneTimeKeyMsg)
+    {
+        LOGE("## matchesInboundSessionJni(): failure - invalid one time key message");
+    }
+    else if(NULL == (messagePtr = env->GetStringUTFChars(aOneTimeKeyMsg, 0)))
+    {
+        LOGE("## matchesInboundSessionJni(): failure - one time key JNI allocation OOM");
+    }
+    else
+    {
+        size_t messageLength = (size_t)env->GetStringUTFLength(aOneTimeKeyMsg);
+
+        size_t matchResult = olm_matches_inbound_session(sessionPtr, (void*)messagePtr , messageLength);
+        if(matchResult == olm_error()) {
+            const char *errorMsgPtr = olm_session_last_error(sessionPtr);
+            LOGE("## matchesInboundSessionJni(): failure - no match  Msg=%s",errorMsgPtr);
+        }
+        else
+        {
+            retCode = ERROR_CODE_OK;
+            LOGD("## matchesInboundSessionJni(): success - result=%ld", matchResult);
+        }
+    }
+
+    // free local alloc
+    if(NULL!= messagePtr)
+    {
+     env->ReleaseStringUTFChars(aOneTimeKeyMsg, messagePtr);
+    }
+
+    return retCode;
+}
+
+
+/**
+ * Checks if the PRE_KEY message is for this in-bound session based on the sender identity key.<br>
+ * This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
+ * @param aTheirIdentityKey the identity key of the sender
+ * @param aOneTimeKeyMsg PRE KEY message
+ * @return ERROR_CODE_OK if match, ERROR_CODE_KO otherwise
+ */
+JNIEXPORT jint JNICALL OLM_SESSION_FUNC_DEF(matchesInboundSessionFromIdKeyJni)(JNIEnv *env, jobject thiz, jstring aTheirIdentityKey, jstring aOneTimeKeyMsg)
+{
+    jint retCode = ERROR_CODE_KO;
+    OlmSession *sessionPtr = NULL;
+    const char *messagePtr = NULL;
+    const char *theirIdentityKeyPtr = NULL;
+
+    if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
+    {
+        LOGE("## matchesInboundSessionFromIdKeyJni(): failure - invalid Session ptr=NULL");
+    }
+    else if(0 == aTheirIdentityKey)
+    {
+        LOGE("## matchesInboundSessionFromIdKeyJni(): failure - invalid theirIdentityKey");
+    }
+    else if(NULL == (theirIdentityKeyPtr = env->GetStringUTFChars(aTheirIdentityKey, 0)))
+    {
+        LOGE("## matchesInboundSessionFromIdKeyJni(): failure - theirIdentityKey JNI allocation OOM");
+    }
+    else if(0==aOneTimeKeyMsg)
+    {
+        LOGE("## matchesInboundSessionFromIdKeyJni(): failure - invalid one time key message");
+    }
+    else if(NULL == (messagePtr = env->GetStringUTFChars(aOneTimeKeyMsg, 0)))
+    {
+        LOGE("## matchesInboundSessionFromIdKeyJni(): failure - one time key JNI allocation OOM");
+    }
+    else
+    {
+        size_t identityKeyLength = (size_t)env->GetStringUTFLength(aTheirIdentityKey);
+        size_t messageLength = (size_t)env->GetStringUTFLength(aOneTimeKeyMsg);
+
+        size_t matchResult = olm_matches_inbound_session_from(sessionPtr, (void const *)theirIdentityKeyPtr, identityKeyLength, (void*)messagePtr , messageLength);
+        if(matchResult == olm_error()) {
+            const char *errorMsgPtr = olm_session_last_error(sessionPtr);
+            LOGE("## matchesInboundSessionFromIdKeyJni(): failure - no match  Msg=%s",errorMsgPtr);
+        }
+        else
+        {
+            retCode = ERROR_CODE_OK;
+            LOGD("## matchesInboundSessionFromIdKeyJni(): success - result=%lu", matchResult);
+        }
+    }
+
+    // free local alloc
+    if(NULL!= theirIdentityKeyPtr)
+    {
+     env->ReleaseStringUTFChars(aTheirIdentityKey, theirIdentityKeyPtr);
+    }
+
+    if(NULL!= messagePtr)
+    {
+     env->ReleaseStringUTFChars(aOneTimeKeyMsg, messagePtr);
+    }
+
+    return retCode;
+}
+
+
+/**
+ * Encrypt a message using the session.<br>
+ * @param aClearMsg clear text message
+ * @param [out] aEncryptedMsg ciphered message
+ * @return ERROR_CODE_OK if encrypt operation succeed, ERROR_CODE_KO otherwise
+ */
+JNIEXPORT jint OLM_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobject thiz, jstring aClearMsg, jobject aEncryptedMsg)
+{
+    jint retCode = ERROR_CODE_KO;
+    OlmSession *sessionPtr = NULL;
+    const char *clearMsgPtr = NULL;
+    uint8_t *randomBuffPtr = NULL;
+    void *encryptedMsgPtr = NULL;
+    jclass encryptedMsgJClass = 0;
+    jfieldID encryptedMsgFieldId;
+    jfieldID typeMsgFieldId;
+
+    LOGD("## encryptMessageJni(): IN ");
+
+    if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
+    {
+        LOGE("## encryptMessageJni(): failure - invalid Session ptr=NULL");
+    }
+    else if(0 == aClearMsg)
+    {
+        LOGE("## encryptMessageJni(): failure - invalid clear message");
+    }
+    else if(0 == aEncryptedMsg)
+    {
+        LOGE("## encryptMessageJni(): failure - invalid clear message");
+    }
+    else if(NULL == (clearMsgPtr = env->GetStringUTFChars(aClearMsg, 0)))
+    {
+        LOGE("## encryptMessageJni(): failure - clear message JNI allocation OOM");
+    }
+    else if(0 == (encryptedMsgJClass = env->GetObjectClass(aEncryptedMsg)))
+    {
+        LOGE("## encryptMessageJni(): failure - unable to get crypted message class");
+    }
+    else if(0 == (encryptedMsgFieldId = env->GetFieldID(encryptedMsgJClass,"mCipherText","Ljava/lang/String;")))
+    {
+        LOGE("## encryptMessageJni(): failure - unable to get message field");
+    }
+    else if(0 == (typeMsgFieldId = env->GetFieldID(encryptedMsgJClass,"mType","J")))
+    {
+        LOGE("## encryptMessageJni(): failure - unable to get message type field");
+    }
+    else
+    {
+        // get message type
+        size_t messageType = olm_encrypt_message_type(sessionPtr);
+
+        // compute random buffer
+        // Note: olm_encrypt_random_length() can return 0, which means
+        // it just does not need new random data to encrypt a new message
+        size_t randomLength = olm_encrypt_random_length(sessionPtr);
+
+        if((0!=randomLength) && !setRandomInBuffer(&randomBuffPtr, randomLength))
+        {
+            LOGE("## encryptMessageJni(): failure - random buffer init");
+        }
+        else
+        {
+            // alloc buffer for encrypted message
+            size_t clearMsgLength = (size_t)env->GetStringUTFLength(aClearMsg);
+            size_t encryptedMsgLength = olm_encrypt_message_length(sessionPtr, clearMsgLength);
+            if(NULL == (encryptedMsgPtr = (void*)malloc(encryptedMsgLength*sizeof(uint8_t))))
+            {
+                LOGE("## encryptMessageJni(): failure - encryptedMsgPtr buffer OOM");
+            }
+            else
+            {
+                if(0==randomLength)
+                {
+                    LOGW("## encryptMessageJni(): random buffer is not required");
+                }
+
+                LOGD("## encryptMessageJni(): messageType=%lu randomLength=%lu clearMsgLength=%lu encryptedMsgLength=%lu",messageType,randomLength, clearMsgLength, encryptedMsgLength);
+                // encrypt message
+                size_t result = olm_encrypt(sessionPtr,
+                                            (void const *)clearMsgPtr,
+                                            clearMsgLength,
+                                            randomBuffPtr,
+                                            randomLength,
+                                            encryptedMsgPtr,
+                                            encryptedMsgLength);
+                if(result == olm_error())
+                {
+                    const char *errorMsgPtr = olm_session_last_error(sessionPtr);
+                    LOGE("## encryptMessageJni(): failure - Msg=%s",errorMsgPtr);
+                }
+                else
+                {
+                    // update encrypted buffer size
+                    (static_cast<char*>(encryptedMsgPtr))[result] = static_cast<char>('\0');
+
+                    // update message type: PRE KEY or normal
+                    env->SetLongField(aEncryptedMsg, typeMsgFieldId, (jlong)messageType);
+
+                    // update message: encryptedMsgPtr => encryptedJstring
+                    jstring encryptedJstring = env->NewStringUTF((const char*)encryptedMsgPtr);
+                    size_t encryptedUtfMsgLength = (size_t)env->GetStringUTFLength(encryptedJstring);
+                    env->SetObjectField(aEncryptedMsg, encryptedMsgFieldId, (jobject)encryptedJstring);
+
+                    retCode = ERROR_CODE_OK;
+                    LOGD("## encryptMessageJni(): success - result=%lu Type=%lu utfLength=%lu encryptedMsg=%s", result, messageType, encryptedUtfMsgLength, (const char*)encryptedMsgPtr);
+                }
+            }
+        }
+    }
+
+    // free alloc
+    if(NULL != clearMsgPtr)
+    {
+     env->ReleaseStringUTFChars(aClearMsg, clearMsgPtr);
+    }
+
+    if(NULL != randomBuffPtr)
+    {
+        free(randomBuffPtr);
+    }
+
+    if(NULL != encryptedMsgPtr)
+    {
+        free(encryptedMsgPtr);
+    }
+
+    return retCode;
+}
+
+
+/**
+ * Decrypt a message using the session.<br>
+ * @param aEncryptedMsg message to decrypt
+ * @return decrypted message if operation succeed, null otherwise
+ */
+JNIEXPORT jstring OLM_SESSION_FUNC_DEF(decryptMessage)(JNIEnv *env, jobject thiz, jobject aEncryptedMsg)
+{
+    jstring decryptedMsgRetValue = 0;
+    jclass encryptedMsgJclass = 0;
+    jstring encryptedMsgJstring = 0; // <= obtained from encryptedMsgFieldId
+    // field IDs
+    jfieldID encryptedMsgFieldId;
+    jfieldID typeMsgFieldId;
+    // ptrs
+    OlmSession *sessionPtr = NULL;
+    const char *encryptedMsgPtr = NULL; // <= obtained from encryptedMsgJstring
+    void *plainTextMsgPtr = NULL;
+    char *tempEncryptedPtr = NULL;
+
+    LOGD("## decryptMessage(): IN ");
+
+    if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
+    {
+        LOGE("##  decryptMessage(): failure - invalid Session ptr=NULL");
+    }
+    else if(0 == aEncryptedMsg)
+    {
+        LOGE("##  decryptMessage(): failure - invalid clear message");
+    }
+    else if(0 == (encryptedMsgJclass = env->GetObjectClass(aEncryptedMsg)))
+    {
+        LOGE("##  decryptMessage(): failure - unable to get crypted message class");
+    }
+    else if(0 == (encryptedMsgFieldId = env->GetFieldID(encryptedMsgJclass,"mCipherText","Ljava/lang/String;")))
+    {
+        LOGE("##  decryptMessage(): failure - unable to get message field");
+    }
+    else if(0 == (typeMsgFieldId = env->GetFieldID(encryptedMsgJclass,"mType","J")))
+    {
+        LOGE("##  decryptMessage(): failure - unable to get message type field");
+    }
+    else if(0 == (encryptedMsgJstring = (jstring)env->GetObjectField(aEncryptedMsg, encryptedMsgFieldId)))
+    {
+        LOGE("##  decryptMessage(): failure - JNI encrypted object ");
+    }
+    else if(0 == (encryptedMsgPtr = env->GetStringUTFChars(encryptedMsgJstring, 0)))
+    {
+        LOGE("##  decryptMessage(): failure - encrypted message JNI allocation OOM");
+    }
+    else
+    {
+        // get message type
+        size_t encryptedMsgType = (size_t)env->GetLongField(aEncryptedMsg, typeMsgFieldId);
+        // get encrypted message length
+        size_t encryptedMsgLength = (size_t)env->GetStringUTFLength(encryptedMsgJstring);
+
+        // create a dedicated temp buffer to be used in next Olm API calls
+        tempEncryptedPtr = static_cast<char*>(malloc(encryptedMsgLength*sizeof(uint8_t)));
+        memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength);
+        LOGD("##  decryptMessage(): MsgType=%ld encryptedMsgLength=%lu encryptedMsg=%s",encryptedMsgType,encryptedMsgLength,encryptedMsgPtr);
+
+        // get max plaintext length
+        size_t maxPlainTextLength = olm_decrypt_max_plaintext_length(sessionPtr,
+                                                                     static_cast<size_t>(encryptedMsgType),
+                                                                     static_cast<void*>(tempEncryptedPtr),
+                                                                     encryptedMsgLength);
+        // Note: tempEncryptedPtr is destroyed by olm_decrypt_max_plaintext_length()
+
+        if(maxPlainTextLength == olm_error())
+        {
+            const char *errorMsgPtr = olm_session_last_error(sessionPtr);
+            LOGE("##  decryptMessage(): failure - olm_decrypt_max_plaintext_length Msg=%s",errorMsgPtr);
+        }
+        else
+        {
+            LOGD("##  decryptMessage(): maxPlaintextLength=%lu",maxPlainTextLength);
+
+            // allocate output decrypted message
+            plainTextMsgPtr = static_cast<void*>(malloc(maxPlainTextLength*sizeof(uint8_t)));
+
+            // decrypt, but before reload encrypted buffer (previous one was destroyed)
+            memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength);
+            size_t plaintextLength = olm_decrypt(sessionPtr,
+                                                 encryptedMsgType,
+                                                 (void*)encryptedMsgPtr,
+                                                 encryptedMsgLength,
+                                                 plainTextMsgPtr,
+                                                 maxPlainTextLength);
+            if(plaintextLength == olm_error())
+            {
+                const char *errorMsgPtr = olm_session_last_error(sessionPtr);
+                LOGE("##  decryptMessage(): failure - olm_decrypt Msg=%s",errorMsgPtr);
+            }
+            else
+            {
+                // update decrypted buffer size
+                (static_cast<char*>(plainTextMsgPtr))[plaintextLength] = static_cast<char>('\0');
+
+                LOGD("##  decryptMessage(): decrypted returnedLg=%lu plainTextMsgPtr=%s",plaintextLength, static_cast<char*>(plainTextMsgPtr));
+                decryptedMsgRetValue = env->NewStringUTF(static_cast<const char*>(plainTextMsgPtr));
+            }
+        }
+    }
+
+    // free alloc
+    if(NULL != encryptedMsgPtr)
+    {
+        env->ReleaseStringUTFChars(encryptedMsgJstring, encryptedMsgPtr);
+    }
+
+    if(NULL != tempEncryptedPtr)
+    {
+        free(tempEncryptedPtr);
+    }
+
+    if(NULL != plainTextMsgPtr)
+    {
+        free(plainTextMsgPtr);
+    }
+
+    return decryptedMsgRetValue;
+}
+
+
+
+
+/**
+* Get the session identifier for this session.
+* @return the session identifier if operation succeed, null otherwise
+*/
+JNIEXPORT jstring OLM_SESSION_FUNC_DEF(getSessionIdentifierJni)(JNIEnv *env, jobject thiz)
+{
+    OlmSession *sessionPtr = NULL;
+    void *sessionIdPtr = NULL;
+    jstring returnValueStr=0;
+
+    // get the size to alloc to contain the id
+    size_t lengthSessionId = olm_session_id_length(sessionPtr);
+
+    if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
+    {
+        LOGE("## getSessionIdentifierJni(): failure - invalid Session ptr=NULL");
+    }
+    else if(NULL == (sessionIdPtr = (void*)malloc(lengthSessionId*sizeof(uint8_t))))
+    {
+       LOGE("## getSessionIdentifierJni(): failure - identifier allocation OOM");
+    }
+    else
+    {
+        size_t result = olm_session_id(sessionPtr, sessionIdPtr, lengthSessionId);
+        if (result == olm_error())
+        {
+            const char *errorMsgPtr = olm_session_last_error(sessionPtr);
+            LOGE("## getSessionIdentifierJni(): failure - get session identifier failure Msg=%s",errorMsgPtr);
+        }
+        else
+        {
+            // update decrypted buffer size
+            (static_cast<char*>(sessionIdPtr))[result] = static_cast<char>('\0');
+
+            LOGD("## getSessionIdentifierJni(): success - result=%lu sessionId=%s",result, (char*)sessionIdPtr);
+            returnValueStr = env->NewStringUTF((const char*)sessionIdPtr);
+        }
+        free(sessionIdPtr);
+    }
+
+    return returnValueStr;
+}
+
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
new file mode 100644
index 0000000000000000000000000000000000000000..9b3c30f4e88fa266fb0de8249c6478992be65c44
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.h
@@ -0,0 +1,37 @@
+#ifndef _OMLSESSION_H
+#define _OMLSESSION_H
+
+#include "olm_jni.h"
+
+#define OLM_SESSION_FUNC_DEF(func_name) FUNC_DEF(OlmSession,func_name)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// session creation/destruction
+JNIEXPORT void JNICALL Java_org_matrix_olm_OlmSession_releaseSessionJni(JNIEnv *env, jobject thiz);
+JNIEXPORT jlong JNICALL Java_org_matrix_olm_OlmSession_initNewSessionJni(JNIEnv *env, jobject thiz);
+
+// outbound session
+JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initOutboundSessionJni(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aTheirIdentityKey, jstring aTheirOneTimeKey);
+
+// inbound sessions: establishment based on PRE KEY message
+JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initInboundSessionJni(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aOneTimeKeyMsg);
+JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initInboundSessionFromIdKeyJni(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aTheirIdentityKey, jstring aOneTimeKeyMsg);
+
+// match inbound sessions: based on PRE KEY message
+JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_matchesInboundSessionJni(JNIEnv *env, jobject thiz, jstring aOneTimeKeyMsg);
+JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_matchesInboundSessionFromIdKeyJni(JNIEnv *env, jobject thiz, jstring aTheirIdentityKey, jstring aOneTimeKeyMsg);
+
+// encrypt/decrypt
+JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_encryptMessageJni(JNIEnv *env, jobject thiz, jstring aClearMsg, jobject aEncryptedMsg);
+JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_decryptMessage(JNIEnv *env, jobject thiz, jobject aEncryptedMsg);
+
+JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_getSessionIdentifierJni(JNIEnv *env, jobject thiz);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
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
new file mode 100644
index 0000000000000000000000000000000000000000..c27fe7c1bee7bb61854c7023bb73a1be98c48c5f
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.cpp
@@ -0,0 +1,141 @@
+/**
+ * Created by pedrocon on 06/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.
+ */
+
+#include "olm_jni.h"
+#include "olm_utility.h"
+
+/**
+* Init a buffer with a given number of random values.
+* @param aBuffer2Ptr the buffer to be initialized
+* @param aRandomSize the number of random values to apply
+* @return true if operation succeed, false otherwise
+**/
+bool setRandomInBuffer(uint8_t **aBuffer2Ptr, size_t aRandomSize)
+{
+    bool retCode = false;
+    if(NULL == aBuffer2Ptr)
+    {
+        LOGD("## setRandomInBuffer(): failure - aBuffer=NULL");
+    }
+    else if(0 == aRandomSize)
+    {
+        LOGD("## setRandomInBuffer(): failure - random size=0");
+    }
+    else if(NULL == (*aBuffer2Ptr = (uint8_t*)malloc(aRandomSize*sizeof(uint8_t))))
+    {
+        LOGD("## setRandomInBuffer(): failure - alloc mem OOM");
+    }
+    else
+    {
+        LOGD("## setRandomInBuffer(): randomSize=%ld",aRandomSize);
+
+        srand(time(NULL)); // init seed
+        for(size_t i=0;i<aRandomSize;i++)
+        {
+            (*aBuffer2Ptr)[i] = (uint8_t)(rand()%ACCOUNT_CREATION_RANDOM_MODULO);
+
+            // debug purpose
+            //LOGD("## setRandomInBuffer(): randomBuffPtr[%ld]=%d",i, (*aBuffer2Ptr)[i]);
+        }
+
+        retCode = true;
+    }
+    return retCode;
+}
+
+
+/**
+* Read the account instance ID of the calling object.
+* @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 operation succeed, -1 if instance ID was not found.
+**/
+jlong getAccountInstanceId(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, "mNativeOlmAccountId", "J")))
+      {
+        instanceId = aJniEnv->GetLongField(aJavaObject, instanceIdField);
+        aJniEnv->DeleteLocalRef(loaderClass);
+        LOGD("## getAccountInstanceId(): read from java instanceId=%lld",instanceId);
+      }
+      else
+      {
+        LOGD("## getAccountInstanceId() ERROR! GetFieldID=null");
+      }
+    }
+    else
+    {
+      LOGD("## getAccountInstanceId() ERROR! GetObjectClass=null");
+    }
+  }
+  else
+  {
+    LOGD("## getAccountInstanceId() ERROR! aJniEnv=NULL");
+  }
+  LOGD("## getAccountInstanceId() success - instanceId=%lld",instanceId);
+  return instanceId;
+}
+
+/**
+* Read the account 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 getSessionInstanceId(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, "mNativeOlmSessionId", "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");
+  }
+
+  //LOGD("## getSessionInstanceId() success - instanceId=%lld",instanceId);
+  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
new file mode 100644
index 0000000000000000000000000000000000000000..6683c6880fac17e8608a4bbd463ed4c68470e934
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_utility.h
@@ -0,0 +1,18 @@
+#ifndef _OMLUTILITY_H
+#define _OMLUTILITY_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+bool setRandomInBuffer(uint8_t **aBuffer2Ptr, size_t aRandomSize);
+jlong getSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
+jlong getAccountInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
diff --git a/java/android/OlmLibSdk/olm-sdk/src/main/res/values/strings.xml b/java/android/OlmLibSdk/olm-sdk/src/main/res/values/strings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..93bea1dd337cb2df4e298bebf403d600aa57884c
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">OlmSdk</string>
+</resources>
diff --git a/java/android/OlmLibSdk/settings.gradle b/java/android/OlmLibSdk/settings.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..d11302cf0d62f5f66b85d1a566a1c6a343a08839
--- /dev/null
+++ b/java/android/OlmLibSdk/settings.gradle
@@ -0,0 +1 @@
+include ':olm-sdk'