diff --git a/.drone.yml b/.drone.yml index cf2be17..0ace6eb 100644 --- a/.drone.yml +++ b/.drone.yml @@ -16,7 +16,7 @@ steps: environment: ANDROID_HOME: /usr/lib/android-sdk commands: - - apt-get -y install ninja-build + - apt-get -y update && apt-get -y install ninja-build - update-java-alternatives -s java-1.17.0-openjdk-amd64 - env VERSION=`git describe --tags --dirty` ./gradlew publish @@ -26,5 +26,5 @@ steps: SSH_KEY: from_secret: SSH_KEY commands: - - apt-get -y install python3 + - apt-get -y update && apt-get -y install python3 - ./scripts/upload-maven-artifacts.py \ No newline at end of file diff --git a/library/src/main/cpp/config_base.cpp b/library/src/main/cpp/config_base.cpp index fa626f2..619de96 100644 --- a/library/src/main/cpp/config_base.cpp +++ b/library/src/main/cpp/config_base.cpp @@ -29,24 +29,17 @@ Java_network_loki_messenger_libsession_1util_ConfigBase_push(JNIEnv *env, jobjec return jni_utils::run_catching_cxx_exception_or_throws(env, [=] { std::lock_guard lock{util::util_mutex_}; auto config = ptrToConfigBase(env, thiz); - auto push_tuple = config->push(); - auto to_push_str = std::get<1>(push_tuple); - auto to_delete = std::get<2>(push_tuple); + auto [seq_no, to_push, to_delete] = config->push(); + + jobject messages = jni_utils::jlist_from_collection(env, to_push, [](JNIEnv *env, const std::vector &data) { + return jni_utils::session_bytes_from_range(env, data); + }); + + jobject obsoleteHashes = jni_utils::jstring_list_from_collection(env, to_delete); - jbyteArray returnByteArray = util::bytes_from_vector(env, to_push_str); - jlong seqNo = std::get<0>(push_tuple); jclass returnObjectClass = env->FindClass("network/loki/messenger/libsession_util/util/ConfigPush"); - jclass stackClass = env->FindClass("java/util/Stack"); - jmethodID methodId = env->GetMethodID(returnObjectClass, "", "([BJLjava/util/List;)V"); - jmethodID stack_init = env->GetMethodID(stackClass, "", "()V"); - jobject our_stack = env->NewObject(stackClass, stack_init); - jmethodID push_stack = env->GetMethodID(stackClass, "push", "(Ljava/lang/Object;)Ljava/lang/Object;"); - for (auto entry : to_delete) { - auto entry_jstring = env->NewStringUTF(entry.data()); - env->CallObjectMethod(our_stack, push_stack, entry_jstring); - } - jobject returnObject = env->NewObject(returnObjectClass, methodId, returnByteArray, seqNo, our_stack); - return returnObject; + jmethodID methodId = env->GetMethodID(returnObjectClass, "", "(Ljava/util/List;JLjava/util/List;)V"); + return env->NewObject(returnObjectClass, methodId, messages, static_cast(seq_no), obsoleteHashes); }); } @@ -75,12 +68,20 @@ Java_network_loki_messenger_libsession_1util_ConfigBase_encryptionDomain(JNIEnv JNIEXPORT void JNICALL Java_network_loki_messenger_libsession_1util_ConfigBase_confirmPushed(JNIEnv *env, jobject thiz, jlong seq_no, - jstring new_hash_jstring) { + jobjectArray hash_list) { std::lock_guard lock{util::util_mutex_}; auto conf = ptrToConfigBase(env, thiz); - auto new_hash = env->GetStringUTFChars(new_hash_jstring, nullptr); - conf->confirm_pushed(seq_no, new_hash); - env->ReleaseStringUTFChars(new_hash_jstring, new_hash); + auto hash_list_size = env->GetArrayLength(hash_list); + std::unordered_set hashes(hash_list_size); + + for (int i = 0; i < hash_list_size; i++) { + auto hash_jstring = jni_utils::JavaLocalRef(env, (jstring) env->GetObjectArrayElement(hash_list, i)); + auto hash = env->GetStringUTFChars(hash_jstring.get(), nullptr); + hashes.insert(hash); + env->ReleaseStringUTFChars(hash_jstring.get(), hash); + } + + conf->confirm_pushed(seq_no, hashes); } #pragma clang diagnostic push @@ -98,9 +99,7 @@ Java_network_loki_messenger_libsession_1util_ConfigBase_merge___3Lkotlin_Pair_2( auto pair = extractHashAndData(env, jElement); configs.push_back(pair); } - auto returned = conf->merge(configs); - auto string_stack = util::build_string_stack(env, returned); - return string_stack; + return jni_utils::jstring_list_from_collection(env, conf->merge(configs)); }); } @@ -137,16 +136,8 @@ Java_network_loki_messenger_libsession_1util_ConfigBase_00024Companion_kindFor(J extern "C" JNIEXPORT jobject JNICALL -Java_network_loki_messenger_libsession_1util_ConfigBase_currentHashes(JNIEnv *env, jobject thiz) { +Java_network_loki_messenger_libsession_1util_ConfigBase_activeHashes(JNIEnv *env, jobject thiz) { std::lock_guard lock{util::util_mutex_}; auto conf = ptrToConfigBase(env, thiz); - jclass stack = env->FindClass("java/util/Stack"); - jmethodID init = env->GetMethodID(stack, "", "()V"); - jobject our_stack = env->NewObject(stack, init); - jmethodID push = env->GetMethodID(stack, "push", "(Ljava/lang/Object;)Ljava/lang/Object;"); - auto vec = conf->current_hashes(); - for (std::string element: vec) { - env->CallObjectMethod(our_stack, push, env->NewStringUTF(element.data())); - } - return our_stack; + return jni_utils::jstring_list_from_collection(env, conf->active_hashes()); } \ No newline at end of file diff --git a/library/src/main/cpp/config_base.h b/library/src/main/cpp/config_base.h index e80f13d..0ff16c9 100644 --- a/library/src/main/cpp/config_base.h +++ b/library/src/main/cpp/config_base.h @@ -3,31 +3,32 @@ #include "session/config/base.hpp" #include "util.h" +#include "jni_utils.h" #include #include inline session::config::ConfigBase* ptrToConfigBase(JNIEnv *env, jobject obj) { - jclass baseClass = env->FindClass("network/loki/messenger/libsession_util/ConfigBase"); - jfieldID pointerField = env->GetFieldID(baseClass, "pointer", "J"); + auto baseClass = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/ConfigBase")); + jfieldID pointerField = env->GetFieldID(baseClass.get(), "pointer", "J"); return (session::config::ConfigBase*) env->GetLongField(obj, pointerField); } inline std::pair> extractHashAndData(JNIEnv *env, jobject kotlin_pair) { - jclass pair = env->FindClass("kotlin/Pair"); - jfieldID first = env->GetFieldID(pair, "first", "Ljava/lang/Object;"); - jfieldID second = env->GetFieldID(pair, "second", "Ljava/lang/Object;"); - jstring hash_as_jstring = static_cast(env->GetObjectField(kotlin_pair, first)); - jbyteArray data_as_jbytes = static_cast(env->GetObjectField(kotlin_pair, second)); - auto hash_as_string = env->GetStringUTFChars(hash_as_jstring, nullptr); - auto data_as_vector = util::vector_from_bytes(env, data_as_jbytes); + auto pair = jni_utils::JavaLocalRef(env, env->FindClass("kotlin/Pair")); + jfieldID first = env->GetFieldID(pair.get(), "first", "Ljava/lang/Object;"); + jfieldID second = env->GetFieldID(pair.get(), "second", "Ljava/lang/Object;"); + auto hash_as_jstring = jni_utils::JavaLocalRef(env, static_cast(env->GetObjectField(kotlin_pair, first))); + auto data_as_jbytes = jni_utils::JavaLocalRef(env, static_cast(env->GetObjectField(kotlin_pair, second))); + auto hash_as_string = env->GetStringUTFChars(hash_as_jstring.get(), nullptr); + auto data_as_vector = util::vector_from_bytes(env, data_as_jbytes.get()); auto ret_pair = std::pair>{hash_as_string, data_as_vector}; - env->ReleaseStringUTFChars(hash_as_jstring, hash_as_string); + env->ReleaseStringUTFChars(hash_as_jstring.get(), hash_as_string); return ret_pair; } inline session::config::ConfigSig* ptrToConfigSig(JNIEnv* env, jobject obj) { - jclass sigClass = env->FindClass("network/loki/messenger/libsession_util/ConfigSig"); - jfieldID pointerField = env->GetFieldID(sigClass, "pointer", "J"); + auto sigClass = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/ConfigSig")); + jfieldID pointerField = env->GetFieldID(sigClass.get(), "pointer", "J"); return (session::config::ConfigSig*) env->GetLongField(obj, pointerField); } diff --git a/library/src/main/cpp/contacts.cpp b/library/src/main/cpp/contacts.cpp index ac756b7..0a5d481 100644 --- a/library/src/main/cpp/contacts.cpp +++ b/library/src/main/cpp/contacts.cpp @@ -69,14 +69,6 @@ Java_network_loki_messenger_libsession_1util_Contacts_all(JNIEnv *env, jobject t return jni_utils::run_catching_cxx_exception_or_throws(env, [=] { std::lock_guard lock{util::util_mutex_}; auto contacts = ptrToContacts(env, thiz); - jclass stack = env->FindClass("java/util/Stack"); - jmethodID init = env->GetMethodID(stack, "", "()V"); - jobject our_stack = env->NewObject(stack, init); - jmethodID push = env->GetMethodID(stack, "push", "(Ljava/lang/Object;)Ljava/lang/Object;"); - for (const auto &contact: *contacts) { - auto contact_obj = serialize_contact(env, contact); - env->CallObjectMethod(our_stack, push, contact_obj); - } - return our_stack; + return jni_utils::jlist_from_collection(env, *ptrToContacts(env, thiz), serialize_contact); }); } diff --git a/library/src/main/cpp/contacts.h b/library/src/main/cpp/contacts.h index 4108352..0998227 100644 --- a/library/src/main/cpp/contacts.h +++ b/library/src/main/cpp/contacts.h @@ -5,27 +5,28 @@ #include #include "session/config/contacts.hpp" #include "util.h" +#include "jni_utils.h" inline session::config::Contacts *ptrToContacts(JNIEnv *env, jobject obj) { - jclass contactsClass = env->FindClass("network/loki/messenger/libsession_util/Contacts"); - jfieldID pointerField = env->GetFieldID(contactsClass, "pointer", "J"); + auto contactsClass = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/Contacts")); + jfieldID pointerField = env->GetFieldID(contactsClass.get(), "pointer", "J"); return (session::config::Contacts *) env->GetLongField(obj, pointerField); } inline jobject serialize_contact(JNIEnv *env, session::config::contact_info info) { - jclass contactClass = env->FindClass("network/loki/messenger/libsession_util/util/Contact"); - jmethodID constructor = env->GetMethodID(contactClass, "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZLnetwork/loki/messenger/libsession_util/util/UserPic;JLnetwork/loki/messenger/libsession_util/util/ExpiryMode;)V"); - jstring id = env->NewStringUTF(info.session_id.data()); - jstring name = env->NewStringUTF(info.name.data()); - jstring nickname = env->NewStringUTF(info.nickname.data()); + auto contactClass = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/Contact")); + jmethodID constructor = env->GetMethodID(contactClass.get(), "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZLnetwork/loki/messenger/libsession_util/util/UserPic;JLnetwork/loki/messenger/libsession_util/util/ExpiryMode;)V"); + auto id = jni_utils::JavaLocalRef(env, env->NewStringUTF(info.session_id.data())); + auto name = jni_utils::JavaLocalRef(env, env->NewStringUTF(info.name.data())); + auto nickname = jni_utils::JavaLocalRef(env, env->NewStringUTF(info.nickname.data())); jboolean approved, approvedMe, blocked; approved = info.approved; approvedMe = info.approved_me; blocked = info.blocked; auto created = info.created; - jobject profilePic = util::serialize_user_pic(env, info.profile_picture); - jobject returnObj = env->NewObject(contactClass, constructor, id, name, nickname, approved, - approvedMe, blocked, profilePic, (jlong)info.priority, + auto profilePic = jni_utils::JavaLocalRef(env, util::serialize_user_pic(env, info.profile_picture)); + jobject returnObj = env->NewObject(contactClass.get(), constructor, id.get(), name.get(), nickname.get(), approved, + approvedMe, blocked, profilePic.get(), (jlong)info.priority, util::serialize_expiry(env, info.exp_mode, info.exp_timer)); return returnObj; } @@ -44,25 +45,24 @@ inline session::config::contact_info deserialize_contact(JNIEnv *env, jobject in "Lnetwork/loki/messenger/libsession_util/util/UserPic;"); getPriority = env->GetFieldID(contactClass, "priority", "J"); getExpiry = env->GetFieldID(contactClass, "expiryMode", "Lnetwork/loki/messenger/libsession_util/util/ExpiryMode;"); - jstring name, nickname, account_id; - account_id = static_cast(env->GetObjectField(info, getId)); - name = static_cast(env->GetObjectField(info, getName)); - nickname = static_cast(env->GetObjectField(info, getNick)); + auto account_id = jni_utils::JavaLocalRef(env, static_cast(env->GetObjectField(info, getId))); + auto name = jni_utils::JavaLocalRef(env, static_cast(env->GetObjectField(info, getName))); + auto nickname = jni_utils::JavaLocalRef(env, static_cast(env->GetObjectField(info, getNick))); bool approved, approvedMe, blocked, hidden; int priority = env->GetLongField(info, getPriority); approved = env->GetBooleanField(info, getApproved); approvedMe = env->GetBooleanField(info, getApprovedMe); blocked = env->GetBooleanField(info, getBlocked); - jobject user_pic = env->GetObjectField(info, getUserPic); - jobject expiry_mode = env->GetObjectField(info, getExpiry); + auto user_pic = jni_utils::JavaLocalRef(env, env->GetObjectField(info, getUserPic)); + auto expiry_mode = jni_utils::JavaLocalRef(env, env->GetObjectField(info, getExpiry)); - auto expiry_pair = util::deserialize_expiry(env, expiry_mode); + auto expiry_pair = util::deserialize_expiry(env, expiry_mode.get()); std::string url; std::vector key; - if (user_pic != nullptr) { - auto deserialized_pic = util::deserialize_user_pic(env, user_pic); + if (user_pic.get() != nullptr) { + auto deserialized_pic = util::deserialize_user_pic(env, user_pic.get()); auto url_jstring = deserialized_pic.first; auto url_bytes = env->GetStringUTFChars(url_jstring, nullptr); url = std::string(url_bytes); @@ -70,9 +70,9 @@ inline session::config::contact_info deserialize_contact(JNIEnv *env, jobject in key = util::vector_from_bytes(env, deserialized_pic.second); } - auto account_id_bytes = env->GetStringUTFChars(account_id, nullptr); - auto name_bytes = name ? env->GetStringUTFChars(name, nullptr) : nullptr; - auto nickname_bytes = nickname ? env->GetStringUTFChars(nickname, nullptr) : nullptr; + auto account_id_bytes = env->GetStringUTFChars(account_id.get(), nullptr); + auto name_bytes = name.get() ? env->GetStringUTFChars(name.get(), nullptr) : nullptr; + auto nickname_bytes = nickname.get() ? env->GetStringUTFChars(nickname.get(), nullptr) : nullptr; auto contact_info = conf->get_or_construct(account_id_bytes); if (name_bytes) { @@ -90,12 +90,12 @@ inline session::config::contact_info deserialize_contact(JNIEnv *env, jobject in contact_info.profile_picture = session::config::profile_pic(); } - env->ReleaseStringUTFChars(account_id, account_id_bytes); + env->ReleaseStringUTFChars(account_id.get(), account_id_bytes); if (name_bytes) { - env->ReleaseStringUTFChars(name, name_bytes); + env->ReleaseStringUTFChars(name.get(), name_bytes); } if (nickname_bytes) { - env->ReleaseStringUTFChars(nickname, nickname_bytes); + env->ReleaseStringUTFChars(nickname.get(), nickname_bytes); } contact_info.priority = priority; diff --git a/library/src/main/cpp/conversation.cpp b/library/src/main/cpp/conversation.cpp index 19f5f6f..f501c04 100644 --- a/library/src/main/cpp/conversation.cpp +++ b/library/src/main/cpp/conversation.cpp @@ -1,5 +1,6 @@ #include #include "conversation.h" +#include "jni_utils.h" extern "C" @@ -266,15 +267,8 @@ Java_network_loki_messenger_libsession_1util_ConversationVolatileConfig_all(JNIE jobject thiz) { std::lock_guard lock{util::util_mutex_}; auto convos = ptrToConvoInfo(env, thiz); - jclass stack = env->FindClass("java/util/Stack"); - jmethodID init = env->GetMethodID(stack, "", "()V"); - jobject our_stack = env->NewObject(stack, init); - jmethodID push = env->GetMethodID(stack, "push", "(Ljava/lang/Object;)Ljava/lang/Object;"); - for (const auto& convo : *convos) { - auto contact_obj = serialize_any(env, convo); - env->CallObjectMethod(our_stack, push, contact_obj); - } - return our_stack; + + return jni_utils::jlist_from_collection(env, *convos, serialize_any); } extern "C" JNIEXPORT jobject JNICALL @@ -282,13 +276,8 @@ Java_network_loki_messenger_libsession_1util_ConversationVolatileConfig_allOneTo jobject thiz) { std::lock_guard lock{util::util_mutex_}; auto convos = ptrToConvoInfo(env, thiz); - jclass stack = env->FindClass("java/util/Stack"); - jmethodID init = env->GetMethodID(stack, "", "()V"); - jobject our_stack = env->NewObject(stack, init); - jmethodID push = env->GetMethodID(stack, "push", "(Ljava/lang/Object;)Ljava/lang/Object;"); - for (auto contact = convos->begin_1to1(); contact != convos->end(); ++contact) - env->CallObjectMethod(our_stack, push, serialize_one_to_one(env, *contact)); - return our_stack; + return jni_utils::jlist_from_iterator(env, convos->begin_1to1(), convos->end(), + serialize_one_to_one); } extern "C" JNIEXPORT jobject JNICALL @@ -296,13 +285,8 @@ Java_network_loki_messenger_libsession_1util_ConversationVolatileConfig_allCommu jobject thiz) { std::lock_guard lock{util::util_mutex_}; auto convos = ptrToConvoInfo(env, thiz); - jclass stack = env->FindClass("java/util/Stack"); - jmethodID init = env->GetMethodID(stack, "", "()V"); - jobject our_stack = env->NewObject(stack, init); - jmethodID push = env->GetMethodID(stack, "push", "(Ljava/lang/Object;)Ljava/lang/Object;"); - for (auto contact = convos->begin_communities(); contact != convos->end(); ++contact) - env->CallObjectMethod(our_stack, push, serialize_open_group(env, *contact)); - return our_stack; + return jni_utils::jlist_from_iterator(env, convos->begin_communities(), convos->end(), + serialize_open_group); } extern "C" JNIEXPORT jobject JNICALL @@ -310,13 +294,8 @@ Java_network_loki_messenger_libsession_1util_ConversationVolatileConfig_allLegac JNIEnv *env, jobject thiz) { std::lock_guard lock{util::util_mutex_}; auto convos = ptrToConvoInfo(env, thiz); - jclass stack = env->FindClass("java/util/Stack"); - jmethodID init = env->GetMethodID(stack, "", "()V"); - jobject our_stack = env->NewObject(stack, init); - jmethodID push = env->GetMethodID(stack, "push", "(Ljava/lang/Object;)Ljava/lang/Object;"); - for (auto contact = convos->begin_legacy_groups(); contact != convos->end(); ++contact) - env->CallObjectMethod(our_stack, push, serialize_legacy_group(env, *contact)); - return our_stack; + return jni_utils::jlist_from_iterator(env, convos->begin_legacy_groups(), convos->end(), + serialize_legacy_group); } extern "C" @@ -325,13 +304,8 @@ Java_network_loki_messenger_libsession_1util_ConversationVolatileConfig_allClose jobject thiz) { std::lock_guard lock{util::util_mutex_}; auto convos = ptrToConvoInfo(env, thiz); - jclass stack = env->FindClass("java/util/Stack"); - jmethodID init = env->GetMethodID(stack, "", "()V"); - jobject our_stack = env->NewObject(stack, init); - jmethodID push = env->GetMethodID(stack, "push", "(Ljava/lang/Object;)Ljava/lang/Object;"); - for (auto contact = convos->begin_groups(); contact != convos->end(); ++contact) - env->CallObjectMethod(our_stack, push, serialize_closed_group(env, *contact)); - return our_stack; + return jni_utils::jlist_from_iterator(env, convos->begin_groups(), convos->end(), + serialize_closed_group); } extern "C" diff --git a/library/src/main/cpp/conversation.h b/library/src/main/cpp/conversation.h index 1a8b51e..75797e2 100644 --- a/library/src/main/cpp/conversation.h +++ b/library/src/main/cpp/conversation.h @@ -5,51 +5,52 @@ #include #include "util.h" #include "session/config/convo_info_volatile.hpp" +#include "jni_utils.h" inline session::config::ConvoInfoVolatile *ptrToConvoInfo(JNIEnv *env, jobject obj) { - jclass contactsClass = env->FindClass("network/loki/messenger/libsession_util/ConversationVolatileConfig"); - jfieldID pointerField = env->GetFieldID(contactsClass, "pointer", "J"); + auto contactsClass = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/ConversationVolatileConfig")); + jfieldID pointerField = env->GetFieldID(contactsClass.get(), "pointer", "J"); return (session::config::ConvoInfoVolatile *) env->GetLongField(obj, pointerField); } inline jobject serialize_one_to_one(JNIEnv *env, session::config::convo::one_to_one one_to_one) { - jclass clazz = env->FindClass("network/loki/messenger/libsession_util/util/Conversation$OneToOne"); - jmethodID constructor = env->GetMethodID(clazz, "", "(Ljava/lang/String;JZ)V"); - auto account_id = env->NewStringUTF(one_to_one.session_id.data()); + auto clazz = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/Conversation$OneToOne")); + jmethodID constructor = env->GetMethodID(clazz.get(), "", "(Ljava/lang/String;JZ)V"); + auto account_id = jni_utils::JavaLocalRef(env, env->NewStringUTF(one_to_one.session_id.data())); auto last_read = one_to_one.last_read; auto unread = one_to_one.unread; - jobject serialized = env->NewObject(clazz, constructor, account_id, last_read, unread); + jobject serialized = env->NewObject(clazz.get(), constructor, account_id.get(), last_read, unread); return serialized; } inline jobject serialize_open_group(JNIEnv *env, session::config::convo::community community) { - jclass clazz = env->FindClass("network/loki/messenger/libsession_util/util/Conversation$Community"); + auto clazz = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/Conversation$Community")); auto base_community = util::serialize_base_community(env, community); - jmethodID constructor = env->GetMethodID(clazz, "", + jmethodID constructor = env->GetMethodID(clazz.get(), "", "(Lnetwork/loki/messenger/libsession_util/util/BaseCommunityInfo;JZ)V"); auto last_read = community.last_read; auto unread = community.unread; - jobject serialized = env->NewObject(clazz, constructor, base_community, last_read, unread); + jobject serialized = env->NewObject(clazz.get(), constructor, base_community, last_read, unread); return serialized; } inline jobject serialize_legacy_group(JNIEnv *env, session::config::convo::legacy_group group) { - jclass clazz = env->FindClass("network/loki/messenger/libsession_util/util/Conversation$LegacyGroup"); - jmethodID constructor = env->GetMethodID(clazz, "", "(Ljava/lang/String;JZ)V"); - auto group_id = env->NewStringUTF(group.id.data()); + auto clazz = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/Conversation$LegacyGroup")); + jmethodID constructor = env->GetMethodID(clazz.get(), "", "(Ljava/lang/String;JZ)V"); + auto group_id = jni_utils::JavaLocalRef(env, env->NewStringUTF(group.id.data())); auto last_read = group.last_read; auto unread = group.unread; - jobject serialized = env->NewObject(clazz, constructor, group_id, last_read, unread); + jobject serialized = env->NewObject(clazz.get(), constructor, group_id.get(), last_read, unread); return serialized; } inline jobject serialize_closed_group(JNIEnv* env, session::config::convo::group group) { - jclass clazz = env->FindClass("network/loki/messenger/libsession_util/util/Conversation$ClosedGroup"); - jmethodID constructor = env->GetMethodID(clazz, "", "(Ljava/lang/String;JZ)V"); - auto session_id = env->NewStringUTF(group.id.data()); + auto clazz = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/Conversation$ClosedGroup")); + jmethodID constructor = env->GetMethodID(clazz.get(), "", "(Ljava/lang/String;JZ)V"); + auto session_id = jni_utils::JavaLocalRef(env, env->NewStringUTF(group.id.data())); auto last_read = group.last_read; auto unread = group.unread; - return env->NewObject(clazz, constructor, session_id, last_read, unread); + return env->NewObject(clazz.get(), constructor, session_id.get(), last_read, unread); } inline jobject serialize_any(JNIEnv *env, session::config::convo::any any) { @@ -66,29 +67,29 @@ inline jobject serialize_any(JNIEnv *env, session::config::convo::any any) { } inline session::config::convo::one_to_one deserialize_one_to_one(JNIEnv *env, jobject info, session::config::ConvoInfoVolatile *conf) { - auto clazz = env->FindClass("network/loki/messenger/libsession_util/util/Conversation$OneToOne"); - auto id_getter = env->GetFieldID(clazz, "accountId", "Ljava/lang/String;"); - auto last_read_getter = env->GetFieldID(clazz, "lastRead", "J"); - auto unread_getter = env->GetFieldID(clazz, "unread", "Z"); - jstring id = static_cast(env->GetObjectField(info, id_getter)); - auto id_chars = env->GetStringUTFChars(id, nullptr); + auto clazz = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/Conversation$OneToOne")); + auto id_getter = env->GetFieldID(clazz.get(), "accountId", "Ljava/lang/String;"); + auto last_read_getter = env->GetFieldID(clazz.get(), "lastRead", "J"); + auto unread_getter = env->GetFieldID(clazz.get(), "unread", "Z"); + auto id = jni_utils::JavaLocalRef(env, static_cast(env->GetObjectField(info, id_getter))); + auto id_chars = env->GetStringUTFChars(id.get(), nullptr); std::string id_string = std::string{id_chars}; auto deserialized = conf->get_or_construct_1to1(id_string); deserialized.last_read = env->GetLongField(info, last_read_getter); deserialized.unread = env->GetBooleanField(info, unread_getter); - env->ReleaseStringUTFChars(id, id_chars); + env->ReleaseStringUTFChars(id.get(), id_chars); return deserialized; } inline session::config::convo::community deserialize_community(JNIEnv *env, jobject info, session::config::ConvoInfoVolatile *conf) { - auto clazz = env->FindClass("network/loki/messenger/libsession_util/util/Conversation$Community"); - auto base_community_getter = env->GetFieldID(clazz, "baseCommunityInfo", "Lnetwork/loki/messenger/libsession_util/util/BaseCommunityInfo;"); - auto last_read_getter = env->GetFieldID(clazz, "lastRead", "J"); - auto unread_getter = env->GetFieldID(clazz, "unread", "Z"); + auto clazz = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/Conversation$Community")); + auto base_community_getter = env->GetFieldID(clazz.get(), "baseCommunityInfo", "Lnetwork/loki/messenger/libsession_util/util/BaseCommunityInfo;"); + auto last_read_getter = env->GetFieldID(clazz.get(), "lastRead", "J"); + auto unread_getter = env->GetFieldID(clazz.get(), "unread", "Z"); - auto base_community_info = env->GetObjectField(info, base_community_getter); + auto base_community_info = jni_utils::JavaLocalRef(env, env->GetObjectField(info, base_community_getter)); - auto base_community_deserialized = util::deserialize_base_community(env, base_community_info); + auto base_community_deserialized = util::deserialize_base_community(env, base_community_info.get()); auto deserialized = conf->get_or_construct_community( base_community_deserialized.base_url(), base_community_deserialized.room(), @@ -102,25 +103,25 @@ inline session::config::convo::community deserialize_community(JNIEnv *env, jobj } inline session::config::convo::legacy_group deserialize_legacy_closed_group(JNIEnv *env, jobject info, session::config::ConvoInfoVolatile *conf) { - auto clazz = env->FindClass("network/loki/messenger/libsession_util/util/Conversation$LegacyGroup"); - auto group_id_getter = env->GetFieldID(clazz, "groupId", "Ljava/lang/String;"); - auto last_read_getter = env->GetFieldID(clazz, "lastRead", "J"); - auto unread_getter = env->GetFieldID(clazz, "unread", "Z"); - auto group_id = static_cast(env->GetObjectField(info, group_id_getter)); - auto group_id_bytes = env->GetStringUTFChars(group_id, nullptr); + auto clazz = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/Conversation$LegacyGroup")); + auto group_id_getter = env->GetFieldID(clazz.get(), "groupId", "Ljava/lang/String;"); + auto last_read_getter = env->GetFieldID(clazz.get(), "lastRead", "J"); + auto unread_getter = env->GetFieldID(clazz.get(), "unread", "Z"); + auto group_id = jni_utils::JavaLocalRef(env, static_cast(env->GetObjectField(info, group_id_getter))); + auto group_id_bytes = env->GetStringUTFChars(group_id.get(), nullptr); auto group_id_string = std::string{group_id_bytes}; auto deserialized = conf->get_or_construct_legacy_group(group_id_string); deserialized.last_read = env->GetLongField(info, last_read_getter); deserialized.unread = env->GetBooleanField(info, unread_getter); - env->ReleaseStringUTFChars(group_id, group_id_bytes); + env->ReleaseStringUTFChars(group_id.get(), group_id_bytes); return deserialized; } inline session::config::convo::group deserialize_closed_group(JNIEnv* env, jobject info, session::config::ConvoInfoVolatile* conf) { - auto clazz = env->FindClass("network/loki/messenger/libsession_util/util/Conversation$ClosedGroup"); - auto id_getter = env->GetFieldID(clazz, "accountId", "Ljava/lang/String;"); - auto last_read_getter = env->GetFieldID(clazz, "lastRead", "J"); - auto unread_getter = env->GetFieldID(clazz, "unread", "Z"); + auto clazz = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/Conversation$ClosedGroup")); + auto id_getter = env->GetFieldID(clazz.get(), "accountId", "Ljava/lang/String;"); + auto last_read_getter = env->GetFieldID(clazz.get(), "lastRead", "J"); + auto unread_getter = env->GetFieldID(clazz.get(), "unread", "Z"); auto session_id = (jstring)env->GetObjectField(info, id_getter); auto session_id_bytes = env->GetStringUTFChars(session_id, nullptr); auto last_read = env->GetLongField(info, last_read_getter); @@ -135,19 +136,19 @@ inline session::config::convo::group deserialize_closed_group(JNIEnv* env, jobje } inline std::optional deserialize_any(JNIEnv *env, jobject convo, session::config::ConvoInfoVolatile *conf) { - auto oto_class = env->FindClass("network/loki/messenger/libsession_util/util/Conversation$OneToOne"); - auto og_class = env->FindClass("network/loki/messenger/libsession_util/util/Conversation$Community"); - auto lgc_class = env->FindClass("network/loki/messenger/libsession_util/util/Conversation$LegacyGroup"); - auto gc_class = env->FindClass("network/loki/messenger/libsession_util/util/Conversation$ClosedGroup"); + auto oto_class = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/Conversation$OneToOne")); + auto og_class = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/Conversation$Community")); + auto lgc_class = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/Conversation$LegacyGroup")); + auto gc_class = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/Conversation$ClosedGroup")); - auto object_class = env->GetObjectClass(convo); - if (env->IsSameObject(object_class, oto_class)) { + auto object_class = jni_utils::JavaLocalRef(env, env->GetObjectClass(convo)); + if (env->IsSameObject(object_class.get(), oto_class.get())) { return session::config::convo::any{deserialize_one_to_one(env, convo, conf)}; - } else if (env->IsSameObject(object_class, og_class)) { + } else if (env->IsSameObject(object_class.get(), og_class.get())) { return session::config::convo::any{deserialize_community(env, convo, conf)}; - } else if (env->IsSameObject(object_class, lgc_class)) { + } else if (env->IsSameObject(object_class.get(), lgc_class.get())) { return session::config::convo::any{deserialize_legacy_closed_group(env, convo, conf)}; - } else if (env->IsSameObject(object_class, gc_class)) { + } else if (env->IsSameObject(object_class.get(), gc_class.get())) { return session::config::convo::any{deserialize_closed_group(env, convo, conf)}; } return std::nullopt; diff --git a/library/src/main/cpp/group_info.h b/library/src/main/cpp/group_info.h index 9fadf53..ab48243 100644 --- a/library/src/main/cpp/group_info.h +++ b/library/src/main/cpp/group_info.h @@ -2,10 +2,11 @@ #define SESSION_ANDROID_GROUP_INFO_H #include "util.h" +#include "jni_utils.h" inline session::config::groups::Info* ptrToInfo(JNIEnv* env, jobject obj) { - jclass configClass = env->FindClass("network/loki/messenger/libsession_util/GroupInfoConfig"); - jfieldID pointerField = env->GetFieldID(configClass, "pointer", "J"); + auto configClass = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/GroupInfoConfig")); + jfieldID pointerField = env->GetFieldID(configClass.get(), "pointer", "J"); return (session::config::groups::Info*) env->GetLongField(obj, pointerField); } diff --git a/library/src/main/cpp/group_keys.cpp b/library/src/main/cpp/group_keys.cpp index deacbd6..cbe4650 100644 --- a/library/src/main/cpp/group_keys.cpp +++ b/library/src/main/cpp/group_keys.cpp @@ -57,16 +57,7 @@ JNIEXPORT jobject JNICALL Java_network_loki_messenger_libsession_1util_GroupKeysConfig_groupKeys(JNIEnv *env, jobject thiz) { std::lock_guard lock{util::util_mutex_}; auto config = ptrToKeys(env, thiz); - auto keys = config->group_keys(); - jclass stack = env->FindClass("java/util/Stack"); - jmethodID init = env->GetMethodID(stack, "", "()V"); - jobject our_stack = env->NewObject(stack, init); - jmethodID push = env->GetMethodID(stack, "push", "(Ljava/lang/Object;)Ljava/lang/Object;"); - for (auto& key : keys) { - auto key_bytes = util::bytes_from_span(env, key); - env->CallObjectMethod(our_stack, push, key_bytes); - } - return our_stack; + return jni_utils::jlist_from_collection(env, config->group_keys(), util::bytes_from_span); } extern "C" @@ -206,34 +197,16 @@ JNIEXPORT jobject JNICALL Java_network_loki_messenger_libsession_1util_GroupKeysConfig_keys(JNIEnv *env, jobject thiz) { std::lock_guard lock{util::util_mutex_}; auto ptr = ptrToKeys(env, thiz); - auto keys = ptr->group_keys(); - jclass stack = env->FindClass("java/util/Stack"); - jmethodID init = env->GetMethodID(stack, "", "()V"); - jobject our_stack = env->NewObject(stack, init); - jmethodID push = env->GetMethodID(stack, "push", "(Ljava/lang/Object;)Ljava/lang/Object;"); - for (auto& key : keys) { - auto key_bytes = util::bytes_from_span(env, key); - env->CallObjectMethod(our_stack, push, key_bytes); - } - return our_stack; + return jni_utils::jlist_from_collection(env, ptr->group_keys(), util::bytes_from_span); } extern "C" JNIEXPORT jobject JNICALL -Java_network_loki_messenger_libsession_1util_GroupKeysConfig_currentHashes(JNIEnv *env, +Java_network_loki_messenger_libsession_1util_GroupKeysConfig_activeHashes(JNIEnv *env, jobject thiz) { std::lock_guard lock{util::util_mutex_}; auto ptr = ptrToKeys(env, thiz); - auto existing = ptr->current_hashes(); - jclass stack = env->FindClass("java/util/Stack"); - jmethodID init = env->GetMethodID(stack, "", "()V"); - jobject our_list = env->NewObject(stack, init); - jmethodID push = env->GetMethodID(stack, "push", "(Ljava/lang/Object;)Ljava/lang/Object;"); - for (auto& hash : existing) { - auto hash_bytes = env->NewStringUTF(hash.data()); - env->CallObjectMethod(our_list, push, hash_bytes); - } - return our_list; + return jni_utils::jstring_list_from_collection(env, ptr->active_hashes()); } extern "C" JNIEXPORT jbyteArray JNICALL diff --git a/library/src/main/cpp/group_keys.h b/library/src/main/cpp/group_keys.h index 9958932..73c41ea 100644 --- a/library/src/main/cpp/group_keys.h +++ b/library/src/main/cpp/group_keys.h @@ -2,10 +2,11 @@ #define SESSION_ANDROID_GROUP_KEYS_H #include "util.h" +#include "jni_utils.h" inline session::config::groups::Keys* ptrToKeys(JNIEnv* env, jobject obj) { - jclass configClass = env->FindClass("network/loki/messenger/libsession_util/GroupKeysConfig"); - jfieldID pointerField = env->GetFieldID(configClass, "pointer", "J"); + auto configClass = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/GroupKeysConfig")); + jfieldID pointerField = env->GetFieldID(configClass.get(), "pointer", "J"); return (session::config::groups::Keys*) env->GetLongField(obj, pointerField); } diff --git a/library/src/main/cpp/group_members.cpp b/library/src/main/cpp/group_members.cpp index 21729c7..f8ee6ea 100644 --- a/library/src/main/cpp/group_members.cpp +++ b/library/src/main/cpp/group_members.cpp @@ -29,15 +29,7 @@ JNIEXPORT jobject JNICALL Java_network_loki_messenger_libsession_1util_GroupMembersConfig_all(JNIEnv *env, jobject thiz) { std::lock_guard lock{util::util_mutex_}; auto config = ptrToMembers(env, thiz); - jclass stack = env->FindClass("java/util/Stack"); - jmethodID init = env->GetMethodID(stack, "", "()V"); - jobject our_stack = env->NewObject(stack, init); - jmethodID push = env->GetMethodID(stack, "push", "(Ljava/lang/Object;)Ljava/lang/Object;"); - for (auto& member : *config) { - auto member_obj = util::serialize_group_member(env, member); - env->CallObjectMethod(our_stack, push, member_obj); - } - return our_stack; + return jni_utils::jlist_from_collection(env, *config, util::serialize_group_member); } extern "C" diff --git a/library/src/main/cpp/group_members.h b/library/src/main/cpp/group_members.h index 38e1142..e67329c 100644 --- a/library/src/main/cpp/group_members.h +++ b/library/src/main/cpp/group_members.h @@ -2,15 +2,15 @@ #define SESSION_ANDROID_GROUP_MEMBERS_H #include "util.h" +#include "jni_utils.h" inline session::config::groups::Members* ptrToMembers(JNIEnv* env, jobject obj) { - jclass configClass = env->FindClass("network/loki/messenger/libsession_util/GroupMembersConfig"); - jfieldID pointerField = env->GetFieldID(configClass, "pointer", "J"); + jfieldID pointerField = env->GetFieldID(jni_utils::JavaLocalRef(env, env->GetObjectClass(obj)).get(), "pointer", "J"); return (session::config::groups::Members*) env->GetLongField(obj, pointerField); } inline session::config::groups::member *ptrToMember(JNIEnv *env, jobject thiz) { - auto ptrField = env->GetFieldID(env->GetObjectClass(thiz), "nativePtr", "J"); + auto ptrField = env->GetFieldID(jni_utils::JavaLocalRef(env, env->GetObjectClass(thiz)).get(), "nativePtr", "J"); return reinterpret_cast(env->GetLongField(thiz, ptrField)); } diff --git a/library/src/main/cpp/jni_utils.h b/library/src/main/cpp/jni_utils.h index c9ccd92..05ab0df 100644 --- a/library/src/main/cpp/jni_utils.h +++ b/library/src/main/cpp/jni_utils.h @@ -3,6 +3,10 @@ #include #include +#include +#include + +#include "util.h" namespace jni_utils { /** @@ -49,6 +53,93 @@ namespace jni_utils { return RetT(); }); } + + /** + * A RAII wrapper for a local reference to a Java object. This will automatically delete the local reference + * @tparam JNIType Type of JNI object + */ + template + class JavaLocalRef { + JNIEnv *env_; + JNIType ref_; + public: + JavaLocalRef(JNIEnv *env, JNIType ref) : env_(env), ref_(ref) {} + ~JavaLocalRef() { + if (ref_) { + env_->DeleteLocalRef(ref_); + } + } + + inline JNIType get() const { + return ref_; + } + }; + + /** + * Create a Java List from an iterator. + * + * @tparam IterBegin The type of beginning iterator + * @tparam IterEnd The type of end iterator. It must be comparable to IterBegin + * @tparam ConvertItemFunc The lambda to convert the item of the iterator into jobject/std::optional. If an std::nullopt is returned, the item will be skipped + * @param env + * @param begin + * @param end + * @param func + * @return The java list + */ + template + jobject jlist_from_iterator(JNIEnv *env, IterBegin begin, IterEnd end, const ConvertItemFunc &func) { + auto list_clazz = JavaLocalRef(env, env->FindClass("java/util/ArrayList")); + jmethodID init = env->GetMethodID(list_clazz.get(), "", "()V"); + jobject our_list = env->NewObject(list_clazz.get(), init); + jmethodID push = env->GetMethodID(list_clazz.get(), "add", "(Ljava/lang/Object;)Z"); + + for (auto iter = begin; iter != end; ++iter) { + std::optional item_java = func(env, *iter); + if (item_java.has_value()) { + env->CallBooleanMethod(our_list, push, *item_java); + env->DeleteLocalRef(*item_java); + } + } + + return our_list; + } + + /** + * Convenience function to create a Java List from a collection, using the lambda to convert + * each item. + * + * See [jlist_from_iterator] for more info. + */ + template + jobject jlist_from_collection(JNIEnv *env, const Collection& obj, const ConvertItemFunc &func) { + return jlist_from_iterator(env, obj.begin(), obj.end(), func); + } + + /** + * Convenience function to create a Java List from a collection of strings + * + * See [jlist_from_iterator] for more info. + */ + template + jobject jstring_list_from_collection(JNIEnv *env, const Collection& obj) { + return jlist_from_collection(env, obj, util::jstringFromOptional); + } + + /** + * Create a Java Bytes class from the collection. The collection must be continous range of byte data + */ + template + jobject session_bytes_from_range(JNIEnv *env, const Collection &obj) { + auto bytes_clazz = JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/Bytes")); + jmethodID init = env->GetMethodID(bytes_clazz.get(), "", "([B)V"); + + static_assert(sizeof(*obj.data()) == sizeof(jbyte)); + + auto bytes_array = JavaLocalRef(env, env->NewByteArray(static_cast(obj.size()))); + env->SetByteArrayRegion(bytes_array.get(), 0, static_cast(obj.size()), reinterpret_cast(obj.data())); + return env->NewObject(bytes_clazz.get(), init, bytes_array.get()); + } } #endif //SESSION_ANDROID_JNI_UTILS_H diff --git a/library/src/main/cpp/user_groups.cpp b/library/src/main/cpp/user_groups.cpp index 029f7a0..ff73dca 100644 --- a/library/src/main/cpp/user_groups.cpp +++ b/library/src/main/cpp/user_groups.cpp @@ -153,15 +153,9 @@ Java_network_loki_messenger_libsession_1util_UserGroupsConfig_size(JNIEnv *env, return conf->size(); } -inline jobject iterator_as_java_stack(JNIEnv *env, const session::config::UserGroups::iterator& begin, const session::config::UserGroups::iterator& end) { - jclass stack = env->FindClass("java/util/Stack"); - jmethodID init = env->GetMethodID(stack, "", "()V"); - jobject our_stack = env->NewObject(stack, init); - jmethodID push = env->GetMethodID(stack, "push", "(Ljava/lang/Object;)Ljava/lang/Object;"); - for (auto it = begin; it != end;) { - // do something with it - auto item = *it; - jobject serialized = nullptr; +inline jobject iterator_as_java_list(JNIEnv *env, session::config::UserGroups::iterator begin, session::config::UserGroups::iterator end) { + return jni_utils::jlist_from_iterator(env, begin, end, [](JNIEnv *env, const session::config::UserGroups::value_type &item) { + std::optional serialized = std::nullopt; if (auto* lgc = std::get_if(&item)) { serialized = serialize_legacy_group_info(env, *lgc); } else if (auto* community = std::get_if(&item)) { @@ -169,12 +163,9 @@ inline jobject iterator_as_java_stack(JNIEnv *env, const session::config::UserGr } else if (auto* closed = std::get_if(&item)) { serialized = serialize_closed_group_info(env, *closed); } - if (serialized != nullptr) { - env->CallObjectMethod(our_stack, push, serialized); - } - it++; - } - return our_stack; + + return serialized; + }); } extern "C" @@ -182,8 +173,7 @@ JNIEXPORT jobject JNICALL Java_network_loki_messenger_libsession_1util_UserGroupsConfig_all(JNIEnv *env, jobject thiz) { std::lock_guard lock{util::util_mutex_}; auto conf = ptrToUserGroups(env, thiz); - jobject all_stack = iterator_as_java_stack(env, conf->begin(), conf->end()); - return all_stack; + return iterator_as_java_list(env, conf->begin(), conf->end()); } extern "C" @@ -192,8 +182,7 @@ Java_network_loki_messenger_libsession_1util_UserGroupsConfig_allCommunityInfo(J jobject thiz) { std::lock_guard lock{util::util_mutex_}; auto conf = ptrToUserGroups(env, thiz); - jobject community_stack = iterator_as_java_stack(env, conf->begin_communities(), conf->end()); - return community_stack; + return iterator_as_java_list(env, conf->begin_communities(), conf->end()); } extern "C" @@ -202,8 +191,7 @@ Java_network_loki_messenger_libsession_1util_UserGroupsConfig_allLegacyGroupInfo jobject thiz) { std::lock_guard lock{util::util_mutex_}; auto conf = ptrToUserGroups(env, thiz); - jobject legacy_stack = iterator_as_java_stack(env, conf->begin_legacy_groups(), conf->end()); - return legacy_stack; + return iterator_as_java_list(env, conf->begin_legacy_groups(), conf->end()); } extern "C" @@ -289,9 +277,7 @@ Java_network_loki_messenger_libsession_1util_UserGroupsConfig_allClosedGroupInfo jobject thiz) { std::lock_guard lock{util::util_mutex_}; auto conf = ptrToUserGroups(env, thiz); - auto closed_group_stack = iterator_as_java_stack(env, conf->begin_groups(), conf->end()); - - return closed_group_stack; + return iterator_as_java_list(env, conf->begin_groups(), conf->end()); } extern "C" diff --git a/library/src/main/cpp/user_groups.h b/library/src/main/cpp/user_groups.h index eb9eb34..17132f5 100644 --- a/library/src/main/cpp/user_groups.h +++ b/library/src/main/cpp/user_groups.h @@ -4,66 +4,67 @@ #include "jni.h" #include "util.h" +#include "jni_utils.h" #include "conversation.h" #include "session/config/user_groups.hpp" inline session::config::UserGroups* ptrToUserGroups(JNIEnv *env, jobject obj) { - jclass configClass = env->FindClass("network/loki/messenger/libsession_util/UserGroupsConfig"); - jfieldID pointerField = env->GetFieldID(configClass, "pointer", "J"); + auto configClass = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/UserGroupsConfig")); + jfieldID pointerField = env->GetFieldID(configClass.get(), "pointer", "J"); return (session::config::UserGroups*) env->GetLongField(obj, pointerField); } inline void deserialize_members_into(JNIEnv *env, jobject members_map, session::config::legacy_group_info& to_append_group) { - jclass map_class = env->FindClass("java/util/Map"); - jclass map_entry_class = env->FindClass("java/util/Map$Entry"); - jclass set_class = env->FindClass("java/util/Set"); - jclass iterator_class = env->FindClass("java/util/Iterator"); - jclass boxed_bool = env->FindClass("java/lang/Boolean"); - - jmethodID get_entry_set = env->GetMethodID(map_class, "entrySet", "()Ljava/util/Set;"); - jmethodID get_at = env->GetMethodID(set_class, "iterator", "()Ljava/util/Iterator;"); - jmethodID has_next = env->GetMethodID(iterator_class, "hasNext", "()Z"); - jmethodID next = env->GetMethodID(iterator_class, "next", "()Ljava/lang/Object;"); - jmethodID get_key = env->GetMethodID(map_entry_class, "getKey", "()Ljava/lang/Object;"); - jmethodID get_value = env->GetMethodID(map_entry_class, "getValue", "()Ljava/lang/Object;"); - jmethodID get_bool_value = env->GetMethodID(boxed_bool, "booleanValue", "()Z"); - - jobject entry_set = env->CallObjectMethod(members_map, get_entry_set); - jobject iterator = env->CallObjectMethod(entry_set, get_at); - - while (env->CallBooleanMethod(iterator, has_next)) { - jobject entry = env->CallObjectMethod(iterator, next); - jstring key = static_cast(env->CallObjectMethod(entry, get_key)); - jobject boxed = env->CallObjectMethod(entry, get_value); - bool is_admin = env->CallBooleanMethod(boxed, get_bool_value); - auto member_string = env->GetStringUTFChars(key, nullptr); + auto map_class = jni_utils::JavaLocalRef(env, env->FindClass("java/util/Map")); + auto map_entry_class = jni_utils::JavaLocalRef(env, env->FindClass("java/util/Map$Entry")); + auto set_class = jni_utils::JavaLocalRef(env, env->FindClass("java/util/Set")); + auto iterator_class = jni_utils::JavaLocalRef(env, env->FindClass("java/util/Iterator")); + auto boxed_bool = jni_utils::JavaLocalRef(env, env->FindClass("java/lang/Boolean")); + + jmethodID get_entry_set = env->GetMethodID(map_class.get(), "entrySet", "()Ljava/util/Set;"); + jmethodID get_at = env->GetMethodID(set_class.get(), "iterator", "()Ljava/util/Iterator;"); + jmethodID has_next = env->GetMethodID(iterator_class.get(), "hasNext", "()Z"); + jmethodID next = env->GetMethodID(iterator_class.get(), "next", "()Ljava/lang/Object;"); + jmethodID get_key = env->GetMethodID(map_entry_class.get(), "getKey", "()Ljava/lang/Object;"); + jmethodID get_value = env->GetMethodID(map_entry_class.get(), "getValue", "()Ljava/lang/Object;"); + jmethodID get_bool_value = env->GetMethodID(boxed_bool.get(), "booleanValue", "()Z"); + + auto entry_set = jni_utils::JavaLocalRef(env, env->CallObjectMethod(members_map, get_entry_set)); + auto iterator = jni_utils::JavaLocalRef(env, env->CallObjectMethod(entry_set.get(), get_at)); + + while (env->CallBooleanMethod(iterator.get(), has_next)) { + auto entry = jni_utils::JavaLocalRef(env, env->CallObjectMethod(iterator.get(), next)); + auto key = jni_utils::JavaLocalRef(env, static_cast(env->CallObjectMethod(entry.get(), get_key))); + auto boxed = jni_utils::JavaLocalRef(env, env->CallObjectMethod(entry.get(), get_value)); + bool is_admin = env->CallBooleanMethod(boxed.get(), get_bool_value); + auto member_string = env->GetStringUTFChars(key.get(), nullptr); to_append_group.insert(member_string, is_admin); - env->ReleaseStringUTFChars(key, member_string); + env->ReleaseStringUTFChars(key.get(), member_string); } } inline session::config::legacy_group_info deserialize_legacy_group_info(JNIEnv *env, jobject info, session::config::UserGroups* conf) { - auto clazz = env->FindClass("network/loki/messenger/libsession_util/util/GroupInfo$LegacyGroupInfo"); - auto id_field = env->GetFieldID(clazz, "accountId", "Ljava/lang/String;"); - auto name_field = env->GetFieldID(clazz, "name", "Ljava/lang/String;"); - auto members_field = env->GetFieldID(clazz, "members", "Ljava/util/Map;"); - auto enc_pub_key_field = env->GetFieldID(clazz, "encPubKey", "[B"); - auto enc_sec_key_field = env->GetFieldID(clazz, "encSecKey", "[B"); - auto priority_field = env->GetFieldID(clazz, "priority", "J"); - auto disappearing_timer_field = env->GetFieldID(clazz, "disappearingTimer", "J"); - auto joined_at_field = env->GetFieldID(clazz, "joinedAtSecs", "J"); - auto id = static_cast(env->GetObjectField(info, id_field)); - jstring name = static_cast(env->GetObjectField(info, name_field)); - jobject members_map = env->GetObjectField(info, members_field); - jbyteArray enc_pub_key = static_cast(env->GetObjectField(info, enc_pub_key_field)); - jbyteArray enc_sec_key = static_cast(env->GetObjectField(info, enc_sec_key_field)); + auto clazz = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/GroupInfo$LegacyGroupInfo")); + auto id_field = env->GetFieldID(clazz.get(), "accountId", "Ljava/lang/String;"); + auto name_field = env->GetFieldID(clazz.get(), "name", "Ljava/lang/String;"); + auto members_field = env->GetFieldID(clazz.get(), "members", "Ljava/util/Map;"); + auto enc_pub_key_method = env->GetMethodID(clazz.get(), "getEncPubKeyAsByteArray", "()[B"); + auto enc_sec_key_method = env->GetMethodID(clazz.get(), "getEncSecKeyAsByteArray", "()[B"); + auto priority_field = env->GetFieldID(clazz.get(), "priority", "J"); + auto disappearing_timer_field = env->GetFieldID(clazz.get(), "disappearingTimer", "J"); + auto joined_at_field = env->GetFieldID(clazz.get(), "joinedAtSecs", "J"); + auto id = jni_utils::JavaLocalRef(env, static_cast(env->GetObjectField(info, id_field))); + auto name = jni_utils::JavaLocalRef(env, static_cast(env->GetObjectField(info, name_field))); + auto members_map = jni_utils::JavaLocalRef(env, env->GetObjectField(info, members_field)); + auto enc_pub_key = jni_utils::JavaLocalRef(env, static_cast(env->CallObjectMethod(info, enc_pub_key_method))); + auto enc_sec_key = jni_utils::JavaLocalRef(env, static_cast(env->CallObjectMethod(info, enc_sec_key_method))); int priority = env->GetLongField(info, priority_field); long joined_at = env->GetLongField(info, joined_at_field); - auto id_bytes = util::string_from_jstring(env, id); - auto name_bytes = env->GetStringUTFChars(name, nullptr); - auto enc_pub_key_bytes = util::vector_from_bytes(env, enc_pub_key); - auto enc_sec_key_bytes = util::vector_from_bytes(env, enc_sec_key); + auto id_bytes = util::string_from_jstring(env, id.get()); + auto name_bytes = env->GetStringUTFChars(name.get(), nullptr); + auto enc_pub_key_bytes = util::vector_from_bytes(env, enc_pub_key.get()); + auto enc_sec_key_bytes = util::vector_from_bytes(env, enc_sec_key.get()); auto info_deserialized = conf->get_or_construct_legacy_group(id_bytes); @@ -71,23 +72,23 @@ inline session::config::legacy_group_info deserialize_legacy_group_info(JNIEnv * for (auto member = current_members.begin(); member != current_members.end(); ++member) { info_deserialized.erase(member->first); } - deserialize_members_into(env, members_map, info_deserialized); + deserialize_members_into(env, members_map.get(), info_deserialized); info_deserialized.name = name_bytes; info_deserialized.enc_pubkey = enc_pub_key_bytes; info_deserialized.enc_seckey = enc_sec_key_bytes; info_deserialized.priority = priority; info_deserialized.disappearing_timer = std::chrono::seconds(env->GetLongField(info, disappearing_timer_field)); info_deserialized.joined_at = joined_at; - env->ReleaseStringUTFChars(name, name_bytes); + env->ReleaseStringUTFChars(name.get(), name_bytes); return info_deserialized; } inline session::config::community_info deserialize_community_info(JNIEnv *env, jobject info, session::config::UserGroups* conf) { - auto clazz = env->FindClass("network/loki/messenger/libsession_util/util/GroupInfo$CommunityGroupInfo"); - auto base_info = env->GetFieldID(clazz, "community", "Lnetwork/loki/messenger/libsession_util/util/BaseCommunityInfo;"); - auto priority = env->GetFieldID(clazz, "priority", "J"); - jobject base_community_info = env->GetObjectField(info, base_info); - auto deserialized_base_info = util::deserialize_base_community(env, base_community_info); + auto clazz = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/GroupInfo$CommunityGroupInfo")); + auto base_info = env->GetFieldID(clazz.get(), "community", "Lnetwork/loki/messenger/libsession_util/util/BaseCommunityInfo;"); + auto priority = env->GetFieldID(clazz.get(), "priority", "J"); + auto base_community_info = jni_utils::JavaLocalRef(env, env->GetObjectField(info, base_info)); + auto deserialized_base_info = util::deserialize_base_community(env, base_community_info.get()); int deserialized_priority = env->GetLongField(info, priority); auto community_info = conf->get_or_construct_community(deserialized_base_info.base_url(), deserialized_base_info.room(), deserialized_base_info.pubkey_hex()); community_info.priority = deserialized_priority; @@ -95,73 +96,71 @@ inline session::config::community_info deserialize_community_info(JNIEnv *env, j } inline jobject serialize_members(JNIEnv *env, std::map members_map) { - jclass map_class = env->FindClass("java/util/HashMap"); - jclass boxed_bool = env->FindClass("java/lang/Boolean"); - jmethodID map_constructor = env->GetMethodID(map_class, "", "()V"); - jmethodID insert = env->GetMethodID(map_class, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); - jmethodID new_bool = env->GetMethodID(boxed_bool, "", "(Z)V"); + auto map_class = jni_utils::JavaLocalRef(env, env->FindClass("java/util/HashMap")); + auto boxed_bool = jni_utils::JavaLocalRef(env, env->FindClass("java/lang/Boolean")); + jmethodID map_constructor = env->GetMethodID(map_class.get(), "", "()V"); + jmethodID insert = env->GetMethodID(map_class.get(), "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + jmethodID new_bool = env->GetMethodID(boxed_bool.get(), "", "(Z)V"); - jobject new_map = env->NewObject(map_class, map_constructor); + auto new_map = env->NewObject(map_class.get(), map_constructor); for (auto it = members_map.begin(); it != members_map.end(); it++) { - auto account_id = env->NewStringUTF(it->first.data()); + auto account_id = jni_utils::JavaLocalRef(env, env->NewStringUTF(it->first.data())); bool is_admin = it->second; - auto jbool = env->NewObject(boxed_bool, new_bool, is_admin); - env->CallObjectMethod(new_map, insert, account_id, jbool); + auto jbool = jni_utils::JavaLocalRef(env, env->NewObject(boxed_bool.get(), new_bool, is_admin)); + env->CallObjectMethod(new_map, insert, account_id.get(), jbool.get()); } return new_map; } inline jobject serialize_legacy_group_info(JNIEnv *env, session::config::legacy_group_info info) { - jstring account_id = env->NewStringUTF(info.session_id.data()); - jstring name = env->NewStringUTF(info.name.data()); - jobject members = serialize_members(env, info.members()); - jbyteArray enc_pubkey = util::bytes_from_vector(env, info.enc_pubkey); - jbyteArray enc_seckey = util::bytes_from_vector(env, info.enc_seckey); + auto account_id = jni_utils::JavaLocalRef(env, env->NewStringUTF(info.session_id.data())); + auto name = jni_utils::JavaLocalRef(env, env->NewStringUTF(info.name.data())); + auto members = jni_utils::JavaLocalRef(env, serialize_members(env, info.members())); + auto enc_pubkey = jni_utils::session_bytes_from_range(env, info.enc_pubkey); + auto enc_seckey = jni_utils::session_bytes_from_range(env, info.enc_seckey); long long priority = info.priority; long long joined_at = info.joined_at; - jclass legacy_group_class = env->FindClass("network/loki/messenger/libsession_util/util/GroupInfo$LegacyGroupInfo"); - jmethodID constructor = env->GetMethodID(legacy_group_class, "", "(Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;[B[BJJJ)V"); - jobject serialized = env->NewObject(legacy_group_class, constructor, account_id, name, members, enc_pubkey, enc_seckey, priority, (jlong) info.disappearing_timer.count(), joined_at); - return serialized; + auto legacy_group_class = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/GroupInfo$LegacyGroupInfo")); + jmethodID constructor = env->GetMethodID(legacy_group_class.get(), "", "(Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lnetwork/loki/messenger/libsession_util/util/Bytes;Lnetwork/loki/messenger/libsession_util/util/Bytes;JJJ)V"); + return env->NewObject(legacy_group_class.get(), constructor, account_id.get(), name.get(), members.get(), enc_pubkey, enc_seckey, priority, (jlong) info.disappearing_timer.count(), joined_at); } inline jobject serialize_closed_group_info(JNIEnv* env, session::config::group_info info) { auto session_id = util::jstringFromOptional(env, info.id); - jbyteArray admin_bytes = info.secretkey.empty() ? nullptr : util::bytes_from_vector(env, info.secretkey); - jbyteArray auth_bytes = info.auth_data.empty() ? nullptr : util::bytes_from_vector(env, info.auth_data); - jstring name = util::jstringFromOptional(env, info.name); - - jclass group_info_class = env->FindClass("network/loki/messenger/libsession_util/util/GroupInfo$ClosedGroupInfo"); - jmethodID constructor = env->GetMethodID(group_info_class, "","(Ljava/lang/String;[B[BJZLjava/lang/String;ZZJ)V"); - jobject return_object = env->NewObject(group_info_class,constructor, - session_id, admin_bytes, auth_bytes, (jlong)info.priority, info.invited, name, - info.kicked(), info.is_destroyed(), info.joined_at); - return return_object; + auto admin_bytes = jni_utils::JavaLocalRef(env, info.secretkey.empty() ? nullptr : jni_utils::session_bytes_from_range(env, info.secretkey)); + auto auth_bytes = jni_utils::JavaLocalRef(env, info.auth_data.empty() ? nullptr : jni_utils::session_bytes_from_range(env, info.auth_data)); + auto name = jni_utils::JavaLocalRef(env, util::jstringFromOptional(env, info.name)); + + auto group_info_class = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/GroupInfo$ClosedGroupInfo")); + jmethodID constructor = env->GetMethodID(group_info_class.get(), "","(Ljava/lang/String;Lnetwork/loki/messenger/libsession_util/util/Bytes;Lnetwork/loki/messenger/libsession_util/util/Bytes;JZLjava/lang/String;ZZJ)V"); + return env->NewObject(group_info_class.get(), constructor, + session_id, admin_bytes.get(), auth_bytes.get(), (jlong)info.priority, info.invited, name.get(), + info.kicked(), info.is_destroyed(), info.joined_at); } inline session::config::group_info deserialize_closed_group_info(JNIEnv* env, jobject info_serialized) { - jclass closed_group_class = env->FindClass("network/loki/messenger/libsession_util/util/GroupInfo$ClosedGroupInfo"); - jfieldID id_field = env->GetFieldID(closed_group_class, "groupAccountId", "Ljava/lang/String;"); - jfieldID secret_field = env->GetFieldID(closed_group_class, "adminKey", "[B"); - jfieldID auth_field = env->GetFieldID(closed_group_class, "authData", "[B"); - jfieldID priority_field = env->GetFieldID(closed_group_class, "priority", "J"); - jfieldID invited_field = env->GetFieldID(closed_group_class, "invited", "Z"); - jfieldID name_field = env->GetFieldID(closed_group_class, "name", "Ljava/lang/String;"); - jfieldID destroy_field = env->GetFieldID(closed_group_class, "destroyed", "Z"); - jfieldID kicked_field = env->GetFieldID(closed_group_class, "kicked", "Z"); - jfieldID joined_at_field = env->GetFieldID(closed_group_class, "joinedAtSecs", "J"); - - - auto id_jobject = static_cast(env->GetObjectField(info_serialized, id_field)); - jbyteArray secret_jBytes = (jbyteArray)env->GetObjectField(info_serialized, secret_field); - jbyteArray auth_jBytes = (jbyteArray)env->GetObjectField(info_serialized, auth_field); - jstring name_jstring = (jstring)env->GetObjectField(info_serialized, name_field); - - auto id_bytes = util::string_from_jstring(env, id_jobject); - auto secret_bytes = util::vector_from_bytes(env, secret_jBytes); - auto auth_bytes = util::vector_from_bytes(env, auth_jBytes); - auto name = util::string_from_jstring(env, name_jstring); + auto closed_group_class = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/GroupInfo$ClosedGroupInfo")); + jfieldID id_field = env->GetFieldID(closed_group_class.get(), "groupAccountId", "Ljava/lang/String;"); + auto secret_method = env->GetMethodID(closed_group_class.get(), "getAdminKeyAsByteArray", "()[B"); + auto auth_method = env->GetMethodID(closed_group_class.get(), "getAuthDataAsByteArray", "()[B"); + jfieldID priority_field = env->GetFieldID(closed_group_class.get(), "priority", "J"); + jfieldID invited_field = env->GetFieldID(closed_group_class.get(), "invited", "Z"); + jfieldID name_field = env->GetFieldID(closed_group_class.get(), "name", "Ljava/lang/String;"); + jfieldID destroy_field = env->GetFieldID(closed_group_class.get(), "destroyed", "Z"); + jfieldID kicked_field = env->GetFieldID(closed_group_class.get(), "kicked", "Z"); + jfieldID joined_at_field = env->GetFieldID(closed_group_class.get(), "joinedAtSecs", "J"); + + + auto id_jobject = jni_utils::JavaLocalRef(env, static_cast(env->GetObjectField(info_serialized, id_field))); + auto secret_jBytes = jni_utils::JavaLocalRef(env, (jbyteArray)env->CallObjectMethod(info_serialized, secret_method)); + auto auth_jBytes = jni_utils::JavaLocalRef(env, (jbyteArray)env->CallObjectMethod(info_serialized, auth_method)); + auto name_jstring = jni_utils::JavaLocalRef(env, (jstring)env->GetObjectField(info_serialized, name_field)); + + auto id_bytes = util::string_from_jstring(env, id_jobject.get()); + auto secret_bytes = util::vector_from_bytes(env, secret_jBytes.get()); + auto auth_bytes = util::vector_from_bytes(env, auth_jBytes.get()); + auto name = util::string_from_jstring(env, name_jstring.get()); session::config::group_info group_info(id_bytes); group_info.auth_data = auth_bytes; @@ -185,9 +184,9 @@ inline session::config::group_info deserialize_closed_group_info(JNIEnv* env, jo inline jobject serialize_community_info(JNIEnv *env, session::config::community_info info) { auto priority = (long long)info.priority; auto serialized_info = util::serialize_base_community(env, info); - auto clazz = env->FindClass("network/loki/messenger/libsession_util/util/GroupInfo$CommunityGroupInfo"); - jmethodID constructor = env->GetMethodID(clazz, "", "(Lnetwork/loki/messenger/libsession_util/util/BaseCommunityInfo;J)V"); - jobject serialized = env->NewObject(clazz, constructor, serialized_info, priority); + auto clazz = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/GroupInfo$CommunityGroupInfo")); + jmethodID constructor = env->GetMethodID(clazz.get(), "", "(Lnetwork/loki/messenger/libsession_util/util/BaseCommunityInfo;J)V"); + jobject serialized = env->NewObject(clazz.get(), constructor, serialized_info, priority); return serialized; } diff --git a/library/src/main/cpp/user_profile.h b/library/src/main/cpp/user_profile.h index cb1b8d9..2b3f273 100644 --- a/library/src/main/cpp/user_profile.h +++ b/library/src/main/cpp/user_profile.h @@ -5,9 +5,11 @@ #include #include +#include "jni_utils.h" + inline session::config::UserProfile* ptrToProfile(JNIEnv* env, jobject obj) { - jclass configClass = env->FindClass("network/loki/messenger/libsession_util/UserProfile"); - jfieldID pointerField = env->GetFieldID(configClass, "pointer", "J"); + auto configClass = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/UserProfile")); + jfieldID pointerField = env->GetFieldID(configClass.get(), "pointer", "J"); return (session::config::UserProfile*) env->GetLongField(obj, pointerField); } diff --git a/library/src/main/cpp/util.cpp b/library/src/main/cpp/util.cpp index 9003c16..60f13d7 100644 --- a/library/src/main/cpp/util.cpp +++ b/library/src/main/cpp/util.cpp @@ -4,6 +4,7 @@ #include #include #include +#include "jni_utils.h" #include @@ -57,30 +58,29 @@ namespace util { } jobject serialize_user_pic(JNIEnv *env, session::config::profile_pic pic) { - jclass returnObjectClass = env->FindClass("network/loki/messenger/libsession_util/util/UserPic"); - jmethodID constructor = env->GetMethodID(returnObjectClass, "", "(Ljava/lang/String;[B)V"); - jstring url = env->NewStringUTF(pic.url.data()); - jbyteArray byteArray = util::bytes_from_vector(env, pic.key); - return env->NewObject(returnObjectClass, constructor, url, byteArray); + auto returnObjectClass = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/UserPic")); + jmethodID constructor = env->GetMethodID(returnObjectClass.get(), "", "(Ljava/lang/String;Lnetwork/loki/messenger/libsession_util/util/Bytes;)V"); + return env->NewObject(returnObjectClass.get(), constructor, + env->NewStringUTF(pic.url.data()), + jni_utils::session_bytes_from_range(env, pic.key) + ); } std::pair deserialize_user_pic(JNIEnv *env, jobject user_pic) { - jclass userPicClass = env->FindClass("network/loki/messenger/libsession_util/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); - return {pic, key}; + auto userPicClass = jni_utils::JavaLocalRef(env, env->GetObjectClass(user_pic)); + return { + static_cast(env->CallObjectMethod(user_pic, env->GetMethodID(userPicClass.get(), "getUrl", "()Ljava/lang/String;"))), + static_cast(env->CallObjectMethod(user_pic, env->GetMethodID(userPicClass.get(), "getKeyAsByteArray", "()[B"))) + }; } jobject serialize_base_community(JNIEnv *env, const session::config::community& community) { - jclass base_community_clazz = env->FindClass("network/loki/messenger/libsession_util/util/BaseCommunityInfo"); - jmethodID base_community_constructor = env->GetMethodID(base_community_clazz, "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); - auto base_url = env->NewStringUTF(community.base_url().data()); - auto room = env->NewStringUTF(community.room().data()); - auto pubkey_jstring = env->NewStringUTF(community.pubkey_hex().data()); - jobject ret = env->NewObject(base_community_clazz, base_community_constructor, base_url, room, pubkey_jstring); - return ret; + auto base_community_clazz = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/BaseCommunityInfo")); + jmethodID base_community_constructor = env->GetMethodID(base_community_clazz.get(), "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + auto base_url = jni_utils::JavaLocalRef(env, env->NewStringUTF(community.base_url().data())); + auto room = jni_utils::JavaLocalRef(env, env->NewStringUTF(community.room().data())); + auto pubkey_jstring = jni_utils::JavaLocalRef(env, env->NewStringUTF(community.pubkey_hex().data())); + return env->NewObject(base_community_clazz.get(), base_community_constructor, base_url.get(), room.get(), pubkey_jstring.get()); } session::config::community deserialize_base_community(JNIEnv *env, jobject base_community) { @@ -88,93 +88,81 @@ namespace util { jfieldID base_url_field = env->GetFieldID(base_community_clazz, "baseUrl", "Ljava/lang/String;"); jfieldID room_field = env->GetFieldID(base_community_clazz, "room", "Ljava/lang/String;"); jfieldID pubkey_hex_field = env->GetFieldID(base_community_clazz, "pubKeyHex", "Ljava/lang/String;"); - auto base_url = (jstring)env->GetObjectField(base_community,base_url_field); - auto room = (jstring)env->GetObjectField(base_community, room_field); - auto pub_key_hex = (jstring)env->GetObjectField(base_community, pubkey_hex_field); - auto base_url_chars = env->GetStringUTFChars(base_url, nullptr); - auto room_chars = env->GetStringUTFChars(room, nullptr); - auto pub_key_hex_chars = env->GetStringUTFChars(pub_key_hex, nullptr); + auto base_url = jni_utils::JavaLocalRef(env, (jstring)env->GetObjectField(base_community,base_url_field)); + auto room = jni_utils::JavaLocalRef(env, (jstring)env->GetObjectField(base_community, room_field)); + auto pub_key_hex = jni_utils::JavaLocalRef(env, (jstring)env->GetObjectField(base_community, pubkey_hex_field)); + auto base_url_chars = env->GetStringUTFChars(base_url.get(), nullptr); + auto room_chars = env->GetStringUTFChars(room.get(), nullptr); + auto pub_key_hex_chars = env->GetStringUTFChars(pub_key_hex.get(), nullptr); auto community = session::config::community(base_url_chars, room_chars, pub_key_hex_chars); - env->ReleaseStringUTFChars(base_url, base_url_chars); - env->ReleaseStringUTFChars(room, room_chars); - env->ReleaseStringUTFChars(pub_key_hex, pub_key_hex_chars); + env->ReleaseStringUTFChars(base_url.get(), base_url_chars); + env->ReleaseStringUTFChars(room.get(), room_chars); + env->ReleaseStringUTFChars(pub_key_hex.get(), pub_key_hex_chars); return community; } jobject serialize_expiry(JNIEnv *env, const session::config::expiration_mode& mode, const std::chrono::seconds& time_seconds) { - jclass none = env->FindClass("network/loki/messenger/libsession_util/util/ExpiryMode$NONE"); - jfieldID none_instance = env->GetStaticFieldID(none, "INSTANCE", "Lnetwork/loki/messenger/libsession_util/util/ExpiryMode$NONE;"); - jclass after_send = env->FindClass("network/loki/messenger/libsession_util/util/ExpiryMode$AfterSend"); - jmethodID send_init = env->GetMethodID(after_send, "", "(J)V"); - jclass after_read = env->FindClass("network/loki/messenger/libsession_util/util/ExpiryMode$AfterRead"); - jmethodID read_init = env->GetMethodID(after_read, "", "(J)V"); + auto none = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/ExpiryMode$NONE")); + jfieldID none_instance = env->GetStaticFieldID(none.get(), "INSTANCE", "Lnetwork/loki/messenger/libsession_util/util/ExpiryMode$NONE;"); + auto after_send = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/ExpiryMode$AfterSend")); + jmethodID send_init = env->GetMethodID(after_send.get(), "", "(J)V"); + auto after_read = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/ExpiryMode$AfterRead")); + jmethodID read_init = env->GetMethodID(after_read.get(), "", "(J)V"); if (mode == session::config::expiration_mode::none) { - return env->GetStaticObjectField(none, none_instance); + return env->GetStaticObjectField(none.get(), none_instance); } else if (mode == session::config::expiration_mode::after_send) { - return env->NewObject(after_send, send_init, time_seconds.count()); + return env->NewObject(after_send.get(), send_init, time_seconds.count()); } else if (mode == session::config::expiration_mode::after_read) { - return env->NewObject(after_read, read_init, time_seconds.count()); + return env->NewObject(after_read.get(), read_init, time_seconds.count()); } return nullptr; } std::pair deserialize_expiry(JNIEnv *env, jobject expiry_mode) { - jclass parent = env->FindClass("network/loki/messenger/libsession_util/util/ExpiryMode"); - jclass after_read = env->FindClass("network/loki/messenger/libsession_util/util/ExpiryMode$AfterRead"); - jclass after_send = env->FindClass("network/loki/messenger/libsession_util/util/ExpiryMode$AfterSend"); - jfieldID duration_seconds = env->GetFieldID(parent, "expirySeconds", "J"); + auto parent = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/ExpiryMode")); + auto after_read = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/ExpiryMode$AfterRead")); + auto after_send = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/ExpiryMode$AfterSend")); + jfieldID duration_seconds = env->GetFieldID(parent.get(), "expirySeconds", "J"); - jclass object_class = env->GetObjectClass(expiry_mode); + auto object_class = jni_utils::JavaLocalRef(env, env->GetObjectClass(expiry_mode)); - if (env->IsSameObject(object_class, after_read)) { + if (env->IsSameObject(object_class.get(), after_read.get())) { return std::pair(session::config::expiration_mode::after_read, env->GetLongField(expiry_mode, duration_seconds)); - } else if (env->IsSameObject(object_class, after_send)) { + } else if (env->IsSameObject(object_class.get(), after_send.get())) { return std::pair(session::config::expiration_mode::after_send, env->GetLongField(expiry_mode, duration_seconds)); } return std::pair(session::config::expiration_mode::none, 0); } - jobject build_string_stack(JNIEnv* env, std::vector to_add) { - jclass stack_class = env->FindClass("java/util/Stack"); - jmethodID constructor = env->GetMethodID(stack_class,"", "()V"); - jmethodID add = env->GetMethodID(stack_class, "push", "(Ljava/lang/Object;)Ljava/lang/Object;"); - jobject our_stack = env->NewObject(stack_class, constructor); - for (std::basic_string_view string: to_add) { - env->CallObjectMethod(our_stack, add, env->NewStringUTF(string.data())); - } - return our_stack; - } - jobject serialize_group_member(JNIEnv* env, const session::config::groups::member& member) { - jclass group_member_class = env->FindClass("network/loki/messenger/libsession_util/util/GroupMember"); - jmethodID constructor = env->GetMethodID(group_member_class, "", "(J)V"); - return env->NewObject(group_member_class, + auto group_member_class = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/GroupMember")); + jmethodID constructor = env->GetMethodID(group_member_class.get(), "", "(J)V"); + return env->NewObject(group_member_class.get(), constructor, reinterpret_cast(new session::config::groups::member(member)) ); } jobject deserialize_swarm_auth(JNIEnv *env, session::config::groups::Keys::swarm_auth auth) { - jclass swarm_auth_class = env->FindClass("network/loki/messenger/libsession_util/GroupKeysConfig$SwarmAuth"); - jmethodID constructor = env->GetMethodID(swarm_auth_class, "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); - jstring sub_account = env->NewStringUTF(auth.subaccount.data()); - jstring sub_account_sig = env->NewStringUTF(auth.subaccount_sig.data()); - jstring signature = env->NewStringUTF(auth.signature.data()); + auto swarm_auth_class = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/GroupKeysConfig$SwarmAuth")); + jmethodID constructor = env->GetMethodID(swarm_auth_class.get(), "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + auto sub_account = jni_utils::JavaLocalRef(env, env->NewStringUTF(auth.subaccount.data())); + auto sub_account_sig = jni_utils::JavaLocalRef(env, env->NewStringUTF(auth.subaccount_sig.data())); + auto signature = jni_utils::JavaLocalRef(env, env->NewStringUTF(auth.signature.data())); - return env->NewObject(swarm_auth_class, constructor, sub_account, sub_account_sig, signature); + return env->NewObject(swarm_auth_class.get(), constructor, sub_account.get(), sub_account_sig.get(), signature.get()); } jobject jlongFromOptional(JNIEnv* env, std::optional optional) { if (!optional) { return nullptr; } - jclass longClass = env->FindClass("java/lang/Long"); - jmethodID constructor = env->GetMethodID(longClass, "", "(J)V"); - jobject returned = env->NewObject(longClass, constructor, (jlong)*optional); - return returned; + auto longClass = jni_utils::JavaLocalRef(env, env->FindClass("java/lang/Long")); + jmethodID constructor = env->GetMethodID(longClass.get(), "", "(J)V"); + return env->NewObject(longClass.get(), constructor, (jlong)*optional); } jstring jstringFromOptional(JNIEnv* env, std::optional optional) { @@ -193,14 +181,13 @@ Java_network_loki_messenger_libsession_1util_util_Sodium_ed25519KeyPair(JNIEnv * auto seed_bytes = util::vector_from_bytes(env, seed); crypto_sign_ed25519_seed_keypair(ed_pk.data(), ed_sk.data(), seed_bytes.data()); - jclass kp_class = env->FindClass("network/loki/messenger/libsession_util/util/KeyPair"); - jmethodID kp_constructor = env->GetMethodID(kp_class, "", "([B[B)V"); - - jbyteArray pk_jarray = util::bytes_from_span(env, std::span {ed_pk.data(), ed_pk.size()}); - jbyteArray sk_jarray = util::bytes_from_span(env, std::span {ed_sk.data(), ed_sk.size()}); + auto kp_class = jni_utils::JavaLocalRef(env, env->FindClass("network/loki/messenger/libsession_util/util/KeyPair")); + jmethodID kp_constructor = env->GetMethodID(kp_class.get(), "", "(Lnetwork/loki/messenger/libsession_util/util/Bytes;Lnetwork/loki/messenger/libsession_util/util/Bytes;)V"); - jobject return_obj = env->NewObject(kp_class, kp_constructor, pk_jarray, sk_jarray); - return return_obj; + return env->NewObject(kp_class.get(), kp_constructor, + jni_utils::JavaLocalRef(env, jni_utils::session_bytes_from_range(env, ed_pk)).get(), + jni_utils::JavaLocalRef(env, jni_utils::session_bytes_from_range(env, ed_sk)).get() + ); } extern "C" diff --git a/library/src/main/cpp/util.h b/library/src/main/cpp/util.h index c7b8e11..5bf8436 100644 --- a/library/src/main/cpp/util.h +++ b/library/src/main/cpp/util.h @@ -29,7 +29,7 @@ namespace util { jobject serialize_group_member(JNIEnv* env, const session::config::groups::member& member); jobject jlongFromOptional(JNIEnv* env, std::optional optional); jstring jstringFromOptional(JNIEnv* env, std::optional optional); - jobject build_string_stack(JNIEnv* env, std::vector to_add); - jobject deserialize_swarm_auth(JNIEnv *env, session::config::groups::Keys::swarm_auth auth);} + jobject deserialize_swarm_auth(JNIEnv *env, session::config::groups::Keys::swarm_auth auth); +} #endif \ No newline at end of file diff --git a/library/src/main/java/network/loki/messenger/libsession_util/Config.kt b/library/src/main/java/network/loki/messenger/libsession_util/Config.kt index 94ac94a..d9f11e8 100644 --- a/library/src/main/java/network/loki/messenger/libsession_util/Config.kt +++ b/library/src/main/java/network/loki/messenger/libsession_util/Config.kt @@ -9,7 +9,6 @@ import network.loki.messenger.libsession_util.util.GroupInfo import network.loki.messenger.libsession_util.util.GroupMember import network.loki.messenger.libsession_util.util.UserPic import java.io.Closeable -import java.util.Stack sealed class Config(initialPointer: Long): Closeable { var pointer = initialPointer @@ -35,14 +34,14 @@ interface ReadableConfig { fun namespace(): Int fun needsPush(): Boolean fun needsDump(): Boolean - fun currentHashes(): List + fun activeHashes(): List } interface MutableConfig : ReadableConfig { fun push(): ConfigPush fun dump(): ByteArray fun encryptionDomain(): String - fun confirmPushed(seqNo: Long, newHash: String) + fun confirmPushed(seqNo: Long, hashes: Array) fun dirty(): Boolean } @@ -65,9 +64,9 @@ sealed class ConfigBase(pointer: Long): Config(pointer), MutableConfig { external override fun push(): ConfigPush external override fun dump(): ByteArray external override fun encryptionDomain(): String - external override fun confirmPushed(seqNo: Long, newHash: String) - external fun merge(toMerge: Array>): Stack - external override fun currentHashes(): List + external override fun confirmPushed(seqNo: Long, hashes: Array) + external fun merge(toMerge: Array>): List + external override fun activeHashes(): List } @@ -398,7 +397,7 @@ class GroupMembersConfig private constructor(pointer: Long): ConfigBase(pointer) override fun namespace() = Namespace.GROUP_MEMBERS() - external override fun all(): Stack + external override fun all(): List external override fun erase(pubKeyHex: String): Boolean external override fun get(pubKeyHex: String): GroupMember? external override fun getOrConstruct(pubKeyHex: String): GroupMember @@ -415,17 +414,17 @@ class GroupMembersConfig private constructor(pointer: Long): ConfigBase(pointer) sealed class ConfigSig(pointer: Long) : Config(pointer) interface ReadableGroupKeysConfig { - fun groupKeys(): Stack + fun groupKeys(): List fun needsDump(): Boolean fun dump(): ByteArray fun needsRekey(): Boolean fun pendingKey(): ByteArray? fun supplementFor(userSessionIds: List): ByteArray fun pendingConfig(): ByteArray? - fun currentHashes(): List + fun activeHashes(): List fun encrypt(plaintext: ByteArray): ByteArray fun decrypt(ciphertext: ByteArray): Pair? - fun keys(): Stack + fun keys(): List fun subAccountSign(message: ByteArray, signingValue: ByteArray): GroupKeysConfig.SwarmAuth fun getSubAccountToken(sessionId: String, canWrite: Boolean = true, canDelete: Boolean = false): ByteArray fun currentGeneration(): Int @@ -476,7 +475,7 @@ class GroupKeysConfig private constructor( override fun namespace() = Namespace.GROUP_KEYS() - external override fun groupKeys(): Stack + external override fun groupKeys(): List external override fun needsDump(): Boolean external override fun dump(): ByteArray external fun loadKey(message: ByteArray, @@ -503,13 +502,13 @@ class GroupKeysConfig private constructor( } external override fun pendingConfig(): ByteArray? - external override fun currentHashes(): List + external override fun activeHashes(): List external fun rekey(infoPtr: Long, membersPtr: Long): ByteArray external override fun encrypt(plaintext: ByteArray): ByteArray external override fun decrypt(ciphertext: ByteArray): Pair? - external override fun keys(): Stack + external override fun keys(): List external override fun makeSubAccount(sessionId: String, canWrite: Boolean, canDelete: Boolean): ByteArray external override fun getSubAccountToken(sessionId: String, canWrite: Boolean, canDelete: Boolean): ByteArray diff --git a/library/src/main/java/network/loki/messenger/libsession_util/util/Bytes.kt b/library/src/main/java/network/loki/messenger/libsession_util/util/Bytes.kt new file mode 100644 index 0000000..052597e --- /dev/null +++ b/library/src/main/java/network/loki/messenger/libsession_util/util/Bytes.kt @@ -0,0 +1,26 @@ +package network.loki.messenger.libsession_util.util + +/** + * A wrapper class for ByteArray to provide the content equals and hashCode implementation, + * so that we can safely use the byte array in data classes. + */ +class Bytes(val data: ByteArray) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Bytes) return false + + if (!data.contentEquals(other.data)) return false + + return true + } + + override fun hashCode(): Int { + return data.contentHashCode() + } + + companion object { + fun ByteArray.toBytes(): Bytes { + return Bytes(this) + } + } +} \ No newline at end of file diff --git a/library/src/main/java/network/loki/messenger/libsession_util/util/GroupInfo.kt b/library/src/main/java/network/loki/messenger/libsession_util/util/GroupInfo.kt index 864ba7d..88daf4a 100644 --- a/library/src/main/java/network/loki/messenger/libsession_util/util/GroupInfo.kt +++ b/library/src/main/java/network/loki/messenger/libsession_util/util/GroupInfo.kt @@ -6,8 +6,8 @@ sealed class GroupInfo { data class ClosedGroupInfo( val groupAccountId: String, - val adminKey: ByteArray?, - val authData: ByteArray?, + val adminKey: Bytes?, + val authData: Bytes?, val priority: Long, val invited: Boolean, val name: String, @@ -16,15 +16,23 @@ sealed class GroupInfo { val joinedAtSecs: Long ): GroupInfo() { init { - require(adminKey == null || adminKey.isNotEmpty()) { + require(adminKey == null || adminKey.data.isNotEmpty()) { "Admin key must be non-empty if present" } - require(authData == null || authData.isNotEmpty()) { + require(authData == null || authData.data.isNotEmpty()) { "Auth data must be non-empty if present" } } + // For native code + val authDataAsByteArray: ByteArray? + get() = authData?.data + + // For native code + val adminKeyAsByteArray: ByteArray? + get() = adminKey?.data + fun hasAdminKey() = adminKey != null val shouldPoll: Boolean @@ -46,8 +54,8 @@ sealed class GroupInfo { val accountId: String, val name: String, val members: Map, - val encPubKey: ByteArray, - val encSecKey: ByteArray, + val encPubKey: Bytes, + val encSecKey: Bytes, val priority: Long, val disappearingTimer: Long, val joinedAtSecs: Long @@ -57,36 +65,12 @@ sealed class GroupInfo { external fun NAME_MAX_LENGTH(): Int } - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as LegacyGroupInfo - - if (accountId != other.accountId) return false - if (name != other.name) return false - if (members != other.members) return false - if (!encPubKey.contentEquals(other.encPubKey)) return false - if (!encSecKey.contentEquals(other.encSecKey)) return false - if (priority != other.priority) return false - if (disappearingTimer != other.disappearingTimer) return false - if (joinedAtSecs != other.joinedAtSecs) return false - - return true - } - - override fun hashCode(): Int { - var result = accountId.hashCode() - result = 31 * result + name.hashCode() - result = 31 * result + members.hashCode() - result = 31 * result + encPubKey.contentHashCode() - result = 31 * result + encSecKey.contentHashCode() - result = 31 * result + priority.hashCode() - result = 31 * result + disappearingTimer.hashCode() - result = 31 * result + joinedAtSecs.hashCode() - return result - } + // For native code + val encPubKeyAsByteArray: ByteArray + get() = encPubKey.data + // For native code + val encSecKeyAsByteArray: ByteArray + get() = encSecKey.data } - } \ No newline at end of file diff --git a/library/src/main/java/network/loki/messenger/libsession_util/util/Utils.kt b/library/src/main/java/network/loki/messenger/libsession_util/util/Utils.kt index 4222395..a19e747 100644 --- a/library/src/main/java/network/loki/messenger/libsession_util/util/Utils.kt +++ b/library/src/main/java/network/loki/messenger/libsession_util/util/Utils.kt @@ -1,67 +1,21 @@ package network.loki.messenger.libsession_util.util -data class ConfigPush(val config: ByteArray, val seqNo: Long, val obsoleteHashes: List) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false +data class ConfigPush(val messages: List, val seqNo: Long, val obsoleteHashes: List) - other as ConfigPush +data class UserPic(val url: String, val key: Bytes) { + constructor(url: String, key: ByteArray) + :this(url, Bytes(key)) - if (!config.contentEquals(other.config)) return false - if (seqNo != other.seqNo) return false - if (obsoleteHashes != other.obsoleteHashes) return false - - return true - } - - override fun hashCode(): Int { - var result = config.contentHashCode() - result = 31 * result + seqNo.hashCode() - result = 31 * result + obsoleteHashes.hashCode() - return result - } - -} - -data class UserPic(val url: String, val key: ByteArray) { companion object { - val DEFAULT = UserPic("", byteArrayOf()) + val DEFAULT = UserPic("", Bytes(byteArrayOf())) } - 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 - } + // Convenience method to get the key as a ByteArray from native side + val keyAsByteArray: ByteArray + get() = key.data } -data class KeyPair(val pubKey: ByteArray, val secretKey: ByteArray) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as KeyPair - - if (!pubKey.contentEquals(other.pubKey)) return false - if (!secretKey.contentEquals(other.secretKey)) return false - - return true - } - - override fun hashCode(): Int { - var result = pubKey.contentHashCode() - result = 31 * result + secretKey.contentHashCode() - return result - } -} \ No newline at end of file +data class KeyPair(val pubKey: Bytes, val secretKey: Bytes) { + constructor(pubKey: ByteArray, secretKey: ByteArray) + : this(Bytes(pubKey), Bytes(secretKey)) +} diff --git a/libsession-util b/libsession-util index e2eed34..95b9fe7 160000 --- a/libsession-util +++ b/libsession-util @@ -1 +1 @@ -Subproject commit e2eed34ef25b3076a29000e43163bda832793159 +Subproject commit 95b9fe74d1c419487f6e69458db1ebc2bb824a2a