mirror of
https://github.com/mollyim/monero-wallet-sdk.git
synced 2024-12-25 07:29:23 -05:00
lib: refactor jni helpers
This commit is contained in:
parent
979e547346
commit
5c4cfd9d92
@ -52,6 +52,7 @@ set(CMAKE_VISIBILITY_INLINES_HIDDEN true)
|
||||
|
||||
set(COMMON_SOURCES
|
||||
common/jvm.cc
|
||||
common/java_native.cc
|
||||
)
|
||||
|
||||
set(WALLET_SOURCES
|
||||
|
192
lib/android/src/main/cpp/common/java_native.cc
Normal file
192
lib/android/src/main/cpp/common/java_native.cc
Normal file
@ -0,0 +1,192 @@
|
||||
#include "java_native.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
namespace monero {
|
||||
|
||||
extern ScopedJavaGlobalRef<jclass> StringClass;
|
||||
|
||||
jclass GetClass(JNIEnv* env, const char* name) {
|
||||
jclass clazz = env->FindClass(name);
|
||||
LOG_FATAL_IF(!clazz, "Java class not found: %s", name);
|
||||
return clazz;
|
||||
}
|
||||
|
||||
jmethodID GetMethodId(JNIEnv* env,
|
||||
jclass clazz,
|
||||
const char* name,
|
||||
const char* signature) {
|
||||
jmethodID ret = env->GetMethodID(clazz, name, signature);
|
||||
LOG_FATAL_IF(CheckException(env),
|
||||
"Error during GetMethodID: %s, %s", name, signature);
|
||||
return ret;
|
||||
}
|
||||
|
||||
jmethodID GetStaticMethodId(JNIEnv* env,
|
||||
jclass clazz,
|
||||
const char* name,
|
||||
const char* signature) {
|
||||
jmethodID ret = env->GetStaticMethodID(clazz, name, signature);
|
||||
LOG_FATAL_IF(CheckException(env),
|
||||
"Error during GetStaticMethodID: %s, %s", name, signature);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CallVoidMethod(JNIEnv* env, jobject obj, jmethodID method_id, ...) {
|
||||
va_list args;
|
||||
va_start(args, method_id);
|
||||
env->CallVoidMethodV(obj, method_id, args);
|
||||
ThrowRuntimeErrorOnException(env);
|
||||
}
|
||||
|
||||
jint CallIntMethod(JNIEnv* env, jobject obj, jmethodID method_id, ...) {
|
||||
va_list args;
|
||||
va_start(args, method_id);
|
||||
jint ret = env->CallIntMethodV(obj, method_id, args);
|
||||
ThrowRuntimeErrorOnException(env);
|
||||
return ret;
|
||||
}
|
||||
|
||||
jboolean CallBooleanMethod(JNIEnv* env, jobject obj, jmethodID method_id, ...) {
|
||||
va_list args;
|
||||
va_start(args, method_id);
|
||||
jboolean ret = env->CallBooleanMethodV(obj, method_id, args);
|
||||
ThrowRuntimeErrorOnException(env);
|
||||
return ret;
|
||||
}
|
||||
|
||||
jobject CallObjectMethod(JNIEnv* env, jobject obj, jmethodID method_id, ...) {
|
||||
va_list args;
|
||||
va_start(args, method_id);
|
||||
jobject ret = env->CallObjectMethodV(obj, method_id, args);
|
||||
ThrowRuntimeErrorOnException(env);
|
||||
return ret;
|
||||
}
|
||||
|
||||
jstring CallStringMethod(JNIEnv* env, jobject obj, jmethodID method_id, ...) {
|
||||
va_list args;
|
||||
va_start(args, method_id);
|
||||
jstring ret = (jstring) env->CallObjectMethodV(obj, method_id, args);
|
||||
ThrowRuntimeErrorOnException(env);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CallStaticVoidMethod(JNIEnv* env, jclass clazz, jmethodID method_id, ...) {
|
||||
va_list args;
|
||||
va_start(args, method_id);
|
||||
env->CallStaticVoidMethodV(clazz, method_id, args);
|
||||
ThrowRuntimeErrorOnException(env);
|
||||
}
|
||||
|
||||
jobject CallStaticObjectMethod(JNIEnv* env, jclass clazz, jmethodID method_id, ...) {
|
||||
va_list args;
|
||||
va_start(args, method_id);
|
||||
jobject ret = env->CallStaticObjectMethodV(clazz, method_id, args);
|
||||
ThrowRuntimeErrorOnException(env);
|
||||
return ret;
|
||||
}
|
||||
|
||||
jobject NewObject(JNIEnv* env, jclass clazz, jmethodID method_id, ...) {
|
||||
va_list args;
|
||||
va_start(args, method_id);
|
||||
jobject new_obj = env->NewObjectV(clazz, method_id, args);
|
||||
ThrowRuntimeErrorOnException(env);
|
||||
LOG_ASSERT(new_obj);
|
||||
return new_obj;
|
||||
}
|
||||
|
||||
std::string JavaToNativeString(JNIEnv* env, jstring j_string) {
|
||||
const char* chars = env->GetStringUTFChars(j_string, /*isCopy=*/nullptr);
|
||||
LOG_FATAL_IF(CheckException(env));
|
||||
const jsize j_len = env->GetStringUTFLength(j_string);
|
||||
LOG_FATAL_IF(CheckException(env));
|
||||
std::string str(chars, j_len);
|
||||
env->ReleaseStringUTFChars(j_string, chars);
|
||||
LOG_FATAL_IF(CheckException(env));
|
||||
return str;
|
||||
}
|
||||
|
||||
std::vector<char> JavaToNativeByteArray(JNIEnv* env, jbyteArray j_array) {
|
||||
const jsize len = env->GetArrayLength(j_array);
|
||||
LOG_FATAL_IF(CheckException(env));
|
||||
std::vector<char> v(len);
|
||||
env->GetByteArrayRegion(j_array, 0, len,
|
||||
reinterpret_cast<jbyte*>(&v[0]));
|
||||
LOG_FATAL_IF(CheckException(env));
|
||||
return v;
|
||||
}
|
||||
|
||||
std::vector<int32_t> JavaToNativeIntArray(JNIEnv* env, jintArray j_array) {
|
||||
const jsize len = env->GetArrayLength(j_array);
|
||||
LOG_FATAL_IF(CheckException(env));
|
||||
std::vector<int32_t> v(len);
|
||||
env->GetIntArrayRegion(j_array, 0, len,
|
||||
reinterpret_cast<jint*>(&v[0]));
|
||||
LOG_FATAL_IF(CheckException(env));
|
||||
return v;
|
||||
}
|
||||
|
||||
std::vector<int64_t> JavaToNativeLongArray(JNIEnv* env, jlongArray j_array) {
|
||||
const jsize len = env->GetArrayLength(j_array);
|
||||
LOG_FATAL_IF(CheckException(env));
|
||||
std::vector<int64_t> v(len);
|
||||
env->GetLongArrayRegion(j_array, 0, len,
|
||||
reinterpret_cast<jlong*>(&v[0]));
|
||||
LOG_FATAL_IF(CheckException(env));
|
||||
return v;
|
||||
}
|
||||
|
||||
jstring NativeToJavaString(JNIEnv* env, const char* str) {
|
||||
jstring j_str = env->NewStringUTF(str);
|
||||
LOG_FATAL_IF(CheckException(env));
|
||||
return j_str;
|
||||
}
|
||||
|
||||
jstring NativeToJavaString(JNIEnv* env, const std::string& str) {
|
||||
return NativeToJavaString(env, str.c_str());
|
||||
}
|
||||
|
||||
jbyteArray NativeToJavaByteArray(JNIEnv* env,
|
||||
const char* data,
|
||||
size_t size) {
|
||||
LOG_FATAL_IF(size > INT_MAX);
|
||||
const jsize j_len = (jsize) size;
|
||||
jbyteArray j_array = env->NewByteArray(j_len);
|
||||
LOG_FATAL_IF(CheckException(env));
|
||||
env->SetByteArrayRegion(j_array, 0, j_len,
|
||||
reinterpret_cast<const jbyte*>(data));
|
||||
LOG_FATAL_IF(CheckException(env));
|
||||
return j_array;
|
||||
}
|
||||
|
||||
jlongArray NativeToJavaLongArray(JNIEnv* env,
|
||||
const int64_t* data,
|
||||
size_t size) {
|
||||
LOG_FATAL_IF(size > INT_MAX);
|
||||
const jsize j_len = (jsize) size;
|
||||
jlongArray j_array = env->NewLongArray(j_len);
|
||||
LOG_FATAL_IF(CheckException(env));
|
||||
env->SetLongArrayRegion(j_array, 0, j_len, data);
|
||||
LOG_FATAL_IF(CheckException(env));
|
||||
return j_array;
|
||||
}
|
||||
|
||||
jlongArray NativeToJavaLongArray(JNIEnv* env,
|
||||
const uint64_t* data,
|
||||
size_t size) {
|
||||
return NativeToJavaLongArray(env, (int64_t*) data, size);
|
||||
}
|
||||
|
||||
jlong NativeToJavaPointer(void* ptr) {
|
||||
static_assert(sizeof(intptr_t) <= sizeof(jlong), "");
|
||||
return reinterpret_cast<intptr_t>(ptr);
|
||||
}
|
||||
|
||||
jobjectArray NativeToJavaStringArray(JNIEnv* env, const std::vector<std::string>& container) {
|
||||
auto convert_function = [](JNIEnv* env, const std::string& str) {
|
||||
return ScopedJavaLocalRef<jstring>(env, NativeToJavaString(env, str));
|
||||
};
|
||||
return NativeToJavaObjectArray(env, container, StringClass.obj(), convert_function);
|
||||
}
|
||||
|
||||
} // namespace monero
|
102
lib/android/src/main/cpp/common/java_native.h
Normal file
102
lib/android/src/main/cpp/common/java_native.h
Normal file
@ -0,0 +1,102 @@
|
||||
#ifndef COMMON_JVM_NATIVE_H_
|
||||
#define COMMON_JVM_NATIVE_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/jvm.h"
|
||||
#include "common/scoped_java_ref.h"
|
||||
|
||||
namespace monero {
|
||||
|
||||
// Returns a class local reference from a fully-qualified name.
|
||||
// This method must be called on a context capable of loading Java classes.
|
||||
jclass GetClass(JNIEnv* env, const char* name);
|
||||
|
||||
// Functions to query method IDs.
|
||||
jmethodID GetMethodId(JNIEnv* env,
|
||||
jclass clazz,
|
||||
const char* name,
|
||||
const char* signature);
|
||||
jmethodID GetStaticMethodId(JNIEnv* env,
|
||||
jclass clazz,
|
||||
const char* name,
|
||||
const char* signature);
|
||||
|
||||
// Call non-static functions.
|
||||
void CallVoidMethod(JNIEnv* env, jobject obj, jmethodID method_id, ...);
|
||||
jint CallIntMethod(JNIEnv* env, jobject obj, jmethodID method_id, ...);
|
||||
jboolean CallBooleanMethod(JNIEnv* env, jobject obj, jmethodID method_id, ...);
|
||||
jobject CallObjectMethod(JNIEnv* env, jobject obj, jmethodID method_id, ...);
|
||||
jstring CallStringMethod(JNIEnv* env, jobject obj, jmethodID method_id, ...);
|
||||
|
||||
// Call static functions.
|
||||
void CallStaticVoidMethod(JNIEnv* env, jclass clazz, jmethodID method_id, ...);
|
||||
jobject CallStaticObjectMethod(JNIEnv* env, jclass clazz, jmethodID method_id, ...);
|
||||
|
||||
// Create a new object using a constructor.
|
||||
jobject NewObject(JNIEnv* env, jclass clazz, jmethodID method_id, ...);
|
||||
|
||||
// --------------------------------------------------------
|
||||
// -- Methods for converting Java types to native types. --
|
||||
// --------------------------------------------------------
|
||||
|
||||
// Converts a (UTF-16) jstring to a native string in modified UTF-8 encoding.
|
||||
std::string JavaToNativeString(JNIEnv* env, jstring j_string);
|
||||
|
||||
std::vector<char> JavaToNativeByteArray(JNIEnv* env, jbyteArray j_array);
|
||||
std::vector<int32_t> JavaToNativeIntArray(JNIEnv* env, jintArray j_array);
|
||||
std::vector<int64_t> JavaToNativeLongArray(JNIEnv* env, jlongArray j_array);
|
||||
|
||||
template<typename T, typename Java_T = jobject, typename Convert>
|
||||
std::vector<T> JavaToNativeVector(JNIEnv* env,
|
||||
jobjectArray j_container,
|
||||
Convert convert) {
|
||||
std::vector<T> container;
|
||||
const jsize size = env->GetArrayLength(j_container);
|
||||
ThrowRuntimeErrorOnException(env);
|
||||
container.reserve(size);
|
||||
for (jsize i = 0; i < size; ++i) {
|
||||
container.emplace_back(convert(
|
||||
env, ScopedJavaLocalRef<Java_T>(
|
||||
env, (Java_T) env->GetObjectArrayElement(j_container, i)).obj()));
|
||||
}
|
||||
return container;
|
||||
}
|
||||
// --------------------------------------------------------
|
||||
// -- Methods for converting native types to Java types. --
|
||||
// --------------------------------------------------------
|
||||
|
||||
jstring NativeToJavaString(JNIEnv* env, const char* str);
|
||||
jstring NativeToJavaString(JNIEnv* env, const std::string& str);
|
||||
|
||||
jbyteArray NativeToJavaByteArray(JNIEnv* env, const char* data, size_t size);
|
||||
jlongArray NativeToJavaLongArray(JNIEnv* env, const int64_t* data, size_t size);
|
||||
jlongArray NativeToJavaLongArray(JNIEnv* env, const uint64_t* data, size_t size);
|
||||
|
||||
jlong NativeToJavaPointer(void* ptr);
|
||||
|
||||
// Helper function for converting std::vector<T> into a Java array.
|
||||
template<typename T, typename Convert>
|
||||
jobjectArray NativeToJavaObjectArray(JNIEnv* env,
|
||||
const std::vector<T>& container,
|
||||
jclass clazz,
|
||||
Convert convert) {
|
||||
jobjectArray j_container = env->NewObjectArray(
|
||||
container.size(), clazz, nullptr);
|
||||
ThrowRuntimeErrorOnException(env);
|
||||
int i = 0;
|
||||
for (const T& element: container) {
|
||||
env->SetObjectArrayElement(j_container, i,
|
||||
convert(env, element).obj());
|
||||
++i;
|
||||
}
|
||||
return j_container;
|
||||
}
|
||||
|
||||
jobjectArray NativeToJavaStringArray(JNIEnv* env,
|
||||
const std::vector<std::string>& container);
|
||||
|
||||
} // namespace monero
|
||||
|
||||
#endif // COMMON_JVM_NATIVE_H_
|
@ -1,9 +1,12 @@
|
||||
#include "jvm.h"
|
||||
|
||||
#include <string>
|
||||
#include <sys/prctl.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
namespace monero {
|
||||
|
||||
static JavaVM* g_jvm = nullptr;
|
||||
@ -16,14 +19,14 @@ static pthread_once_t g_jni_ptr_once = PTHREAD_ONCE_INIT;
|
||||
static pthread_key_t g_jni_ptr;
|
||||
|
||||
// Return thread ID as a string.
|
||||
static std::string getThreadId() {
|
||||
static std::string GetThreadId() {
|
||||
char buf[21]; // Big enough to hold decimal digits for 64-bits plus NULL
|
||||
snprintf(buf, sizeof(buf), "%lu", static_cast<long>(gettid()));
|
||||
return {buf};
|
||||
}
|
||||
|
||||
// Return the current thread's name.
|
||||
static std::string getThreadName() {
|
||||
static std::string GetThreadName() {
|
||||
char name[17] = {0}; // In Android thread's name can be up to 16 bytes long
|
||||
if (prctl(PR_GET_NAME, name) < 0) {
|
||||
return {"<noname>"};
|
||||
@ -31,9 +34,9 @@ static std::string getThreadName() {
|
||||
return {name};
|
||||
}
|
||||
|
||||
static JNIEnv* attachCurrentThread() {
|
||||
static JNIEnv* AttachCurrentThread() {
|
||||
JNIEnv* env = nullptr;
|
||||
std::string name(getThreadName() + " - " + getThreadId());
|
||||
std::string name(GetThreadName() + " - " + GetThreadId());
|
||||
JavaVMAttachArgs args;
|
||||
args.version = JNI_VERSION_1_6;
|
||||
args.name = name.c_str();
|
||||
@ -45,7 +48,7 @@ static JNIEnv* attachCurrentThread() {
|
||||
}
|
||||
|
||||
// Return the `JNIEnv*` for the current thread.
|
||||
JNIEnv* getJniEnv() {
|
||||
JNIEnv* GetJniEnv() {
|
||||
void* env = pthread_getspecific(g_jni_ptr);
|
||||
if (!env) {
|
||||
// Call GetEnv() first to detect if the thread already is attached.
|
||||
@ -56,7 +59,7 @@ JNIEnv* getJniEnv() {
|
||||
"Unexpected GetEnv ret: %d:%p", status, env);
|
||||
if (status != JNI_OK) {
|
||||
// Can race safely. Attaching a thread that is already attached is a no-op.
|
||||
return attachCurrentThread();
|
||||
return AttachCurrentThread();
|
||||
}
|
||||
}
|
||||
return reinterpret_cast<JNIEnv*>(env);
|
||||
@ -65,7 +68,7 @@ JNIEnv* getJniEnv() {
|
||||
// This function only runs on threads where `g_jni_ptr` is non-NULL, meaning
|
||||
// we were responsible for originally attaching the thread, so are responsible
|
||||
// for detaching it now.
|
||||
static void threadDestructor(void* prev_jni_ptr) {
|
||||
static void ThreadDestructor(void* prev_jni_ptr) {
|
||||
auto* env = reinterpret_cast<JNIEnv*>(prev_jni_ptr);
|
||||
if (!env) {
|
||||
return;
|
||||
@ -74,16 +77,16 @@ static void threadDestructor(void* prev_jni_ptr) {
|
||||
LOG_FATAL_IF(status != JNI_OK, "Failed to detach thread: %d", status);
|
||||
}
|
||||
|
||||
static void createJniPtrKey() {
|
||||
LOG_FATAL_IF(pthread_key_create(&g_jni_ptr, &threadDestructor));
|
||||
static void CreateJniPtrKey() {
|
||||
LOG_FATAL_IF(pthread_key_create(&g_jni_ptr, &ThreadDestructor));
|
||||
}
|
||||
|
||||
JNIEnv* initializeJvm(JavaVM* jvm, int version) {
|
||||
JNIEnv* InitializeJvm(JavaVM* jvm, int version) {
|
||||
LOG_ASSERT(!g_jvm);
|
||||
g_jvm = jvm;
|
||||
LOG_ASSERT(g_jvm);
|
||||
|
||||
LOG_FATAL_IF(pthread_once(&g_jni_ptr_once, &createJniPtrKey));
|
||||
LOG_FATAL_IF(pthread_once(&g_jni_ptr_once, &CreateJniPtrKey));
|
||||
|
||||
void* env = nullptr;
|
||||
if (jvm->GetEnv(&env, version) != JNI_OK) {
|
||||
@ -93,173 +96,4 @@ JNIEnv* initializeJvm(JavaVM* jvm, int version) {
|
||||
return static_cast<JNIEnv*>(env);
|
||||
}
|
||||
|
||||
jclass JvmRef<jobject>::getClass(JNIEnv* env) const {
|
||||
jclass clazz = env->GetObjectClass(obj());
|
||||
LOG_FATAL_IF(checkException(env));
|
||||
return clazz;
|
||||
}
|
||||
|
||||
void JvmRef<jobject>::callVoidMethod(JNIEnv* env,
|
||||
jmethodID method_id, ...) const {
|
||||
va_list args;
|
||||
va_start(args, method_id);
|
||||
env->CallVoidMethodV(obj(), method_id, args);
|
||||
throwRuntimeErrorOnException(env);
|
||||
}
|
||||
|
||||
jint JvmRef<jobject>::callIntMethod(JNIEnv* env,
|
||||
jmethodID method_id, ...) const {
|
||||
va_list args;
|
||||
va_start(args, method_id);
|
||||
jint ret = env->CallIntMethodV(obj(), method_id, args);
|
||||
throwRuntimeErrorOnException(env);
|
||||
return ret;
|
||||
}
|
||||
|
||||
jboolean JvmRef<jobject>::callBooleanMethod(JNIEnv* env,
|
||||
jmethodID method_id, ...) const {
|
||||
va_list args;
|
||||
va_start(args, method_id);
|
||||
jboolean ret = env->CallBooleanMethodV(obj(), method_id, args);
|
||||
throwRuntimeErrorOnException(env);
|
||||
return ret;
|
||||
}
|
||||
|
||||
jobject JvmRef<jobject>::callObjectMethod(JNIEnv* env,
|
||||
jmethodID method_id, ...) const {
|
||||
va_list args;
|
||||
va_start(args, method_id);
|
||||
jobject ret = env->CallObjectMethodV(obj(), method_id, args);
|
||||
throwRuntimeErrorOnException(env);
|
||||
return ret;
|
||||
}
|
||||
|
||||
jstring JvmRef<jobject>::callStringMethod(JNIEnv* env,
|
||||
jmethodID method_id, ...) const {
|
||||
va_list args;
|
||||
va_start(args, method_id);
|
||||
auto ret = (jstring) env->CallObjectMethodV(obj(), method_id, args);
|
||||
throwRuntimeErrorOnException(env);
|
||||
return ret;
|
||||
}
|
||||
|
||||
jmethodID JvmRef<jobject>::getMethodId(JNIEnv* env,
|
||||
const char* name,
|
||||
const char* signature) const {
|
||||
jmethodID ret = env->GetMethodID(getClass(env), name, signature);
|
||||
LOG_FATAL_IF(checkException(env),
|
||||
"Error during GetMethodID: %s, %s", name, signature);
|
||||
return ret;
|
||||
}
|
||||
|
||||
jmethodID JvmRef<jobject>::getStaticMethodId(JNIEnv* env,
|
||||
const char* name,
|
||||
const char* signature) const {
|
||||
jmethodID ret = env->GetStaticMethodID(getClass(env), name, signature);
|
||||
LOG_FATAL_IF(checkException(env),
|
||||
"Error during GetStaticMethodID: %s, %s", name, signature);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void JvmRef<jclass>::callStaticVoidMethod(JNIEnv* env,
|
||||
jmethodID method_id, ...) const {
|
||||
va_list args;
|
||||
va_start(args, method_id);
|
||||
env->CallStaticVoidMethodV(obj(), method_id, args);
|
||||
}
|
||||
|
||||
jobject JvmRef<jclass>::callStaticObjectMethod(JNIEnv* env,
|
||||
jmethodID method_id, ...) const {
|
||||
va_list args;
|
||||
va_start(args, method_id);
|
||||
return env->CallStaticObjectMethodV(obj(), method_id, args);
|
||||
}
|
||||
|
||||
jobject JvmRef<jclass>::newObject(JNIEnv* env, jmethodID method_id, ...) const {
|
||||
va_list args;
|
||||
va_start(args, method_id);
|
||||
jobject new_obj = env->NewObjectV(obj(), method_id, args);
|
||||
LOG_FATAL_IF(checkException(env));
|
||||
LOG_ASSERT(new_obj);
|
||||
return new_obj;
|
||||
}
|
||||
|
||||
ScopedJvmLocalRef<jclass> findClass(JNIEnv* env, const char* name) {
|
||||
ScopedJvmLocalRef<jclass> clazz(env, env->FindClass(name));
|
||||
LOG_FATAL_IF(clazz.is_null(), "%s", name);
|
||||
return clazz;
|
||||
}
|
||||
|
||||
ScopedJvmLocalRef<jstring> nativeToJvmString(JNIEnv* env, const char* str) {
|
||||
jstring j_str = env->NewStringUTF(str);
|
||||
LOG_FATAL_IF(checkException(env));
|
||||
return {env, j_str};
|
||||
}
|
||||
|
||||
ScopedJvmLocalRef<jstring> nativeToJvmString(JNIEnv* env, const std::string& str) {
|
||||
return nativeToJvmString(env, str.c_str());
|
||||
}
|
||||
|
||||
ScopedJvmLocalRef<jbyteArray> nativeToJvmByteArray(JNIEnv* env,
|
||||
const char* bytes,
|
||||
size_t len) {
|
||||
LOG_FATAL_IF(len > INT_MAX);
|
||||
auto j_len = (jsize) len;
|
||||
jbyteArray j_array = env->NewByteArray(j_len);
|
||||
LOG_FATAL_IF(checkException(env));
|
||||
env->SetByteArrayRegion(j_array, 0, j_len,
|
||||
reinterpret_cast<const jbyte*>(bytes));
|
||||
LOG_FATAL_IF(checkException(env));
|
||||
return {env, j_array};
|
||||
}
|
||||
|
||||
ScopedJvmLocalRef<jlongArray> nativeToJvmLongArray(JNIEnv* env,
|
||||
const int64_t* longs,
|
||||
size_t len) {
|
||||
LOG_FATAL_IF(len > INT_MAX);
|
||||
auto j_len = (jsize) len;
|
||||
jlongArray j_array = env->NewLongArray(j_len);
|
||||
LOG_FATAL_IF(checkException(env));
|
||||
env->SetLongArrayRegion(j_array, 0, j_len, longs);
|
||||
LOG_FATAL_IF(checkException(env));
|
||||
return {env, j_array};
|
||||
}
|
||||
|
||||
ScopedJvmLocalRef<jlongArray> nativeToJvmLongArray(JNIEnv* env,
|
||||
const uint64_t* longs,
|
||||
size_t len) {
|
||||
return nativeToJvmLongArray(env, reinterpret_cast<const int64_t*>(longs), len);
|
||||
}
|
||||
|
||||
jlong nativeToJvmPointer(void* ptr) {
|
||||
static_assert(sizeof(intptr_t) <= sizeof(jlong), "");
|
||||
return reinterpret_cast<intptr_t>(ptr);
|
||||
}
|
||||
|
||||
std::string jvmToStdString(JNIEnv* env, const JvmRef<jstring>& j_string) {
|
||||
const char* chars = env->GetStringUTFChars(j_string.obj(), /*isCopy=*/nullptr);
|
||||
LOG_FATAL_IF(checkException(env));
|
||||
const jsize len = env->GetStringUTFLength(j_string.obj());
|
||||
LOG_FATAL_IF(checkException(env));
|
||||
std::string str(chars, len);
|
||||
env->ReleaseStringUTFChars(j_string.obj(), chars);
|
||||
LOG_FATAL_IF(checkException(env));
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string jvmToStdString(JNIEnv* env, jstring j_string) {
|
||||
return jvmToStdString(env, JvmParamRef<jstring>(j_string));
|
||||
}
|
||||
|
||||
std::vector<char> jvmToNativeByteArray(JNIEnv* env,
|
||||
const JvmRef<jbyteArray>& j_array) {
|
||||
const jsize len = env->GetArrayLength(j_array.obj());
|
||||
LOG_FATAL_IF(checkException(env));
|
||||
std::vector<char> v(len);
|
||||
env->GetByteArrayRegion(j_array.obj(), 0, len,
|
||||
reinterpret_cast<jbyte*>(&v[0]));
|
||||
LOG_FATAL_IF(checkException(env));
|
||||
return v;
|
||||
}
|
||||
|
||||
} // namespace monero
|
||||
|
@ -4,22 +4,18 @@
|
||||
#include <jni.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
namespace monero {
|
||||
|
||||
// Get a JNIEnv* usable on this thread, regardless of whether it's a native
|
||||
// thread or a Java thread. Attach the thread to the JVM if necessary.
|
||||
JNIEnv* getJniEnv();
|
||||
JNIEnv* GetJniEnv();
|
||||
|
||||
// This method should be called from JNI_OnLoad.
|
||||
JNIEnv* initializeJvm(JavaVM* jvm, int version);
|
||||
JNIEnv* InitializeJvm(JavaVM* jvm, int version);
|
||||
|
||||
// Checks for any Java exception and clears currently thrown exception.
|
||||
static bool inline checkException(JNIEnv* env, bool log_exception = true) {
|
||||
static bool inline CheckException(JNIEnv* env, bool log_exception = true) {
|
||||
if (env->ExceptionCheck()) {
|
||||
if (log_exception) env->ExceptionDescribe();
|
||||
env->ExceptionClear();
|
||||
@ -28,295 +24,12 @@ static bool inline checkException(JNIEnv* env, bool log_exception = true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static void inline throwRuntimeErrorOnException(JNIEnv* env) {
|
||||
if (checkException(env, false)) {
|
||||
static void inline ThrowRuntimeErrorOnException(JNIEnv* env) {
|
||||
if (CheckException(env, false)) {
|
||||
throw std::runtime_error("Uncaught exception returned from Java call");
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Originally these classes are from WebRTC and Chromium.
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
// Generic base class for ScopedJvmLocalRef and ScopedJvmGlobalRef.
|
||||
template<typename T>
|
||||
class JvmRef;
|
||||
|
||||
// Template specialization of JvmRef, which acts as the base class for all
|
||||
// other JvmRef<> template types. This allows you to e.g. pass JvmRef<jstring>
|
||||
// into a function taking const JvmRef<jobject>&.
|
||||
template<>
|
||||
class JvmRef<jobject> {
|
||||
public:
|
||||
virtual jobject obj() const { return m_obj; }
|
||||
bool is_null() const {
|
||||
// This is not valid for weak references. For weak references first make a
|
||||
// ScopedJvmLocalRef or ScopedJvmGlobalRef before checking if it is null.
|
||||
return m_obj == nullptr;
|
||||
}
|
||||
|
||||
virtual jclass getClass(JNIEnv* env) const;
|
||||
|
||||
jmethodID getMethodId(JNIEnv* env,
|
||||
const char* name,
|
||||
const char* signature) const;
|
||||
jmethodID getStaticMethodId(JNIEnv* env,
|
||||
const char* name,
|
||||
const char* signature) const;
|
||||
void callVoidMethod(JNIEnv* env, jmethodID method_id, ...) const;
|
||||
jint callIntMethod(JNIEnv* env, jmethodID method_id, ...) const;
|
||||
jboolean callBooleanMethod(JNIEnv* env, jmethodID method_id, ...) const;
|
||||
jobject callObjectMethod(JNIEnv* env, jmethodID method_id, ...) const;
|
||||
jstring callStringMethod(JNIEnv* env, jmethodID method_id, ...) const;
|
||||
|
||||
protected:
|
||||
constexpr JvmRef() : m_obj(nullptr) {}
|
||||
explicit JvmRef(jobject obj) : m_obj(obj) {}
|
||||
jobject m_obj;
|
||||
|
||||
private:
|
||||
JvmRef(const JvmRef&) = delete;
|
||||
JvmRef& operator=(const JvmRef&) = delete;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class JvmRef : public JvmRef<jobject> {
|
||||
public:
|
||||
T obj() const { return static_cast<T>(m_obj); }
|
||||
|
||||
protected:
|
||||
JvmRef() : JvmRef<jobject>(nullptr) {}
|
||||
explicit JvmRef(T obj) : JvmRef<jobject>(obj) {}
|
||||
|
||||
private:
|
||||
JvmRef(const JvmRef&) = delete;
|
||||
JvmRef& operator=(const JvmRef&) = delete;
|
||||
};
|
||||
|
||||
// Template specialization of JvmRef for Java's Class objects.
|
||||
template<>
|
||||
class JvmRef<jclass> : public JvmRef<jobject> {
|
||||
public:
|
||||
jclass obj() const { return static_cast<jclass>(m_obj); }
|
||||
|
||||
jclass getClass() const { return obj(); }
|
||||
|
||||
jclass getClass(JNIEnv* env) const { return getClass(); }
|
||||
|
||||
void callStaticVoidMethod(JNIEnv* env, jmethodID method_id, ...) const;
|
||||
jobject callStaticObjectMethod(JNIEnv* env, jmethodID method_id, ...) const;
|
||||
|
||||
jobject newObject(JNIEnv* env, jmethodID method_id, ...) const;
|
||||
|
||||
protected:
|
||||
constexpr JvmRef() noexcept: JvmRef<jobject>() {}
|
||||
explicit JvmRef(jclass obj) : JvmRef<jobject>(obj) {}
|
||||
|
||||
private:
|
||||
JvmRef(const JvmRef&) = delete;
|
||||
JvmRef& operator=(const JvmRef&) = delete;
|
||||
};
|
||||
|
||||
// Holds a local reference to a JNI method parameter.
|
||||
template<typename T>
|
||||
class JvmParamRef : public JvmRef<T> {
|
||||
public:
|
||||
// Assumes that `obj` is a parameter passed to a JNI method from Java.
|
||||
// Does not assume ownership as parameters should not be deleted.
|
||||
explicit JvmParamRef(T obj) : JvmRef<T>(obj) {}
|
||||
// JvmParamRef(JNIEnv*, T obj) : JvmRef<T>(obj) {}
|
||||
|
||||
private:
|
||||
JvmParamRef(const JvmParamRef&) = delete;
|
||||
JvmParamRef& operator=(const JvmParamRef&) = delete;
|
||||
};
|
||||
|
||||
// Holds a local reference to a Java object. The local reference is scoped
|
||||
// to the lifetime of this object.
|
||||
// Instances of this class may hold onto any JNIEnv passed into it until
|
||||
// destroyed. Therefore, since a JNIEnv is only suitable for use on a single
|
||||
// thread, objects of this class must be created, used, and destroyed, on a
|
||||
// single thread.
|
||||
// Therefore, this class should only be used as a stack-based object and from a
|
||||
// single thread. If you wish to have the reference outlive the current
|
||||
// callstack (e.g. as a class member) or you wish to pass it across threads,
|
||||
// use a ScopedJvmGlobalRef instead.
|
||||
template<typename T>
|
||||
class ScopedJvmLocalRef : public JvmRef<T> {
|
||||
public:
|
||||
constexpr ScopedJvmLocalRef() : m_env(nullptr) {}
|
||||
explicit constexpr ScopedJvmLocalRef(std::nullptr_t) : m_env(nullptr) {}
|
||||
|
||||
ScopedJvmLocalRef(JNIEnv* env, const JvmRef<T>& other) : m_env(env) {
|
||||
Reset(other.obj(), OwnershipPolicy::RETAIN);
|
||||
}
|
||||
// Allow constructing e.g. ScopedJvmLocalRef<jobject> from
|
||||
// ScopedJvmLocalRef<jstring>.
|
||||
template<typename G>
|
||||
ScopedJvmLocalRef(ScopedJvmLocalRef<G>&& other) : m_env(other.m_env) {
|
||||
Reset(other.Release(), OwnershipPolicy::ADOPT);
|
||||
}
|
||||
ScopedJvmLocalRef(const ScopedJvmLocalRef& other) : m_env(other.m_env) {
|
||||
Reset(other.obj(), OwnershipPolicy::RETAIN);
|
||||
}
|
||||
|
||||
// Assumes that `obj` is a reference to a Java object and takes
|
||||
// ownership of this reference. This should preferably not be used
|
||||
// outside of JNI helper functions.
|
||||
ScopedJvmLocalRef(JNIEnv* env, T obj) : JvmRef<T>(obj), m_env(env) {}
|
||||
|
||||
~ScopedJvmLocalRef() {
|
||||
if (m_obj != nullptr)
|
||||
m_env->DeleteLocalRef(m_obj);
|
||||
}
|
||||
|
||||
void operator=(const ScopedJvmLocalRef& other) {
|
||||
Reset(other.obj(), OwnershipPolicy::RETAIN);
|
||||
}
|
||||
void operator=(ScopedJvmLocalRef&& other) {
|
||||
Reset(other.Release(), OwnershipPolicy::ADOPT);
|
||||
}
|
||||
|
||||
// Releases the reference to the caller. The caller *must* delete the
|
||||
// reference when it is done with it. Note that calling a Java method
|
||||
// is *not* a transfer of ownership and Release() should not be used.
|
||||
T Release() {
|
||||
T obj = static_cast<T>(m_obj);
|
||||
m_obj = nullptr;
|
||||
return obj;
|
||||
}
|
||||
|
||||
private:
|
||||
using JvmRef<T>::m_obj;
|
||||
|
||||
enum OwnershipPolicy {
|
||||
// The scoped object takes ownership of an object by taking over an existing
|
||||
// ownership claim.
|
||||
ADOPT,
|
||||
// The scoped object will retain the the object and any initial ownership is
|
||||
// not changed.
|
||||
RETAIN
|
||||
};
|
||||
|
||||
void Reset(T obj, OwnershipPolicy policy) {
|
||||
if (m_obj != nullptr)
|
||||
m_env->DeleteLocalRef(m_obj);
|
||||
m_obj = (obj != nullptr && policy == OwnershipPolicy::RETAIN)
|
||||
? m_env->NewLocalRef(obj)
|
||||
: obj;
|
||||
}
|
||||
|
||||
// This class is only good for use on the thread it was created on so
|
||||
// it's safe to cache the non-threadsafe JNIEnv* inside this object.
|
||||
JNIEnv* const m_env;
|
||||
};
|
||||
|
||||
// Holds a global reference to a Java object. The global reference is scoped
|
||||
// to the lifetime of this object. This class does not hold onto any JNIEnv*
|
||||
// passed to it, hence it is safe to use across threads (within the constraints
|
||||
// imposed by the underlying Java object that it references).
|
||||
template<typename T>
|
||||
class ScopedJvmGlobalRef : public JvmRef<T> {
|
||||
public:
|
||||
using JvmRef<T>::m_obj;
|
||||
|
||||
ScopedJvmGlobalRef() = default;
|
||||
explicit constexpr ScopedJvmGlobalRef(std::nullptr_t) {}
|
||||
ScopedJvmGlobalRef(JNIEnv* env, const JvmRef<T>& other)
|
||||
: JvmRef<T>(static_cast<T>(env->NewGlobalRef(other.obj()))) {}
|
||||
explicit ScopedJvmGlobalRef(const ScopedJvmLocalRef<T>& other)
|
||||
: ScopedJvmGlobalRef(other.env(), other) {}
|
||||
ScopedJvmGlobalRef(ScopedJvmGlobalRef&& other)
|
||||
: JvmRef<T>(other.Release()) {}
|
||||
|
||||
~ScopedJvmGlobalRef() {
|
||||
if (m_obj != nullptr)
|
||||
getJniEnv()->DeleteGlobalRef(m_obj);
|
||||
}
|
||||
|
||||
void operator=(const JvmRef<T>& other) {
|
||||
JNIEnv* env = getJniEnv();
|
||||
if (m_obj != nullptr) {
|
||||
env->DeleteGlobalRef(m_obj);
|
||||
}
|
||||
m_obj = other.is_null() ? nullptr : env->NewGlobalRef(other.obj());
|
||||
}
|
||||
|
||||
void operator=(std::nullptr_t) {
|
||||
if (m_obj != nullptr) {
|
||||
getJniEnv()->DeleteGlobalRef(m_obj);
|
||||
}
|
||||
m_obj = nullptr;
|
||||
}
|
||||
|
||||
// Releases the reference to the caller. The caller *must* delete the
|
||||
// reference when it is done with it. Note that calling a Java method
|
||||
// is *not* a transfer of ownership and Release() should not be used.
|
||||
T Release() {
|
||||
T obj = static_cast<T>(m_obj);
|
||||
m_obj = nullptr;
|
||||
return obj;
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedJvmGlobalRef(const ScopedJvmGlobalRef&) = delete;
|
||||
ScopedJvmGlobalRef& operator=(const ScopedJvmGlobalRef&) = delete;
|
||||
};
|
||||
|
||||
// Returns a class local reference from a fully-qualified name.
|
||||
// This method must be called on a context capable of loading Java classes.
|
||||
ScopedJvmLocalRef<jclass> findClass(JNIEnv* env, const char* name);
|
||||
|
||||
// Methods for converting native types to Java types.
|
||||
ScopedJvmLocalRef<jstring> nativeToJvmString(JNIEnv* env, const char* str);
|
||||
ScopedJvmLocalRef<jstring> nativeToJvmString(JNIEnv* env, const std::string& str);
|
||||
ScopedJvmLocalRef<jbyteArray> nativeToJvmByteArray(JNIEnv* env, const char* bytes, size_t len);
|
||||
ScopedJvmLocalRef<jlongArray> nativeToJvmLongArray(JNIEnv* env, const int64_t* longs, size_t len);
|
||||
ScopedJvmLocalRef<jlongArray> nativeToJvmLongArray(JNIEnv* env, const uint64_t* longs, size_t len);
|
||||
jlong nativeToJvmPointer(void* ptr);
|
||||
|
||||
// Helper function for converting std::vector<T> into a Java array.
|
||||
template<typename T, typename Convert>
|
||||
ScopedJvmLocalRef<jobjectArray> nativeToJvmObjectArray(
|
||||
JNIEnv* env,
|
||||
const std::vector<T>& container,
|
||||
jclass clazz,
|
||||
Convert convert) {
|
||||
ScopedJvmLocalRef<jobjectArray> j_container(
|
||||
env, env->NewObjectArray(container.size(), clazz, nullptr));
|
||||
int i = 0;
|
||||
for (const T& element: container) {
|
||||
env->SetObjectArrayElement(j_container.obj(), i,
|
||||
convert(env, element).obj());
|
||||
++i;
|
||||
}
|
||||
return j_container;
|
||||
}
|
||||
|
||||
template<typename T, typename Convert>
|
||||
std::vector<T> jvmToNativeVector(
|
||||
JNIEnv* env,
|
||||
const JvmRef<jobjectArray>& j_container,
|
||||
Convert convert) {
|
||||
std::vector<T> container;
|
||||
const jsize size = env->GetArrayLength(j_container.obj());
|
||||
container.reserve(size);
|
||||
for (jsize i = 0; i < size; ++i) {
|
||||
container.emplace_back(convert(
|
||||
env, ScopedJvmLocalRef<jobject>(
|
||||
env, env->GetObjectArrayElement(j_container.obj(), i))));
|
||||
}
|
||||
LOG_FATAL_IF(checkException(env));
|
||||
return container;
|
||||
}
|
||||
|
||||
// Converts a Java string to a std string in modified UTF-8 encoding
|
||||
std::string jvmToStdString(JNIEnv* env, const JvmRef<jstring>& j_string);
|
||||
std::string jvmToStdString(JNIEnv* env, jstring j_string);
|
||||
|
||||
std::vector<char> jvmToNativeByteArray(JNIEnv* env,
|
||||
const JvmRef<jbyteArray>& j_array);
|
||||
|
||||
} // namespace monero
|
||||
|
||||
#endif // COMMON_JVM_H_
|
||||
|
221
lib/android/src/main/cpp/common/scoped_java_ref.h
Normal file
221
lib/android/src/main/cpp/common/scoped_java_ref.h
Normal file
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright 2017 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// Originally these classes are from Chromium.
|
||||
// https://cs.chromium.org/chromium/src/base/android/scoped_java_ref.h.
|
||||
|
||||
#ifndef COMMON_SCOPED_JAVA_REF_H_
|
||||
#define COMMON_SCOPED_JAVA_REF_H_
|
||||
|
||||
#include "common/jvm.h"
|
||||
|
||||
namespace monero {
|
||||
|
||||
// Generic base class for ScopedJavaLocalRef and ScopedJavaGlobalRef. Useful
|
||||
// for allowing functions to accept a reference without having to mandate
|
||||
// whether it is a local or global type.
|
||||
template<typename T>
|
||||
class JavaRef;
|
||||
|
||||
// Template specialization of JavaRef, which acts as the base class for all
|
||||
// other JavaRef<> template types. This allows you to e.g. pass JavaRef<jstring>
|
||||
// into a function taking const JavaRef<jobject>&.
|
||||
template<>
|
||||
class JavaRef<jobject> {
|
||||
public:
|
||||
JavaRef(const JavaRef&) = delete;
|
||||
JavaRef& operator=(const JavaRef&) = delete;
|
||||
|
||||
// void callVoidMethod(JNIEnv* env, jmethodID method_id, ...) const;
|
||||
// jint callIntMethod(JNIEnv* env, jmethodID method_id, ...) const;
|
||||
// jboolean callBooleanMethod(JNIEnv* env, jmethodID method_id, ...) const;
|
||||
// jobject callObjectMethod(JNIEnv* env, jmethodID method_id, ...) const;
|
||||
// jstring callStringMethod(JNIEnv* env, jmethodID method_id, ...) const;
|
||||
|
||||
jobject obj() const { return m_obj; }
|
||||
bool is_null() const {
|
||||
// This is not valid for weak references. For weak references you need to
|
||||
// use env->IsSameObject(objc_, nullptr), but that should be avoided anyway
|
||||
// since it does not prevent the object from being freed immediately
|
||||
// thereafter. Consequently, programmers should not use this check on weak
|
||||
// references anyway and should first make a ScopedJavaLocalRef or
|
||||
// ScopedJavaGlobalRef before checking if it is null.
|
||||
return m_obj == nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
constexpr JavaRef() : m_obj(nullptr) {}
|
||||
explicit JavaRef(jobject obj) : m_obj(obj) {}
|
||||
jobject m_obj;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class JavaRef : public JavaRef<jobject> {
|
||||
public:
|
||||
JavaRef(const JavaRef&) = delete;
|
||||
JavaRef& operator=(const JavaRef&) = delete;
|
||||
|
||||
T obj() const { return static_cast<T>(m_obj); }
|
||||
|
||||
protected:
|
||||
JavaRef() : JavaRef<jobject>(nullptr) {}
|
||||
explicit JavaRef(T obj) : JavaRef<jobject>(obj) {}
|
||||
};
|
||||
|
||||
// Holds a local reference to a JNI method parameter.
|
||||
// Method parameters should not be deleted, and so this class exists purely to
|
||||
// wrap them as a JavaRef<T> in the JNI binding generator. Do not create
|
||||
// instances manually.
|
||||
template<typename T>
|
||||
class JavaParamRef : public JavaRef<T> {
|
||||
public:
|
||||
// Assumes that `obj` is a parameter passed to a JNI method from Java.
|
||||
// Does not assume ownership as parameters should not be deleted.
|
||||
explicit JavaParamRef(T obj) : JavaRef<T>(obj) {}
|
||||
JavaParamRef(JNIEnv*, T obj) : JavaRef<T>(obj) {}
|
||||
|
||||
JavaParamRef(const JavaParamRef&) = delete;
|
||||
JavaParamRef& operator=(const JavaParamRef&) = delete;
|
||||
};
|
||||
|
||||
// Holds a local reference to a Java object. The local reference is scoped
|
||||
// to the lifetime of this object.
|
||||
// Instances of this class may hold onto any JNIEnv passed into it until
|
||||
// destroyed. Therefore, since a JNIEnv is only suitable for use on a single
|
||||
// thread, objects of this class must be created, used, and destroyed, on a
|
||||
// single thread.
|
||||
// Therefore, this class should only be used as a stack-based object and from a
|
||||
// single thread. If you wish to have the reference outlive the current
|
||||
// callstack (e.g. as a class member) or you wish to pass it across threads,
|
||||
// use a ScopedJavaGlobalRef instead.
|
||||
template<typename T>
|
||||
class ScopedJavaLocalRef : public JavaRef<T> {
|
||||
public:
|
||||
ScopedJavaLocalRef() = default;
|
||||
ScopedJavaLocalRef(std::nullptr_t) {}
|
||||
|
||||
ScopedJavaLocalRef(JNIEnv* env, const JavaRef<T>& other) : m_env(env) {
|
||||
reset(other.obj(), OwnershipPolicy::RETAIN);
|
||||
}
|
||||
// Allow constructing e.g. ScopedJavaLocalRef<jobject> from
|
||||
// ScopedJavaLocalRef<jstring>.
|
||||
template<typename G>
|
||||
ScopedJavaLocalRef(ScopedJavaLocalRef<G>&& other) : m_env(other.env()) {
|
||||
reset(other.release(), OwnershipPolicy::ADOPT);
|
||||
}
|
||||
ScopedJavaLocalRef(const ScopedJavaLocalRef& other) : m_env(other.m_env) {
|
||||
reset(other.obj(), OwnershipPolicy::RETAIN);
|
||||
}
|
||||
|
||||
// Assumes that `obj` is a reference to a Java object and takes
|
||||
// ownership of this reference. This should preferably not be used
|
||||
// outside of JNI helper functions.
|
||||
ScopedJavaLocalRef(JNIEnv* env, T obj) : JavaRef<T>(obj), m_env(env) {}
|
||||
|
||||
~ScopedJavaLocalRef() {
|
||||
if (m_obj != nullptr)
|
||||
m_env->DeleteLocalRef(m_obj);
|
||||
}
|
||||
|
||||
void operator=(const ScopedJavaLocalRef& other) {
|
||||
reset(other.obj(), OwnershipPolicy::RETAIN);
|
||||
}
|
||||
void operator=(ScopedJavaLocalRef&& other) {
|
||||
reset(other.release(), OwnershipPolicy::ADOPT);
|
||||
}
|
||||
|
||||
// Releases the reference to the caller. The caller *must* delete the
|
||||
// reference when it is done with it. Note that calling a Java method
|
||||
// is *not* a transfer of ownership and release() should not be used.
|
||||
T release() {
|
||||
T obj = static_cast<T>(m_obj);
|
||||
m_obj = nullptr;
|
||||
return obj;
|
||||
}
|
||||
|
||||
JNIEnv* env() const { return m_env; }
|
||||
|
||||
private:
|
||||
using JavaRef<T>::m_obj;
|
||||
|
||||
enum OwnershipPolicy {
|
||||
// The scoped object takes ownership of an object by taking over an existing
|
||||
// ownership claim.
|
||||
ADOPT,
|
||||
// The scoped object will retain the the object and any initial ownership is
|
||||
// not changed.
|
||||
RETAIN
|
||||
};
|
||||
|
||||
void reset(T obj, OwnershipPolicy policy) {
|
||||
if (m_obj != nullptr)
|
||||
m_env->DeleteLocalRef(m_obj);
|
||||
m_obj = (obj != nullptr && policy == OwnershipPolicy::RETAIN)
|
||||
? m_env->NewLocalRef(obj)
|
||||
: obj;
|
||||
}
|
||||
|
||||
JNIEnv* const m_env = GetJniEnv();
|
||||
};
|
||||
|
||||
// Holds a global reference to a Java object. The global reference is scoped
|
||||
// to the lifetime of this object. This class does not hold onto any JNIEnv*
|
||||
// passed to it, hence it is safe to use across threads (within the constraints
|
||||
// imposed by the underlying Java object that it references).
|
||||
template<typename T>
|
||||
class ScopedJavaGlobalRef : public JavaRef<T> {
|
||||
public:
|
||||
using JavaRef<T>::m_obj;
|
||||
|
||||
ScopedJavaGlobalRef() = default;
|
||||
explicit constexpr ScopedJavaGlobalRef(std::nullptr_t) {}
|
||||
ScopedJavaGlobalRef(JNIEnv* env, const JavaRef<T>& other)
|
||||
: JavaRef<T>(static_cast<T>(env->NewGlobalRef(other.obj()))) {}
|
||||
explicit ScopedJavaGlobalRef(const ScopedJavaLocalRef<T>& other)
|
||||
: ScopedJavaGlobalRef(other.env(), other) {}
|
||||
ScopedJavaGlobalRef(ScopedJavaGlobalRef&& other)
|
||||
: JavaRef<T>(other.release()) {}
|
||||
|
||||
~ScopedJavaGlobalRef() {
|
||||
if (m_obj != nullptr)
|
||||
GetJniEnv()->DeleteGlobalRef(m_obj);
|
||||
}
|
||||
|
||||
ScopedJavaGlobalRef(const ScopedJavaGlobalRef&) = delete;
|
||||
ScopedJavaGlobalRef& operator=(const ScopedJavaGlobalRef&) = delete;
|
||||
|
||||
void operator=(const JavaRef<T>& other) {
|
||||
JNIEnv* env = GetJniEnv();
|
||||
if (m_obj != nullptr) {
|
||||
env->DeleteGlobalRef(m_obj);
|
||||
}
|
||||
m_obj = other.is_null() ? nullptr : env->NewGlobalRef(other.obj());
|
||||
}
|
||||
|
||||
void operator=(std::nullptr_t) {
|
||||
if (m_obj != nullptr) {
|
||||
GetJniEnv()->DeleteGlobalRef(m_obj);
|
||||
}
|
||||
m_obj = nullptr;
|
||||
}
|
||||
|
||||
// Releases the reference to the caller. The caller *must* delete the
|
||||
// reference when it is done with it. Note that calling a Java method
|
||||
// is *not* a transfer of ownership and release() should not be used.
|
||||
T release() {
|
||||
T obj = static_cast<T>(m_obj);
|
||||
m_obj = nullptr;
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace monero
|
||||
|
||||
#endif // COMMON_SCOPED_JAVA_REF_H_
|
@ -3,19 +3,18 @@
|
||||
namespace monero {
|
||||
|
||||
// im.molly.monero.mnemonics
|
||||
ScopedJvmGlobalRef<jclass> MoneroMnemonicClass;
|
||||
jmethodID MoneroMnemonic_buildMnemonicFromNative;
|
||||
jmethodID MoneroMnemonic_buildMnemonicFromJNI;
|
||||
ScopedJavaGlobalRef<jclass> MoneroMnemonicClass;
|
||||
|
||||
void initializeJniCache(JNIEnv* env) {
|
||||
// im.molly.monero.mnemonics
|
||||
auto moneroMnemonicClass = findClass(env, "im/molly/monero/mnemonics/MoneroMnemonic");
|
||||
void InitializeJniCache(JNIEnv* env) {
|
||||
jclass moneroMnemonic = GetClass(env, "im/molly/monero/mnemonics/MoneroMnemonic");
|
||||
|
||||
MoneroMnemonic_buildMnemonicFromNative = moneroMnemonicClass
|
||||
.getStaticMethodId(env,
|
||||
"buildMnemonicFromNative",
|
||||
"([B[BLjava/lang/String;)Lim/molly/monero/mnemonics/MnemonicCode;");
|
||||
MoneroMnemonic_buildMnemonicFromJNI = GetStaticMethodId(
|
||||
env, moneroMnemonic,
|
||||
"buildMnemonicFromJNI",
|
||||
"([B[BLjava/lang/String;)Lim/molly/monero/mnemonics/MnemonicCode;");
|
||||
|
||||
MoneroMnemonicClass = moneroMnemonicClass;
|
||||
MoneroMnemonicClass = ScopedJavaLocalRef<jclass>(env, moneroMnemonic);
|
||||
}
|
||||
|
||||
} // namespace monero
|
||||
|
@ -1,16 +1,16 @@
|
||||
#ifndef MNEMONICS_JNI_CACHE_H__
|
||||
#define MNEMONICS_JNI_CACHE_H__
|
||||
|
||||
#include "common/jvm.h"
|
||||
#include "common/java_native.h"
|
||||
|
||||
namespace monero {
|
||||
|
||||
// Initialize various classes and method pointers cached for use in JNI.
|
||||
void initializeJniCache(JNIEnv* env);
|
||||
void InitializeJniCache(JNIEnv* env);
|
||||
|
||||
// im.molly.monero.mnemonics
|
||||
extern ScopedJvmGlobalRef<jclass> MoneroMnemonicClass;
|
||||
extern jmethodID MoneroMnemonic_buildMnemonicFromNative;
|
||||
extern jmethodID MoneroMnemonic_buildMnemonicFromJNI;
|
||||
extern ScopedJavaGlobalRef<jclass> MoneroMnemonicClass;
|
||||
|
||||
} // namespace monero
|
||||
|
||||
|
@ -7,12 +7,12 @@ namespace monero {
|
||||
extern "C"
|
||||
JNIEXPORT jint
|
||||
JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
JNIEnv* env = initializeJvm(vm, JNI_VERSION_1_6);
|
||||
JNIEnv* env = InitializeJvm(vm, JNI_VERSION_1_6);
|
||||
if (env == nullptr) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
initializeJniCache(env);
|
||||
InitializeJniCache(env);
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
@ -13,31 +13,27 @@ Java_im_molly_monero_mnemonics_MoneroMnemonicKt_nativeElectrumWordsGenerateMnemo
|
||||
jclass clazz,
|
||||
jbyteArray j_entropy,
|
||||
jstring j_language) {
|
||||
std::vector<char> entropy = jvmToNativeByteArray(env, JvmParamRef<jbyteArray>(j_entropy));
|
||||
std::vector<char> entropy = JavaToNativeByteArray(env, j_entropy);
|
||||
Eraser entropy_eraser(entropy);
|
||||
|
||||
std::string language = jvmToStdString(env, j_language);
|
||||
epee::wipeable_string words;
|
||||
std::string language = JavaToNativeString(env, j_language);
|
||||
|
||||
epee::wipeable_string words;
|
||||
bool success =
|
||||
crypto::ElectrumWords::bytes_to_words(entropy.data(),
|
||||
entropy.size(),
|
||||
words,
|
||||
language);
|
||||
crypto::ElectrumWords::bytes_to_words(entropy.data(), entropy.size(),
|
||||
words, language);
|
||||
if (!success) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ScopedJvmLocalRef<jobject> j_mnemonic_code(
|
||||
env, MoneroMnemonicClass.callStaticObjectMethod(
|
||||
env, MoneroMnemonic_buildMnemonicFromNative,
|
||||
j_entropy,
|
||||
nativeToJvmByteArray(env, words.data(), words.size()).obj(),
|
||||
j_language
|
||||
)
|
||||
);
|
||||
jobject j_mnemonic_code = CallStaticObjectMethod(
|
||||
env, MoneroMnemonicClass.obj(),
|
||||
MoneroMnemonic_buildMnemonicFromJNI,
|
||||
j_entropy,
|
||||
NativeToJavaByteArray(env, words.data(), words.size()),
|
||||
j_language);
|
||||
|
||||
return j_mnemonic_code.Release();
|
||||
return j_mnemonic_code;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
@ -47,32 +43,29 @@ Java_im_molly_monero_mnemonics_MoneroMnemonicKt_nativeElectrumWordsRecoverEntrop
|
||||
jclass clazz,
|
||||
jbyteArray j_source
|
||||
) {
|
||||
std::vector<char> words = jvmToNativeByteArray(env, JvmParamRef<jbyteArray>(j_source));
|
||||
std::vector<char> words = JavaToNativeByteArray(env, j_source);
|
||||
Eraser words_eraser(words);
|
||||
|
||||
std::string language;
|
||||
epee::wipeable_string entropy, w_words(words.data(), words.size());
|
||||
|
||||
std::string language;
|
||||
bool success =
|
||||
crypto::ElectrumWords::words_to_bytes(w_words,
|
||||
entropy,
|
||||
0 /* len */,
|
||||
true /* duplicate */,
|
||||
0, /* len */
|
||||
true, /* duplicate */
|
||||
language);
|
||||
if (!success) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ScopedJvmLocalRef<jobject> j_mnemonic_code(
|
||||
env, MoneroMnemonicClass.callStaticObjectMethod(
|
||||
env, MoneroMnemonic_buildMnemonicFromNative,
|
||||
nativeToJvmByteArray(env, entropy.data(), entropy.size()).obj(),
|
||||
j_source,
|
||||
nativeToJvmString(env, language).obj()
|
||||
)
|
||||
);
|
||||
jobject j_mnemonic_code = CallStaticObjectMethod(
|
||||
env, MoneroMnemonicClass.obj(),
|
||||
MoneroMnemonic_buildMnemonicFromJNI,
|
||||
NativeToJavaByteArray(env, entropy.data(), entropy.size()),
|
||||
j_source,
|
||||
NativeToJavaString(env, language));
|
||||
|
||||
return j_mnemonic_code.Release();
|
||||
return j_mnemonic_code;
|
||||
}
|
||||
|
||||
} // namespace monero
|
||||
|
@ -26,9 +26,12 @@ class ScopedFd {
|
||||
other.m_fd = -1;
|
||||
}
|
||||
|
||||
ScopedFd(JNIEnv* env, const JvmRef<jobject>& parcel_file_descriptor) : m_fd(-1) {
|
||||
ScopedFd(JNIEnv* env, const JavaRef<jobject>& parcel_file_descriptor) {
|
||||
if (!parcel_file_descriptor.is_null()) {
|
||||
m_fd = parcel_file_descriptor.callIntMethod(env, ParcelFileDescriptor_detachFd);
|
||||
m_fd = CallIntMethod(env, parcel_file_descriptor.obj(),
|
||||
ParcelFd_detachFd);
|
||||
} else {
|
||||
m_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "http_client.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
#include "jni_cache.h"
|
||||
|
||||
namespace monero {
|
||||
@ -32,15 +34,15 @@ bool RemoteNodeClient::is_connected(bool* ssl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RemoteNodeClient::HttpResponse jvmToHttpResponse(JNIEnv* env, JvmRef<jobject>& j_http_response) {
|
||||
jint code = j_http_response.callIntMethod(env, HttpResponse_getCode);
|
||||
jstring mime_type = j_http_response.callStringMethod(env, HttpResponse_getContentType);
|
||||
jobject body = j_http_response.callObjectMethod(env, HttpResponse_getBody);
|
||||
return {
|
||||
code,
|
||||
(mime_type != nullptr) ? jvmToStdString(env, mime_type) : "",
|
||||
ScopedFd(env, ScopedJvmLocalRef<jobject>(env, body))
|
||||
};
|
||||
RemoteNodeClient::HttpResponse JavaToHttpResponse(JNIEnv* env, jobject obj) {
|
||||
jint code = CallIntMethod(env, obj, HttpResponse_getCode);
|
||||
ScopedJavaLocalRef<jstring>
|
||||
j_mime_type(env, CallStringMethod(env, obj, HttpResponse_getContentType));
|
||||
ScopedJavaLocalRef<jobject>
|
||||
j_body(env, CallObjectMethod(env, obj, HttpResponse_getBody));
|
||||
return {code, j_mime_type.is_null() ? ""
|
||||
: JavaToNativeString(env, j_mime_type.obj()),
|
||||
ScopedFd(env, j_body)};
|
||||
}
|
||||
|
||||
bool RemoteNodeClient::invoke(const boost::string_ref uri,
|
||||
@ -49,26 +51,30 @@ bool RemoteNodeClient::invoke(const boost::string_ref uri,
|
||||
std::chrono::milliseconds timeout,
|
||||
const epee::net_utils::http::http_response_info** ppresponse_info,
|
||||
const epee::net_utils::http::fields_list& additional_params) {
|
||||
JNIEnv* env = getJniEnv();
|
||||
std::ostringstream header;
|
||||
for (const auto& p: additional_params) {
|
||||
header << p.first << ": " << p.second << "\r\n";
|
||||
}
|
||||
JNIEnv* env = GetJniEnv();
|
||||
try {
|
||||
ScopedJvmLocalRef<jobject> j_response(
|
||||
env, m_wallet_native.callObjectMethod(
|
||||
env, WalletNative_callRemoteNode,
|
||||
nativeToJvmString(env, method.data()).obj(),
|
||||
nativeToJvmString(env, uri.data()).obj(),
|
||||
nativeToJvmString(env, header.str()).obj(),
|
||||
nativeToJvmByteArray(env, body.data(), body.length()).obj()
|
||||
)
|
||||
);
|
||||
ScopedJavaLocalRef<jstring> j_method(env, NativeToJavaString(env, method.data()));
|
||||
ScopedJavaLocalRef<jstring> j_uri(env, NativeToJavaString(env, uri.data()));
|
||||
ScopedJavaLocalRef<jstring> j_hdr(env, NativeToJavaString(env, header.str()));
|
||||
ScopedJavaLocalRef<jbyteArray>
|
||||
j_body(env, NativeToJavaByteArray(env, body.data(), body.length()));
|
||||
ScopedJavaLocalRef<jobject>
|
||||
j_response = {env, CallObjectMethod(env,
|
||||
m_wallet_native.obj(),
|
||||
WalletNative_callRemoteNode,
|
||||
j_method.obj(),
|
||||
j_uri.obj(),
|
||||
j_hdr.obj(),
|
||||
j_body.obj())};
|
||||
m_response_info.clear();
|
||||
if (j_response.is_null()) {
|
||||
return false;
|
||||
}
|
||||
HttpResponse http_response = jvmToHttpResponse(env, j_response);
|
||||
HttpResponse http_response = JavaToHttpResponse(env, j_response.obj());
|
||||
if (http_response.code == 401) {
|
||||
// Handle HTTP unauthorized in the same way as http_simple_client_template.
|
||||
return false;
|
||||
|
@ -13,10 +13,8 @@ using AbstractHttpClient = epee::net_utils::http::abstract_http_client;
|
||||
|
||||
class RemoteNodeClient : public AbstractHttpClient {
|
||||
public:
|
||||
RemoteNodeClient(
|
||||
JNIEnv* env,
|
||||
const JvmRef<jobject>& wallet_native)
|
||||
: m_wallet_native(env, wallet_native) {}
|
||||
RemoteNodeClient(JNIEnv* env, const JavaRef<jobject>& wallet_native) :
|
||||
m_wallet_native(env, wallet_native) {}
|
||||
|
||||
bool set_proxy(const std::string& address) override;
|
||||
void set_server(std::string host,
|
||||
@ -54,7 +52,7 @@ class RemoteNodeClient : public AbstractHttpClient {
|
||||
};
|
||||
|
||||
private:
|
||||
const ScopedJvmGlobalRef<jobject> m_wallet_native;
|
||||
const ScopedJavaGlobalRef<jobject> m_wallet_native;
|
||||
epee::net_utils::http::http_response_info m_response_info;
|
||||
};
|
||||
|
||||
@ -62,18 +60,16 @@ using HttpClientFactory = epee::net_utils::http::http_client_factory;
|
||||
|
||||
class RemoteNodeClientFactory : public HttpClientFactory {
|
||||
public:
|
||||
RemoteNodeClientFactory(
|
||||
JNIEnv* env,
|
||||
const JvmRef<jobject>& wallet_native)
|
||||
: m_wallet_native(env, wallet_native) {}
|
||||
RemoteNodeClientFactory(JNIEnv* env, const JavaRef<jobject>& wallet_native) :
|
||||
m_wallet_native(env, wallet_native) {}
|
||||
|
||||
std::unique_ptr<AbstractHttpClient> create() override {
|
||||
return std::unique_ptr<AbstractHttpClient>(new RemoteNodeClient(getJniEnv(),
|
||||
m_wallet_native));
|
||||
return std::unique_ptr<AbstractHttpClient>(
|
||||
new RemoteNodeClient(GetJniEnv(), m_wallet_native));
|
||||
}
|
||||
|
||||
private:
|
||||
const ScopedJvmGlobalRef<jobject> m_wallet_native;
|
||||
const ScopedJavaGlobalRef<jobject> m_wallet_native;
|
||||
};
|
||||
|
||||
} // namespace monero
|
||||
|
@ -3,54 +3,71 @@
|
||||
namespace monero {
|
||||
|
||||
// im.molly.monero
|
||||
ScopedJvmGlobalRef<jclass> TxInfoClass;
|
||||
jmethodID HttpResponse_getBody;
|
||||
jmethodID HttpResponse_getCode;
|
||||
jmethodID HttpResponse_getContentType;
|
||||
jmethodID ITransferRequestCb_onTransferCreated;
|
||||
jmethodID Logger_logFromNative;
|
||||
jmethodID TxInfo_ctor;
|
||||
jmethodID WalletNative_createPendingTransfer;
|
||||
jmethodID WalletNative_callRemoteNode;
|
||||
jmethodID WalletNative_onRefresh;
|
||||
jmethodID WalletNative_onSuspendRefresh;
|
||||
ScopedJavaGlobalRef<jclass> TxInfoClass;
|
||||
|
||||
// android.os
|
||||
jmethodID ParcelFileDescriptor_detachFd;
|
||||
jmethodID ParcelFd_detachFd;
|
||||
|
||||
void initializeJniCache(JNIEnv* env) {
|
||||
// im.molly.monero
|
||||
auto httpResponse = findClass(env, "im/molly/monero/HttpResponse");
|
||||
auto logger = findClass(env, "im/molly/monero/Logger");
|
||||
auto txInfoClass = findClass(env, "im/molly/monero/internal/TxInfo");
|
||||
auto walletNative = findClass(env, "im/molly/monero/WalletNative");
|
||||
// java.lang
|
||||
ScopedJavaGlobalRef<jclass> StringClass;
|
||||
|
||||
TxInfoClass = txInfoClass;
|
||||
void InitializeJniCache(JNIEnv* env) {
|
||||
jclass httpResponse = GetClass(env, "im/molly/monero/HttpResponse");
|
||||
jclass iTransferRequestCb = GetClass(env, "im/molly/monero/ITransferRequestCallback");
|
||||
jclass logger = GetClass(env, "im/molly/monero/Logger");
|
||||
jclass txInfo = GetClass(env, "im/molly/monero/internal/TxInfo");
|
||||
jclass walletNative = GetClass(env, "im/molly/monero/WalletNative");
|
||||
jclass parcelFd = GetClass(env, "android/os/ParcelFileDescriptor");
|
||||
|
||||
HttpResponse_getBody = httpResponse
|
||||
.getMethodId(env, "getBody", "()Landroid/os/ParcelFileDescriptor;");
|
||||
HttpResponse_getCode = httpResponse
|
||||
.getMethodId(env, "getCode", "()I");
|
||||
HttpResponse_getContentType = httpResponse
|
||||
.getMethodId(env, "getContentType", "()Ljava/lang/String;");
|
||||
Logger_logFromNative = logger
|
||||
.getMethodId(env, "logFromNative", "(ILjava/lang/String;Ljava/lang/String;)V");
|
||||
TxInfo_ctor = txInfoClass
|
||||
.getMethodId(env,
|
||||
"<init>",
|
||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IILjava/lang/String;JIIJJJJZZ)V");
|
||||
WalletNative_callRemoteNode = walletNative
|
||||
.getMethodId(env,
|
||||
"callRemoteNode",
|
||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[B)Lim/molly/monero/HttpResponse;");
|
||||
WalletNative_onRefresh = walletNative
|
||||
.getMethodId(env, "onRefresh", "(IJZ)V");
|
||||
WalletNative_onSuspendRefresh = walletNative
|
||||
.getMethodId(env, "onSuspendRefresh", "(Z)V");
|
||||
HttpResponse_getBody = GetMethodId(
|
||||
env, httpResponse,
|
||||
"getBody", "()Landroid/os/ParcelFileDescriptor;");
|
||||
HttpResponse_getCode = GetMethodId(
|
||||
env, httpResponse,
|
||||
"getCode", "()I");
|
||||
HttpResponse_getContentType = GetMethodId(
|
||||
env, httpResponse,
|
||||
"getContentType", "()Ljava/lang/String;");
|
||||
ITransferRequestCb_onTransferCreated = GetMethodId(
|
||||
env, iTransferRequestCb,
|
||||
"onTransferCreated", "(Lim/molly/monero/IPendingTransfer;)V");
|
||||
Logger_logFromNative = GetMethodId(
|
||||
env, logger,
|
||||
"logFromNative", "(ILjava/lang/String;Ljava/lang/String;)V");
|
||||
TxInfo_ctor = GetMethodId(
|
||||
env, txInfo,
|
||||
"<init>",
|
||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IILjava/lang/String;JIIJJJJZZ)V");
|
||||
WalletNative_createPendingTransfer = GetMethodId(
|
||||
env, walletNative,
|
||||
"createPendingTransfer",
|
||||
"(J)Lim/molly/monero/WalletNative$NativePendingTransfer;");
|
||||
WalletNative_callRemoteNode = GetMethodId(
|
||||
env, walletNative,
|
||||
"callRemoteNode",
|
||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[B)Lim/molly/monero/HttpResponse;");
|
||||
WalletNative_onRefresh = GetMethodId(
|
||||
env, walletNative,
|
||||
"onRefresh", "(IJZ)V");
|
||||
WalletNative_onSuspendRefresh = GetMethodId(
|
||||
env, walletNative,
|
||||
"onSuspendRefresh", "(Z)V");
|
||||
|
||||
// android.os
|
||||
auto parcelFileDescriptor = findClass(env, "android/os/ParcelFileDescriptor");
|
||||
TxInfoClass = ScopedJavaLocalRef<jclass>(env, txInfo);
|
||||
|
||||
ParcelFileDescriptor_detachFd = parcelFileDescriptor
|
||||
.getMethodId(env, "detachFd", "()I");
|
||||
ParcelFd_detachFd = GetMethodId(env, parcelFd, "detachFd", "()I");
|
||||
|
||||
StringClass = ScopedJavaLocalRef<jclass>(env, GetClass(env, "java/lang/String"));
|
||||
}
|
||||
|
||||
} // namespace monero
|
||||
|
@ -2,14 +2,14 @@
|
||||
#define WALLET_JNI_CACHE_H__
|
||||
|
||||
#include "common/jvm.h"
|
||||
#include "common/java_native.h"
|
||||
|
||||
namespace monero {
|
||||
|
||||
// Initialize various classes and method pointers cached for use in JNI.
|
||||
void initializeJniCache(JNIEnv* env);
|
||||
void InitializeJniCache(JNIEnv* env);
|
||||
|
||||
// im.molly.monero
|
||||
extern ScopedJvmGlobalRef<jclass> TxInfoClass;
|
||||
extern jmethodID HttpResponse_getBody;
|
||||
extern jmethodID HttpResponse_getCode;
|
||||
extern jmethodID HttpResponse_getContentType;
|
||||
@ -18,9 +18,13 @@ extern jmethodID TxInfo_ctor;
|
||||
extern jmethodID WalletNative_callRemoteNode;
|
||||
extern jmethodID WalletNative_onRefresh;
|
||||
extern jmethodID WalletNative_onSuspendRefresh;
|
||||
extern ScopedJavaGlobalRef<jclass> TxInfoClass;
|
||||
|
||||
// android.os
|
||||
extern jmethodID ParcelFileDescriptor_detachFd;
|
||||
extern jmethodID ParcelFd_detachFd;
|
||||
|
||||
// java.lang
|
||||
extern ScopedJavaGlobalRef<jclass> StringClass;
|
||||
|
||||
} // namespace monero
|
||||
|
||||
|
@ -9,13 +9,13 @@ namespace monero {
|
||||
extern "C"
|
||||
JNIEXPORT jint
|
||||
JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
JNIEnv* env = initializeJvm(vm, JNI_VERSION_1_6);
|
||||
JNIEnv* env = InitializeJvm(vm, JNI_VERSION_1_6);
|
||||
if (env == nullptr) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
initializeJniCache(env);
|
||||
initializeEasyLogging();
|
||||
InitializeJniCache(env);
|
||||
InitializeEasyLogging();
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "logging.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
#include "jni_cache.h"
|
||||
|
||||
#include "easylogging++.h"
|
||||
@ -41,7 +43,7 @@ class JvmEasyLoggingDispatcher : public el::LogDispatchCallback {
|
||||
#define EL_BASE_FORMAT "%msg"
|
||||
#define EL_TRACE_FORMAT "[%fbase:%line] " EL_BASE_FORMAT
|
||||
|
||||
void initializeEasyLogging() {
|
||||
void InitializeEasyLogging() {
|
||||
el::Configurations c;
|
||||
c.setGlobally(el::ConfigurationType::ToFile, "false");
|
||||
c.setGlobally(el::ConfigurationType::ToStandardOutput, "false");
|
||||
@ -69,14 +71,15 @@ void JvmLogSink::write(const std::string& tag,
|
||||
const std::string& msg) {
|
||||
LOG_FATAL_IF(m_logger.is_null(), "Logger not set");
|
||||
const int pri_idx = static_cast<int>(priority);
|
||||
JNIEnv* env = getJniEnv();
|
||||
ScopedJvmLocalRef<jstring> j_tag = nativeToJvmString(env, tag);
|
||||
ScopedJvmLocalRef<jstring> j_msg = nativeToJvmString(env, msg);
|
||||
m_logger.callVoidMethod(env, Logger_logFromNative,
|
||||
pri_idx, j_tag.obj(), j_msg.obj());
|
||||
JNIEnv* env = GetJniEnv();
|
||||
ScopedJavaLocalRef<jstring> j_tag(env, NativeToJavaString(env, tag));
|
||||
ScopedJavaLocalRef<jstring> j_msg(env, NativeToJavaString(env, msg));
|
||||
|
||||
CallVoidMethod(env, m_logger.obj(), Logger_logFromNative,
|
||||
pri_idx, j_tag.obj(), j_msg.obj());
|
||||
}
|
||||
|
||||
void JvmLogSink::set_logger(JNIEnv* env, const JvmRef<jobject>& logger) {
|
||||
void JvmLogSink::set_logger(JNIEnv* env, const JavaRef<jobject>& logger) {
|
||||
m_logger = logger;
|
||||
}
|
||||
|
||||
@ -86,7 +89,7 @@ Java_im_molly_monero_NativeLoaderKt_nativeSetLogger(
|
||||
JNIEnv* env,
|
||||
jclass clazz,
|
||||
jobject j_logger) {
|
||||
JvmLogSink::instance()->set_logger(env, JvmParamRef<jobject>(j_logger));
|
||||
JvmLogSink::instance()->set_logger(env, JavaParamRef<jobject>(j_logger));
|
||||
}
|
||||
|
||||
} // namespace monero
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/jvm.h"
|
||||
#include "common/scoped_java_ref.h"
|
||||
|
||||
namespace monero {
|
||||
|
||||
@ -20,7 +20,7 @@ enum LoggingLevel {
|
||||
};
|
||||
|
||||
// Register easylogging++ post dispatcher and configure log format.
|
||||
void initializeEasyLogging();
|
||||
void InitializeEasyLogging();
|
||||
|
||||
// Log sink to send logs to JVM via Logging.kt API.
|
||||
class JvmLogSink {
|
||||
@ -36,13 +36,13 @@ class JvmLogSink {
|
||||
// This is called when a log message is dispatched by easylogging++.
|
||||
void write(const std::string& tag, LoggingLevel priority, const std::string& msg);
|
||||
|
||||
void set_logger(JNIEnv* env, const JvmRef<jobject>& logger);
|
||||
void set_logger(JNIEnv* env, const JavaRef<jobject>& logger);
|
||||
|
||||
protected:
|
||||
JvmLogSink() = default;
|
||||
|
||||
private:
|
||||
ScopedJvmGlobalRef<jobject> m_logger;
|
||||
ScopedJavaGlobalRef<jobject> m_logger;
|
||||
};
|
||||
|
||||
} // namespace monero
|
||||
|
@ -36,7 +36,7 @@ static_assert(PER_KB_FEE_QUANTIZATION_DECIMALS == 8,
|
||||
Wallet::Wallet(
|
||||
JNIEnv* env,
|
||||
int network_id,
|
||||
const JvmRef<jobject>& wallet_native)
|
||||
const JavaRef<jobject>& wallet_native)
|
||||
: m_wallet(static_cast<cryptonote::network_type>(network_id),
|
||||
0, /* kdf_rounds */
|
||||
true, /* unattended */
|
||||
@ -57,7 +57,7 @@ Wallet::Wallet(
|
||||
|
||||
// Generate keypairs deterministically. Account creation time will be set
|
||||
// to Monero epoch.
|
||||
void generateAccountKeys(cryptonote::account_base& account,
|
||||
void GenerateAccountKeys(cryptonote::account_base& account,
|
||||
const std::vector<char>& secret_scalar) {
|
||||
crypto::secret_key secret_key;
|
||||
LOG_FATAL_IF(secret_scalar.size() != sizeof(secret_key.data),
|
||||
@ -71,7 +71,7 @@ void Wallet::restoreAccount(const std::vector<char>& secret_scalar, uint64_t res
|
||||
LOG_FATAL_IF(m_account_ready, "Account should not be reinitialized");
|
||||
std::lock_guard<std::mutex> lock(m_wallet_mutex);
|
||||
auto& account = m_wallet.get_account();
|
||||
generateAccountKeys(account, secret_scalar);
|
||||
GenerateAccountKeys(account, secret_scalar);
|
||||
if (restore_point < CRYPTONOTE_MAX_BLOCK_NUMBER) {
|
||||
m_restore_height = restore_point;
|
||||
m_last_block_timestamp = 0;
|
||||
@ -384,8 +384,8 @@ void Wallet::notifyRefreshState(bool debounce) {
|
||||
last_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
if (!debounce) {
|
||||
m_callback.callVoidMethod(getJniEnv(), WalletNative_onRefresh,
|
||||
height, ts, m_balance_changed);
|
||||
CallVoidMethod(GetJniEnv(), m_callback.obj(), WalletNative_onRefresh,
|
||||
height, ts, m_balance_changed);
|
||||
}
|
||||
}
|
||||
|
||||
@ -429,18 +429,20 @@ template<typename T>
|
||||
auto Wallet::suspendRefreshAndRunLocked(T block) -> decltype(block()) {
|
||||
std::unique_lock<std::mutex> wallet_lock(m_wallet_mutex, std::try_to_lock);
|
||||
if (!wallet_lock.owns_lock()) {
|
||||
JNIEnv* env = getJniEnv();
|
||||
JNIEnv* env = GetJniEnv();
|
||||
for (;;) {
|
||||
if (!m_wallet.stopped()) {
|
||||
m_wallet.stop();
|
||||
m_callback.callVoidMethod(env, WalletNative_onSuspendRefresh, true);
|
||||
CallVoidMethod(env, m_callback.obj(),
|
||||
WalletNative_onSuspendRefresh, true);
|
||||
}
|
||||
if (wallet_lock.try_lock()) {
|
||||
break;
|
||||
}
|
||||
std::this_thread::yield();
|
||||
}
|
||||
m_callback.callVoidMethod(env, WalletNative_onSuspendRefresh, false);
|
||||
CallVoidMethod(env, m_callback.obj(),
|
||||
WalletNative_onSuspendRefresh, false);
|
||||
m_refresh_cond.notify_one();
|
||||
}
|
||||
// Call the lambda and release the mutex upon completion.
|
||||
@ -469,8 +471,8 @@ Java_im_molly_monero_WalletNative_nativeCreate(
|
||||
JNIEnv* env,
|
||||
jobject thiz,
|
||||
jint network_id) {
|
||||
auto wallet = new Wallet(env, network_id, JvmParamRef<jobject>(thiz));
|
||||
return nativeToJvmPointer(wallet);
|
||||
auto* wallet = new Wallet(env, network_id, JavaParamRef<jobject>(thiz));
|
||||
return NativeToJavaPointer(wallet);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
@ -480,7 +482,7 @@ Java_im_molly_monero_WalletNative_nativeDispose(
|
||||
jobject thiz,
|
||||
jlong handle) {
|
||||
auto* wallet = reinterpret_cast<Wallet*>(handle);
|
||||
free(wallet);
|
||||
delete wallet;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
@ -489,11 +491,10 @@ Java_im_molly_monero_WalletNative_nativeRestoreAccount(
|
||||
JNIEnv* env,
|
||||
jobject thiz,
|
||||
jlong handle,
|
||||
jbyteArray p_secret_scalar,
|
||||
jbyteArray j_secret_scalar,
|
||||
jlong restore_point) {
|
||||
auto* wallet = reinterpret_cast<Wallet*>(handle);
|
||||
std::vector<char> secret_scalar = jvmToNativeByteArray(
|
||||
env, JvmParamRef<jbyteArray>(p_secret_scalar));
|
||||
std::vector<char> secret_scalar = JavaToNativeByteArray(env, j_secret_scalar);
|
||||
Eraser secret_eraser(secret_scalar);
|
||||
wallet->restoreAccount(secret_scalar, restore_point);
|
||||
}
|
||||
@ -582,7 +583,7 @@ Java_im_molly_monero_WalletNative_nativeGetAccountPrimaryAddress(
|
||||
jobject thiz,
|
||||
jlong handle) {
|
||||
auto* wallet = reinterpret_cast<Wallet*>(handle);
|
||||
return nativeToJvmString(env, wallet->public_address()).Release();
|
||||
return NativeToJavaString(env, wallet->public_address());
|
||||
}
|
||||
|
||||
extern "C"
|
||||
@ -605,18 +606,27 @@ Java_im_molly_monero_WalletNative_nativeGetCurrentBlockchainTimestamp(
|
||||
return wallet->current_blockchain_timestamp();
|
||||
}
|
||||
|
||||
ScopedJvmLocalRef<jobject> nativeToJvmTxInfo(JNIEnv* env,
|
||||
const TxInfo& tx) {
|
||||
LOG_FATAL_IF(tx.m_height >= CRYPTONOTE_MAX_BLOCK_NUMBER, "Blockchain max height reached");
|
||||
ScopedJavaLocalRef<jobject> NativeToJavaTxInfo(JNIEnv* env,
|
||||
const TxInfo& tx) {
|
||||
LOG_FATAL_IF(tx.m_height >= CRYPTONOTE_MAX_BLOCK_NUMBER,
|
||||
"Blockchain max height reached");
|
||||
// TODO: Check amount overflow
|
||||
return {env, TxInfoClass.newObject(
|
||||
env, TxInfo_ctor,
|
||||
nativeToJvmString(env, pod_to_hex(tx.m_tx_hash)).obj(),
|
||||
tx.m_public_key_known ? nativeToJvmString(env, pod_to_hex(tx.m_public_key)).obj() : nullptr,
|
||||
tx.m_key_image_known ? nativeToJvmString(env, pod_to_hex(tx.m_key_image)).obj() : nullptr,
|
||||
return {env, NewObject(
|
||||
env,
|
||||
TxInfoClass.obj(), TxInfo_ctor,
|
||||
ScopedJavaLocalRef<jstring>(
|
||||
env, NativeToJavaString(env, pod_to_hex(tx.m_tx_hash))).obj(),
|
||||
tx.m_public_key_known ? ScopedJavaLocalRef<jstring>(
|
||||
env, NativeToJavaString(env, pod_to_hex(tx.m_public_key))).obj()
|
||||
: nullptr,
|
||||
tx.m_key_image_known ? ScopedJavaLocalRef<jstring>(
|
||||
env, NativeToJavaString(env, pod_to_hex(tx.m_key_image))).obj()
|
||||
: nullptr,
|
||||
tx.m_subaddress_major,
|
||||
tx.m_subaddress_minor,
|
||||
(!tx.m_recipient.empty()) ? nativeToJvmString(env, tx.m_recipient).obj() : nullptr,
|
||||
(!tx.m_recipient.empty()) ? ScopedJavaLocalRef<jstring>(
|
||||
env, NativeToJavaString(env, tx.m_recipient)).obj()
|
||||
: nullptr,
|
||||
tx.m_amount,
|
||||
static_cast<jint>(tx.m_height),
|
||||
tx.m_state,
|
||||
@ -636,14 +646,14 @@ Java_im_molly_monero_WalletNative_nativeGetTxHistory(
|
||||
jobject thiz,
|
||||
jlong handle) {
|
||||
auto* wallet = reinterpret_cast<Wallet*>(handle);
|
||||
ScopedJvmLocalRef<jobjectArray> j_array;
|
||||
jobjectArray j_array;
|
||||
wallet->withTxHistory([env, &j_array](std::vector<TxInfo> const& txs) {
|
||||
j_array = nativeToJvmObjectArray(env,
|
||||
txs,
|
||||
TxInfoClass.getClass(),
|
||||
&nativeToJvmTxInfo);
|
||||
j_array = NativeToJavaObjectArray<TxInfo>(env,
|
||||
txs,
|
||||
TxInfoClass.obj(),
|
||||
&NativeToJavaTxInfo);
|
||||
});
|
||||
return j_array.Release();
|
||||
return j_array;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
@ -654,7 +664,7 @@ Java_im_molly_monero_WalletNative_nativeFetchBaseFeeEstimate(
|
||||
jlong handle) {
|
||||
auto* wallet = reinterpret_cast<Wallet*>(handle);
|
||||
std::vector<uint64_t> fees = wallet->fetchBaseFeeEstimate();
|
||||
return nativeToJvmLongArray(env, fees.data(), fees.size()).Release();
|
||||
return NativeToJavaLongArray(env, fees.data(), fees.size());
|
||||
}
|
||||
|
||||
} // namespace monero
|
||||
|
@ -79,9 +79,10 @@ class Wallet : tools::i_wallet2_callback {
|
||||
REFRESH_ERROR = 3,
|
||||
};
|
||||
|
||||
public:
|
||||
Wallet(JNIEnv* env,
|
||||
int network_id,
|
||||
const JvmRef<jobject>& wallet_native);
|
||||
const JavaRef<jobject>& wallet_native);
|
||||
|
||||
void restoreAccount(const std::vector<char>& secret_scalar, uint64_t restore_point);
|
||||
uint64_t estimateRestoreHeight(uint64_t timestamp);
|
||||
@ -130,7 +131,7 @@ class Wallet : tools::i_wallet2_callback {
|
||||
std::mutex m_refresh_mutex;
|
||||
|
||||
// Reference to Kotlin wallet instance.
|
||||
const ScopedJvmGlobalRef<jobject> m_callback;
|
||||
const ScopedJavaGlobalRef<jobject> m_callback;
|
||||
|
||||
std::condition_variable m_refresh_cond;
|
||||
std::atomic<bool> m_refresh_running;
|
||||
|
@ -63,7 +63,7 @@ object MoneroMnemonic {
|
||||
|
||||
@CalledByNative("mnemonics/mnemonics.cc")
|
||||
@JvmStatic
|
||||
private fun buildMnemonicFromNative(
|
||||
private fun buildMnemonicFromJNI(
|
||||
entropy: ByteArray,
|
||||
wordsBytes: ByteArray,
|
||||
language: String,
|
||||
|
Loading…
Reference in New Issue
Block a user