From 643d139e8ab3d2ecaca38bd78fcf5091a5c6c9b1 Mon Sep 17 00:00:00 2001 From: 0x330a <92654767+0x330a@users.noreply.github.com> Date: Fri, 2 Dec 2022 16:52:57 +1100 Subject: [PATCH] feat: improve cpp jni layer and add more test functionality --- libsession-util/build.gradle | 1 + .../ExampleInstrumentedTest.kt | 60 ---------- .../libsession_util/InstrumentedTests.kt | 110 ++++++++++++++++++ libsession-util/src/main/cpp/session_util.cpp | 59 +++++++++- .../loki/messenger/libsession_util/Config.kt | 22 ++++ .../messenger/libsession_util/util/Utils.kt | 20 ++-- 6 files changed, 196 insertions(+), 76 deletions(-) delete mode 100644 libsession-util/src/androidTest/java/network/loki/messenger/libsession_util/ExampleInstrumentedTest.kt create mode 100644 libsession-util/src/androidTest/java/network/loki/messenger/libsession_util/InstrumentedTests.kt diff --git a/libsession-util/build.gradle b/libsession-util/build.gradle index 560fd2aadb..1ae54bcb7d 100644 --- a/libsession-util/build.gradle +++ b/libsession-util/build.gradle @@ -40,6 +40,7 @@ android { dependencies { testImplementation 'junit:junit:4.13.2' + api(project(":libsignal")) androidTestImplementation 'androidx.test.ext:junit:1.1.4' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' } \ No newline at end of file diff --git a/libsession-util/src/androidTest/java/network/loki/messenger/libsession_util/ExampleInstrumentedTest.kt b/libsession-util/src/androidTest/java/network/loki/messenger/libsession_util/ExampleInstrumentedTest.kt deleted file mode 100644 index 05317874bb..0000000000 --- a/libsession-util/src/androidTest/java/network/loki/messenger/libsession_util/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,60 +0,0 @@ -package network.loki.messenger.libsession_util - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import org.junit.Assert.* -import org.junit.Test -import org.junit.runner.RunWith - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("network.loki.messenger.libsession_util.test", appContext.packageName) - } - - @Test - fun jni_accessible() { - val userProfile = UserProfile.newInstance() - assertNotNull(userProfile) - userProfile.free() - } - - @Test - fun jni_user_profile_c_api() { - val userProfile = UserProfile.newInstance() - - assertFalse(userProfile.needsPush()) - assertFalse(userProfile.needsDump()) - - val name = userProfile.getName() - assertNull(name) - - val (toPush, seqNo) = userProfile.push() - assertEquals("d1:#i0e1:&de1:GetLongField(obj, pointerField); } +jbyteArray bytes_from_string(JNIEnv* env, std::string_view from_str) { + size_t length = from_str.length(); + jsize jlength = (jsize)length; + jbyteArray new_array = env->NewByteArray(jlength); + env->SetByteArrayRegion(new_array, 0, jlength, reinterpret_cast(from_str.data())); + return new_array; +} + +std::string string_from_bytes(JNIEnv* env, jbyteArray byteArray) { + size_t len = env->GetArrayLength(byteArray); + jbyte* bytes = env->GetByteArrayElements(byteArray, nullptr); + std::string newSt((char*)bytes, len); + env->ReleaseByteArrayElements(byteArray, bytes, 0); + return newSt; +} + extern "C" JNIEXPORT jobject JNICALL Java_network_loki_messenger_libsession_1util_UserProfile_00024Companion_newInstance( JNIEnv* env, @@ -60,28 +76,65 @@ Java_network_loki_messenger_libsession_1util_ConfigBase_dirty(JNIEnv *env, jobje auto* configBase = ptrToConfigBase(env, thiz); return configBase->is_dirty(); } + extern "C" JNIEXPORT jboolean JNICALL Java_network_loki_messenger_libsession_1util_ConfigBase_needsPush(JNIEnv *env, jobject thiz) { auto config = ptrToConfigBase(env, thiz); return config->needs_push(); } + extern "C" JNIEXPORT jboolean JNICALL Java_network_loki_messenger_libsession_1util_ConfigBase_needsDump(JNIEnv *env, jobject thiz) { auto config = ptrToConfigBase(env, thiz); return config->needs_dump(); } + extern "C" JNIEXPORT jobject JNICALL Java_network_loki_messenger_libsession_1util_ConfigBase_push(JNIEnv *env, jobject thiz) { auto config = ptrToConfigBase(env, thiz); auto pair = config->push(); std::string to_push_str = pair.first; - jstring returnString = env->NewStringUTF(to_push_str.c_str()); + jbyteArray returnByteArray = bytes_from_string(env, to_push_str); jlong seqNo = pair.second; jclass returnObjectClass = env->FindClass("network/loki/messenger/libsession_util/util/ConfigWithSeqNo"); - jmethodID methodId = env->GetMethodID(returnObjectClass, "", "(Ljava/lang/String;J)V"); - jobject returnObject = env->NewObject(returnObjectClass, methodId, returnString, seqNo); + jmethodID methodId = env->GetMethodID(returnObjectClass, "", "([BJ)V"); + jobject returnObject = env->NewObject(returnObjectClass, methodId, returnByteArray, seqNo); return returnObject; +} + +extern "C" +JNIEXPORT jobject JNICALL +Java_network_loki_messenger_libsession_1util_UserProfile_getPic(JNIEnv *env, jobject thiz) { + auto profile = ptrToProfile(env, thiz); + auto pair = profile->get_profile_pic(); + // return nullptr if either parameter is null as per profile class + if (pair.first == nullptr || pair.second == nullptr) return nullptr; + const std::string* pic_url = pair.first; + const std::string* pic_key = pair.second; + jclass returnObjectClass = env->FindClass("network/loki/messenger/libsession_util/UserPic"); + jmethodID constructor = env->GetMethodID(returnObjectClass, "", "(Ljava/lang/String;[B)V"); + jstring url = env->NewStringUTF(pic_url->c_str()); + jbyteArray byteArray = bytes_from_string(env, *pic_key); + jobject returnObject = env->NewObject(returnObjectClass, constructor, url, byteArray); + return returnObject; +} +extern "C" +JNIEXPORT void JNICALL +Java_network_loki_messenger_libsession_1util_UserProfile_setPic(JNIEnv *env, jobject thiz, + jobject user_pic) { + auto profile = ptrToProfile(env, thiz); + jclass userPicClass = env->FindClass("network/loki/messenger/libsession_util/UserPic"); + jfieldID picField = env->GetFieldID(userPicClass, "url", "Ljava/lang/String;"); + jfieldID keyField = env->GetFieldID(userPicClass, "key", "[B"); + auto pic = (jstring)env->GetObjectField(user_pic, picField); + auto key = (jbyteArray)env->GetObjectField(user_pic, keyField); + + const char* pic_chars = env->GetStringUTFChars(pic, nullptr); + const std::string key_str = string_from_bytes(env, key); + auto* pic_string = new std::string(pic_chars); + + profile->set_profile_pic(*pic_string, key_str); } \ No newline at end of file diff --git a/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt b/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt index 046d1d70b1..eb53ddbbb5 100644 --- a/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt +++ b/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt @@ -25,4 +25,26 @@ class UserProfile(pointer: Long): ConfigBase(pointer) { external fun setName(newName: String) external fun getName(): String? external fun free() + external fun getPic(): UserPic? + external fun setPic(userPic: UserPic) +} + +data class UserPic(val url: String, val key: ByteArray) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as UserPic + + if (url != other.url) return false + if (!key.contentEquals(other.key)) return false + + return true + } + + override fun hashCode(): Int { + var result = url.hashCode() + result = 31 * result + key.contentHashCode() + return result + } } \ No newline at end of file diff --git a/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/Utils.kt b/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/Utils.kt index 51b0990299..8a6b96e915 100644 --- a/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/Utils.kt +++ b/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/Utils.kt @@ -1,28 +1,22 @@ package network.loki.messenger.libsession_util.util -data class StringWithLen(private val bytes: ByteArray, private val len: Long) { // We might not need this class, could be helpful though - - override fun toString(): String = bytes.decodeToString() - +data class ConfigWithSeqNo(val config: ByteArray, val seqNo: Long) { override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false - other as StringWithLen + other as ConfigWithSeqNo - if (!bytes.contentEquals(other.bytes)) return false - if (len != other.len) return false + if (!config.contentEquals(other.config)) return false + if (seqNo != other.seqNo) return false return true } override fun hashCode(): Int { - var result = bytes.contentHashCode() - result = 31 * result + len.hashCode() + var result = config.contentHashCode() + result = 31 * result + seqNo.hashCode() return result } -} - - -data class ConfigWithSeqNo(val config: String, val seqNo: Long) \ No newline at end of file +} \ No newline at end of file