From 5573d3ab23de21b93b1ecc50d4fce96b02a42886 Mon Sep 17 00:00:00 2001
From: pedroGitt <pedro.contreiras@amdocs.com>
Date: Wed, 5 Oct 2016 18:25:09 +0200
Subject: First commit adding Olm Lib for Android - Add Android Studio project

---
 java/android/OlmLibSdk/build.gradle                |  23 +
 java/android/OlmLibSdk/gradle.properties           |  19 +
 java/android/OlmLibSdk/gradlew                     | 160 +++++++
 java/android/OlmLibSdk/gradlew.bat                 |  90 ++++
 java/android/OlmLibSdk/olm-sdk/build.gradle        |  72 +++
 .../java/org/matrix/olm/OlmAccountTest.java        | 223 ++++++++++
 .../OlmLibSdk/olm-sdk/src/main/AndroidManifest.xml |  11 +
 .../src/main/java/org/matrix/olm/OlmAccount.java   |   5 +-
 .../src/main/java/org/matrix/olm/OlmManager.java   |  32 ++
 .../src/main/java/org/matrix/olm/OlmSession.java   | 327 ++++++++++++++
 .../OlmLibSdk/olm-sdk/src/main/jni/Android.mk      |  52 +++
 .../OlmLibSdk/olm-sdk/src/main/jni/Application.mk  |   3 +
 .../OlmLibSdk/olm-sdk/src/main/jni/olm_account.cpp | 491 +++++++++++++++++++++
 .../OlmLibSdk/olm-sdk/src/main/jni/olm_account.h   |  34 ++
 .../OlmLibSdk/olm-sdk/src/main/jni/olm_jni.h       |  46 ++
 .../OlmLibSdk/olm-sdk/src/main/jni/olm_session.cpp | 476 ++++++++++++++++++++
 .../OlmLibSdk/olm-sdk/src/main/jni/olm_session.h   |  38 ++
 java/android/OlmLibSdk/settings.gradle             |   1 +
 18 files changed, 2102 insertions(+), 1 deletion(-)
 create mode 100644 java/android/OlmLibSdk/build.gradle
 create mode 100644 java/android/OlmLibSdk/gradle.properties
 create mode 100644 java/android/OlmLibSdk/gradlew
 create mode 100644 java/android/OlmLibSdk/gradlew.bat
 create mode 100644 java/android/OlmLibSdk/olm-sdk/build.gradle
 create mode 100644 java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java
 create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/AndroidManifest.xml
 create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmManager.java
 create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmSession.java
 create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/Android.mk
 create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/Application.mk
 create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.cpp
 create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.h
 create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_jni.h
 create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.cpp
 create mode 100644 java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.h
 create mode 100644 java/android/OlmLibSdk/settings.gradle

(limited to 'java/android')

diff --git a/java/android/OlmLibSdk/build.gradle b/java/android/OlmLibSdk/build.gradle
new file mode 100644
index 0000000..77ce66e
--- /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 0000000..13d64a5
--- /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 0000000..9d82f78
--- /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 0000000..8a0b282
--- /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 0000000..96eaaf7
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/build.gradle
@@ -0,0 +1,72 @@
+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 ndkBuildNative(type: Exec, description: 'NDK building..') {
+        workingDir file('src/main')
+        commandLine getNdkBuildCmd() //, '-B', '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
+    }
+
+    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 0000000..3d7568f
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/androidTest/java/org/matrix/olm/OlmAccountTest.java
@@ -0,0 +1,223 @@
+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 static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+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;
+
+    public static final String TEST_STRING = "This is a string";
+    public static final long TEST_LONG = 12345678L;
+
+    @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 test1CreateAccount() {
+        Log.d(LOG_TAG,"## testInitNewAccount");
+        mOlmAccount = new OlmAccount();
+        assertNotNull(mOlmAccount);
+    }
+
+    @Test
+    public void test2InitNewAccount() {
+        Log.d(LOG_TAG,"## testInitNewAccount");
+        assertTrue(mOlmAccount.initNewAccount());
+        mIsAccountCreated = true;
+    }
+
+    @Test
+    public void test3GetOlmAccountId() {
+        Log.d(LOG_TAG,"## testGetOlmAccountId");
+
+        long olmNativeInstance = mOlmAccount.getOlmAccountId();
+        assertTrue(0!=olmNativeInstance);
+    }
+
+    @Test
+    public void test4IdentityKeys() {
+        Log.d(LOG_TAG,"## testIdentityKeys");
+
+        JSONObject identityKeysJson = mOlmAccount.identityKeys();
+        assertNotNull(identityKeysJson);
+        Log.d(LOG_TAG,"## testIdentityKeys Keys="+identityKeysJson);
+
+        try {
+            String fingerPrintKey = identityKeysJson.getString(OlmAccount.JSON_KEY_FINGER_PRINT_KEY);
+            assertFalse("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);
+            assertFalse("identity key missing",TextUtils.isEmpty(identityKey));
+        } catch (JSONException e) {
+            e.printStackTrace();
+            assertTrue("Exception MSg="+e.getMessage(), false);
+        }
+
+
+    }
+
+
+    //****************************************************
+    //** ************** One time keys TESTS **************
+    //****************************************************
+    @Test
+    public void test5MaxOneTimeKeys() {
+        Log.d(LOG_TAG,"## testMaxOneTimeKeys");
+
+        long maxOneTimeKeys = mOlmAccount.maxOneTimeKeys();
+        Log.d(LOG_TAG,"## testMaxOneTimeKeys(): maxOneTimeKeys="+maxOneTimeKeys);
+
+        assertTrue(maxOneTimeKeys>0);
+    }
+
+    @Test
+    public void test6GenerateOneTimeKeys() {
+        Log.d(LOG_TAG,"## testGenerateOneTimeKeys");
+        int retValue = mOlmAccount.generateOneTimeKeys(GENERATION_ONE_TIME_KEYS_NUMBER);
+        assertTrue(0==retValue);
+    }
+
+    @Test
+    public void test7OneTimeKeysJsonFormat() {
+        Log.d(LOG_TAG,"## testIdentityKeys");
+        JSONObject generatedKeysJsonObj;
+        JSONObject oneTimeKeysJson = mOlmAccount.oneTimeKeys();
+        assertNotNull(oneTimeKeysJson);
+
+        try {
+            generatedKeysJsonObj = oneTimeKeysJson.getJSONObject(OlmAccount.JSON_KEY_ONE_TIME_KEY);
+            assertFalse(OlmAccount.JSON_KEY_ONE_TIME_KEY +" object is missing", null==generatedKeysJsonObj);
+
+            /*String oneTimeKeyA = generatedKeysJsonObj.getString(OlmAccount.JSON_KEY_ONE_TIME_KEY_GENERATED_A);
+            assertFalse(" one time KeyA object is missing", TextUtils.isEmpty(oneTimeKeyA));
+
+            String oneTimeKeyB = generatedKeysJsonObj.getString(OlmAccount.JSON_KEY_ONE_TIME_KEY_GENERATED_B);
+            assertFalse(" one time KeyA object is missing", TextUtils.isEmpty(oneTimeKeyA));*/
+        } catch (JSONException e) {
+            assertTrue("Exception MSg="+e.getMessage(), false);
+        }
+    }
+
+    // TODO testRemoveOneTimeKeysForSession when session is available
+    /*@Test
+    public void testRemoveOneTimeKeysForSession() {
+        Log.d(LOG_TAG,"## testRemoveOneTimeKeysForSession");
+        OLMSession olmSession = new OLMSession();
+
+        JSONArray keysJsonArray = mOlmAccount.removeOneTimeKeysForSession(olmSession);
+
+        assertNotNull(keysJsonArray);
+        // TODO add extra test to test the JSON content format..
+    }*/
+
+    @Test
+    public void test8MarkOneTimeKeysAsPublished() {
+        Log.d(LOG_TAG,"## testMarkOneTimeKeysAsPublished");
+
+        int retCode = mOlmAccount.markOneTimeKeysAsPublished();
+        // if OK => retCode=0
+        assertTrue(0 == retCode);
+    }
+
+    @Test
+    public void test9SignMessage() {
+        Log.d(LOG_TAG,"## testMarkOneTimeKeysAsPublished");
+
+        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 onteTimeKeysKeysJson = account.oneTimeKeys();
+        Log.d(LOG_TAG, "## testJni(): onteTimeKeysKeysJson="+onteTimeKeysKeysJson.toString());
+
+        // TODO removeOneTimeKeysForSession(session);
+
+        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/main/AndroidManifest.xml b/java/android/OlmLibSdk/olm-sdk/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..8a8747f
--- /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
index 299a07d..ac8e12d 100644
--- a/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmAccount.java
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmAccount.java
@@ -26,7 +26,10 @@ public class OlmAccount {
 
     // JSON keys used in the JSON objects returned by JNI
     public static String JSON_KEY_ONE_TIME_KEY = "curve25519";
+    public static String JSON_KEY_IDENTITY_KEY = "curve25519";
     public static String JSON_KEY_FINGER_PRINT_KEY = "ed25519";
+    public static String JSON_KEY_ONE_TIME_KEY_GENERATED_A = "AAAAAA";
+    public static String JSON_KEY_ONE_TIME_KEY_GENERATED_B = "AAAAAB";
 
     /** instance unique identifier, used in JNI to match the corresponding native class **/
     private int mJavaInstanceId;
@@ -99,7 +102,7 @@ public class OlmAccount {
     private native byte[] identityKeysJni();
 
     /**
-     * Return the identity keys in a JSON array.<br>
+     * Return the identity keys (identity & fingerprint keys) in a JSON array.<br>
      * Public API for {@link #identityKeysJni()}.
      * @return identity keys in JSON array format if operation succeed, null otherwise
      */
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 0000000..55b9910
--- /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/OlmSession.java b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmSession.java
new file mode 100644
index 0000000..28324da
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/java/org/matrix/olm/OlmSession.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2016 OpenMarket Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.olm;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+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;
+    }
+
+    /**
+     * 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;
+
+            int retCode = 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 TODO TBC
+     * @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 TODO TBC!.<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 TODO TBC
+     * @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);
+
+
+    /**
+     * 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);
+
+
+    /**
+     * 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();
+
+/*
+- (BOOL) matchesInboundSession:(NSString*)oneTimeKeyMessage;
+- (BOOL) matchesInboundSessionFrom:(NSString*)theirIdentityKey oneTimeKeyMessage:(NSString *)oneTimeKeyMessage;
+
+// UTF-8 plaintext -> base64 ciphertext
+- (OLMMessage*) encryptMessage:(NSString*)message;
+
+// base64 ciphertext -> UTF-8 plaintext
+- (NSString*) decryptMessage:(OLMMessage*)message;
+*/
+
+
+    /**
+     * 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 in a JSON array.<br>
+     * Public API for {@link #identityKeysJni()}.
+     * @return identity keys in JSON array format 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.
+     * @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.
+     * These keys must be published on the server.
+     * @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>
+     * Public API for {@link #oneTimeKeysJni()}.
+     * @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);
+
+    @Override
+    public String toString() {
+        return super.toString();
+    }
+
+}
+
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 0000000..d59f916
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/Android.mk
@@ -0,0 +1,52 @@
+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
+
+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 0000000..29a4296
--- /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 0000000..d8ee409
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.cpp
@@ -0,0 +1,491 @@
+/*
+ * 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"
+
+
+/**
+* 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 JNICALL Java_org_matrix_olm_OlmAccount_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
+  { // even if free(NULL) does not crash, a test is performed for debug purpose
+    LOGD("## releaseAccountJni(): IN");
+    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 JNICALL Java_org_matrix_olm_OlmAccount_initNewAccountJni(JNIEnv *env, jobject thiz)
+{
+    OlmAccount* accountPtr = NULL;
+    size_t accountRetCode;
+    uint8_t* randomBuffPtr = NULL;
+    int 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(NULL == (randomBuffPtr = (std::uint8_t*)malloc(randomSize*sizeof(std::uint8_t))))
+        {
+            LOGE("## initNewAccount(): failure - random buffer OOM");
+        }
+        else
+        {    // create random buffer
+            LOGD("## initNewAccount(): randomSize=%d",randomSize);
+
+            srand(time(NULL)); // init seed
+            for(int i=0;i<randomSize;i++)
+            {
+                randomBuffPtr[i] = (std::uint8_t)(rand()%ACCOUNT_CREATION_RANDOM_MODULO);;
+            }
+
+            // create account
+            accountRetCode = olm_create_account(accountPtr, randomBuffPtr, randomSize);
+            if(accountRetCode == olm_error()) {
+                const char *errorMsgPtr = olm_account_last_error(accountPtr);
+                LOGE("## initNewAccount(): failure - account creation failed Msg=%s", errorMsgPtr);
+             }
+
+            free(randomBuffPtr);
+            LOGD("## initNewAccount(): success - OLM account created");
+            LOGD("## initNewAccount(): success - accountPtr=%p (jlong)(intptr_t)accountPtr=%lld",accountPtr,(jlong)(intptr_t)accountPtr);
+        }
+    }
+
+    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 JNICALL Java_org_matrix_olm_OlmAccount_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(std::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 JNICALL Java_org_matrix_olm_OlmAccount_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 JNICALL Java_org_matrix_olm_OlmAccount_generateOneTimeKeys(JNIEnv *env, jobject thiz, jint aNumberOfKeys)
+{
+    OlmAccount* accountPtr = NULL;;
+    jint retCode = ERROR_CODE_KO;
+    size_t length;
+    void* keysBytesPtr; // TODO check type: or uint8_t?
+    size_t result;
+
+    LOGD("## generateOneTimeKeys(): accountPtr =%p aNumberOfKeys=%d",accountPtr, aNumberOfKeys);
+
+    if(NULL == (accountPtr = (OlmAccount*)getAccountInstanceId(env,thiz)))
+    {
+        LOGE("## generateOneTimeKeys(): failure - invalid Account ptr");
+    }
+    else
+    {   // keys memory allocation
+        length = olm_account_generate_one_time_keys_random_length(accountPtr, aNumberOfKeys);
+        LOGD("## generateOneTimeKeys(): randomLength=%ld", length);
+        if(NULL == (keysBytesPtr=(void*)malloc(length*sizeof(void*))))
+        {
+            LOGE("## generateOneTimeKeys(): failure - random allocation OOM");
+        }
+        else
+        {   // retrieve key pairs in keysBytesPtr
+            result = olm_account_generate_one_time_keys(accountPtr, aNumberOfKeys, keysBytesPtr, length);
+            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);
+            }
+
+            free(keysBytesPtr);
+        }
+    }
+
+    return retCode;
+}
+
+/**
+ * Get "one time keys".
+ * 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 JNICALL Java_org_matrix_olm_OlmAccount_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 JNICALL Java_org_matrix_olm_OlmAccount_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 JNICALL Java_org_matrix_olm_OlmAccount_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.
+ * @param aMessage message to sign
+ * @return the corresponding signed message, null otherwise
+**/
+JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmAccount_signMessage(JNIEnv *env, jobject thiz, jstring aMessage)
+{
+    OlmAccount* accountPtr = NULL;
+    size_t signatureLength;
+    void* signaturePtr;
+    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 == (signaturePtr=(void *)malloc(signatureLength*sizeof(void*))))
+            {
+                LOGE("## signMessage(): failure - signature allocation OOM");
+            }
+            else
+            {   // sign message
+                resultSign = olm_account_sign(accountPtr, (void*)messageToSign, messageLength, signaturePtr, 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*)signaturePtr); // UTF8
+                    LOGD("## signMessage(): success - retCode=%ld",resultSign);
+                }
+
+                free(signaturePtr);
+            }
+
+            // release messageToSign
+            env->ReleaseStringUTFChars(aMessage, messageToSign);
+        }
+    }
+
+    return signedMsgRetValue;
+}
+
+
+JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmManager_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;
+}
+
+
+/**
+* Read the account instance ID of the calling object.
+* @return the instance ID if read succeed, -1 otherwise.
+**/
+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;
+}
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 0000000..8ba1633
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_account.h
@@ -0,0 +1,34 @@
+#ifndef _OMLACCOUNT_H
+#define _OMLACCOUNT_H
+
+#include "olm_jni.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+jlong getAccountInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
+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 0000000..a504333
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_jni.h
@@ -0,0 +1,46 @@
+#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
+
+// 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 = 500;
+
+
+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 0000000..22544d4
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.cpp
@@ -0,0 +1,476 @@
+/*
+ * 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"
+
+
+/**
+* Init memory allocation for session creation.
+* @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 JNICALL Java_org_matrix_olm_OlmSession_releaseSessionJni(JNIEnv *env, jobject thiz)
+{
+  OlmSession* sessionPtr = NULL;
+
+  if(NULL == (sessionPtr = (OlmSession*)getSessionInstanceId(env,thiz)))
+  {
+      LOGE("## releaseSessionJni(): failure - invalid Session ptr=NULL");
+  }
+  else
+  { // even if free(NULL) does not crash, a test is performed for debug purpose
+    LOGD("## releaseSessionJni(): IN");
+    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 JNICALL Java_org_matrix_olm_OlmSession_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 JNICALL Java_org_matrix_olm_OlmSession_initOutboundSessionJni(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aTheirIdentityKey, jstring aTheirOneTimeKey)
+{
+    jint retCode = ERROR_CODE_KO;
+    OlmSession* sessionPtr = NULL;
+    OlmAccount* accountPtr = NULL;
+    void *randomBuffPtr;
+    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(NULL == (randomBuffPtr = (void*)malloc(randomSize*sizeof(void*))))
+        {
+            LOGE("## initOutboundSessionJni(): failure - random buffer OOM");
+        }
+        else
+        {   // convert identity & one time keys to C strings
+            const char* theirIdentityKeyPtr = NULL;
+            const char* theirOneTimeKeyPtr = NULL;
+
+            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
+            {
+                int theirIdentityKeyLength = env->GetStringUTFLength(aTheirIdentityKey);
+                int theirOneTimeKeyLength  = env->GetStringUTFLength(aTheirOneTimeKey);
+                LOGD("## initOutboundSessionJni(): identityKey=%s oneTimeKey=%s",theirIdentityKeyPtr,theirOneTimeKeyPtr);
+
+                sessionResult = olm_create_outbound_session(sessionPtr, accountPtr, theirIdentityKeyPtr, theirIdentityKeyLength, theirOneTimeKeyPtr, theirOneTimeKeyLength, 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 local alloc
+            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 TODO TBC
+ * @return ERROR_CODE_OK if operation succeed, ERROR_CODE_KO otherwise
+ */
+JNIEXPORT jint JNICALL Java_org_matrix_olm_OlmSession_initInboundSessionJni(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jstring aOneTimeKeyMsg)
+{
+    jint retCode = ERROR_CODE_KO;
+    OlmSession *sessionPtr = NULL;
+    OlmAccount *accountPtr = 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("## initOutboundSessionJni(): failure - invalid message");
+    }
+    else
+    {   // convert message to C strings
+        const char *messagePtr = NULL;
+        if(NULL == (messagePtr = env->GetStringUTFChars(aOneTimeKeyMsg, 0)))
+        {
+            LOGE("## initInboundSessionJni(): failure - message JNI allocation OOM");
+        }
+        else
+        {
+            int messageLength = env->GetStringUTFLength(aOneTimeKeyMsg);
+            LOGD("## initInboundSessionJni(): message=%s messageLength=%d",messagePtr,messageLength);
+
+            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 JNICALL Java_org_matrix_olm_OlmSession_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("## initOutboundSessionJni(): 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 = env->GetStringUTFLength(aOneTimeKeyMsg);
+        size_t theirIdentityKeyLength = 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 JNICALL Java_org_matrix_olm_OlmSession_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 = 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);
+        }
+    }
+
+    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 Java_org_matrix_olm_OlmSession_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 = env->GetStringUTFLength(aTheirIdentityKey);
+        size_t messageLength = 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);
+        }
+    }
+
+    return retCode;
+}
+
+
+/**
+* Get the session identifier for this session.
+* @return the session identifier if operation succeed, null otherwise
+*/
+JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_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 lengthSessId = 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(lengthSessId*sizeof(void*))))
+    {
+       LOGE("## getSessionIdentifierJni(): failure - identifier allocation OOM");
+    }
+    else
+    {
+        size_t result = olm_session_id(sessionPtr, sessionIdPtr, lengthSessId);
+        if (result == olm_error())
+        {
+            const char *errorMsgPtr = olm_session_last_error(sessionPtr);
+            LOGE("## getSessionIdentifierJni(): failure - get session identifier failure Msg=%s",errorMsgPtr);
+        }
+        else
+        {
+            returnValueStr = env->NewStringUTF((const char*)sessionIdPtr);
+        }
+        free(sessionIdPtr);
+    }
+
+    return returnValueStr;
+}
+
+/**
+* Read the account instance ID of the calling object (aJavaObject) passed in parameter.
+* @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->GetIntField(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_session.h b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.h
new file mode 100644
index 0000000..edd1012
--- /dev/null
+++ b/java/android/OlmLibSdk/olm-sdk/src/main/jni/olm_session.h
@@ -0,0 +1,38 @@
+#ifndef _OMLSESSION_H
+#define _OMLSESSION_H
+
+#include "olm_jni.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+jlong getSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
+
+// 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);
+
+JNIEXPORT jstring JNICALL Java_org_matrix_olm_OlmSession_getSessionIdentifierJni(JNIEnv *env, jobject thiz);
+
+
+// signing
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/java/android/OlmLibSdk/settings.gradle b/java/android/OlmLibSdk/settings.gradle
new file mode 100644
index 0000000..d11302c
--- /dev/null
+++ b/java/android/OlmLibSdk/settings.gradle
@@ -0,0 +1 @@
+include ':olm-sdk'
-- 
cgit v1.2.3-70-g09d2