char* (*loadDex) (char * dexPath, char * odexPath,int flag) = NULL;
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
char* (*loadDex) (char *, char *,int) = NULL;
LOGD("JNI_OnLoad!");
void *ldvm = (void*) dlopen("/system/lib/libdvm.so", RTLD_LAZY);
if(ldvm == NULL)
{
LOGD("ERROR : %s",dlerror());
//is art
void *ldvm = (void*) dlopen("/system/lib/libart.so", RTLD_LAZY);
}
loadDex = (char* (*) (char *, char *,int)) dlsym (ldvm, "loadDex");
void *venv;
if ((*vm)->GetEnv(vm, (void**) &venv, JNI_VERSION_1_4) != JNI_OK)
{
return -1;
}
return JNI_VERSION_1_4;
}
I use dlsym() function in order to get the pointer of loadDex() but it returns 0. Anyone here can teach me how to get the exact pointer?
Thanks in advance!
loadDex was a private API of dalvik and doesn't exist in ART. This sort of thing should just be done in Java.
Related
When our app is getting a lot of traffic through JNI (hundreds of elements) seems like we are getting a lot of heap corruption errors (seems like it happens more for bigger elements).
abort 0x0000007e32cdf360
art::Runtime::Abort(char const*) 0x0000007daf4c22ac
android::base::LogMessage::~LogMessage() 0x0000007e33a6a654
art::gc::Verification::LogHeapCorruption(art::ObjPtr<art::mirror::Object>, art::MemberOffset, art::mirror::Object*, bool) const 0x0000007daf298318
art::gc::collector::ConcurrentCopying::MarkNonMoving(art::Thread*, art::mirror::Object*, art::mirror::Object*, art::MemberOffset) 0x0000007daf226b98
art::gc::collector::ConcurrentCopying::ThreadFlipVisitor::VisitRoots(art::mirror::CompressedReference<art::mirror::Object>**, unsigned long, art::RootInfo const&) 0x0000007daf22909c
art::Thread::HandleScopeVisitRoots(art::RootVisitor*, int) 0x0000007daf50af7c
void art::Thread::VisitRoots<false>(art::RootVisitor*) 0x0000007daf50e840
art::gc::collector::ConcurrentCopying::ThreadFlipVisitor::Run(art::Thread*) 0x0000007daf22870c
art::(anonymous namespace)::CheckJNI::ReleasePrimitiveArrayElements(char const*, art::Primitive::Type, _JNIEnv*, _jarray*, void*, int) 0x0000007daf37c680
Java_org_libsodium_jni_SodiumJNI_crypto_1aead_1xchacha20poly1305_1ietf_1decrypt sodium-jni.c:156
art_quick_generic_jni_trampoline 0x0000007daf148354
<unknown> 0x000000009d05bbe8
Seems like the line causing the is located here (our code is open source) https://github.com/standardnotes/react-native-sodium/blob/367b61a90180fe75ddef5b599e01c47cb4761b1f/android/src/main/cpp/sodium-jni.c#L156. I've tried to debug this more but my JNI + CPP knowledge is limited. Do you have any tips for exchanging data from Java to C++ in a better way?
Code snippet:
JNIEXPORT jint JNICALL
Java_org_libsodium_jni_SodiumJNI_crypto_1aead_1xchacha20poly1305_1ietf_1decrypt(JNIEnv *jenv,
jclass clazz,
jbyteArray j_m,
jintArray j_mlen_p,
jbyteArray j_nsec,
jbyteArray j_c,
jint j_clen,
jbyteArray j_ad,
jint j_adlen,
jbyteArray j_npub,
jbyteArray j_k) {
unsigned char *c = as_unsigned_char_array(jenv, j_c);
unsigned char *m = (unsigned char *) (*jenv)->GetByteArrayElements(jenv, j_m, 0);
unsigned char *npub = as_unsigned_char_array(jenv, j_npub);
unsigned char *ad = as_unsigned_char_array(jenv, j_ad);
unsigned char *nsec = as_unsigned_char_array(jenv, j_nsec);
unsigned char *k = as_unsigned_char_array(jenv, j_k);
int result = crypto_aead_xchacha20poly1305_ietf_decrypt(m, j_mlen_p, nsec, c, j_clen, ad, j_adlen, npub, k);
(*jenv)->ReleaseByteArrayElements(jenv, j_m, (jbyte *) m, 0);
return (jint)result;
}
Calling from java:
#ReactMethod
public void crypto_aead_xchacha20poly1305_ietf_decrypt(final String cipherText, final String public_nonce, final String key, final String additionalData, final Promise p) {
try {
byte[] c = this.base64ToBin(cipherText, Sodium.base64_variant_ORIGINAL());
byte[] npub = this.hexToBin(public_nonce);
byte[] k = this.hexToBin(key);
if (c == null || c.length <= 0)
p.reject(ESODIUM,ERR_FAILURE);
else if (npub.length != Sodium.crypto_aead_xchacha20poly1305_IETF_NPUBBYTES())
p.reject(ESODIUM,ERR_BAD_NONCE);
else if (k.length != Sodium.crypto_aead_xchacha20poly1305_IETF_KEYBYTES())
p.reject(ESODIUM,ERR_BAD_KEY);
else {
byte[] ad = additionalData != null ? additionalData.getBytes(StandardCharsets.UTF_8) : null;
int adlen = additionalData != null ? ad.length : 0;
int[] decrypted_len = new int[1];
byte[] decrypted = new byte[c.length - Sodium.crypto_aead_chacha20poly1305_IETF_ABYTES()];
int result = Sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(decrypted, decrypted_len, null, c, c.length, ad, adlen, npub, k);
if (result != 0)
p.reject(ESODIUM,ERR_FAILURE);
else
p.resolve(new String(decrypted, StandardCharsets.UTF_8));
}
}
catch (Throwable t) {
p.reject(ESODIUM,ERR_FAILURE,t);
}
}
Seems like it happens for bigger elements most of the time, but not always. Also happens for crypto_1aead_1xchacha20poly1305_1ietf_1encrypt.
ReleasePrimitiveArrayElements means ->ReleaseByteArrayElements().
The issue is likely that you're referring to JNIEnv* and at some point it detaches from the thread (the processing time would be rather interesting). You'd need to obtain JNIEnv* differently, eg. alike AttachCurrentThreadIfNeeded(). Also see JNI threads.
I have a swig wrapper for jni # ndk.
The function header is:
//
// Created by Tomasz on 03/11/2017.
//
#ifndef PC_ANDORID_APP_RESIZE_GIF_H
#define PC_ANDORID_APP_RESIZE_GIF_H
int Version();
int ResizeAnimation(const char * infile, const char * outfile);
#endif //PC_ANDORID_APP_RESIZE_GIF_H
The swig interface is simple as this:
%module GifResizer
%inline %{
#include "resize-gif.h"
extern int Version();
extern int ResizeAnimation(const char * infile, const char * outfile);
%}
and the implementation of ResizeAnimation is:
int ResizeAnimation(const char * infile, const char * outfile) {
initialize();
/* ... */
return 0;
}
The problem is, that value of params in Swig generater wrapper:
SWIGEXPORT jint JNICALL Java_org_imagemagick_GifResizerJNI_ResizeAnimation(JNIEnv *jenv, jclass jcls, jstring jarg1, jstring jarg2) {
jint jresult = 0 ;
char *arg1 = (char *) 0 ;
char *arg2 = (char *) 0 ;
int result;
(void)jenv;
(void)jcls;
arg1 = 0;
if (jarg1) {
arg1 = (char *)(*jenv)->GetStringUTFChars(jenv, jarg1, 0);
if (!arg1) return 0;
}
arg2 = 0;
if (jarg2) {
arg2 = (char *)(*jenv)->GetStringUTFChars(jenv, jarg2, 0);
if (!arg2) return 0;
}
result = (int)ResizeAnimation((char const *)arg1,(char const *)arg2);
jresult = (jint)result;
if (arg1) (*jenv)->ReleaseStringUTFChars(jenv, jarg1, (const char *)arg1);
if (arg2) (*jenv)->ReleaseStringUTFChars(jenv, jarg2, (const char *)arg2);
return jresult;
}
is okay and the arg1 and arg2 have proper values, but once ResizeAnimation is called, the pointers point to different memory address, and infile (arg1) is null, while outfile (arg2) is some random memory.
All the sources are built with standard android CMake for NDK.
The problem was caused by running x86_64 code on x86 emulator. Silly :)
I a Library in C that I'm leveraging for an Android application. This library has an audio stream that it occasionally flushes. When this happens it calls a write callback function of my design.
My intent is to have that C callback call a method on a specific Java Object which will handle stuff with the strem.
Currently I have code like so:
methodID compressionHandler=0;
jobject compressionHandlerClass;
int audioBufferChunkSize;
static JavaVM *gJavaVM;
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
gJavaVM = vm;
return JNI_VERSION_1_6;
}
JNIEXPORT void JNICALL
Java_com_my_code_init(JNIEnv* env, jobject obj, /*classpath of the class we want to call against*/jstring compressedAudioHandlerPath, /*class instance we want to call against*/jobject callbackClass) {
......
// this is a global ref as per:
//http://stackoverflow.com/questions/14765776/jni-error-app-bug-accessed-stale-local-reference-0xbc00021-index-8-in-a-tabl
compressionHandlerClass = (*env)->NewGlobalRef(env,callbackClass);
// name of the class
const char *classLocation;
// convert jString to c String
classLocation = (*env)->GetStringUTFChars( env, compressedAudioHandlerPath , NULL ) ;
// tmp variable for holding the class location, relates to the above issue with garbage collection
jclass clazz = (*env)->FindClass(env, classLocation);
// the actual method that we want to call, this gets used in the writeCallback
compressionHandler = (*env)->GetMethodID(env, clazz, "handleCompressedAudio", "([B)V");
......
}
The callback method looks like so:
void writeCallback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data) {
JNIEnv *env;
int isAttached = 0;
if ((status = (*gJavaVM)->GetEnv(gJavaVM, (void**)&env, JNI_VERSION_1_6)) < 0) {
if ((status = (*gJavaVM)->AttachCurrentThread(gJavaVM, &env, NULL)) < 0) {
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
isAttached = 1;
}
if(*env!=0 && compressionHandler!=0){
jbyteArray arr = (*env)->NewByteArray(env,bytes);
(*env)->SetByteArrayRegion(env,arr, 0, bytes, (jbyte*)buffer);
(*env)->CallVoidMethod(env,compressionHandlerClass, compressionHandler,arr);
free(arr);
free(env);
free(isAttached);
}
}
I'm getting crashes at the CallVoidMethod, that signature of which is an interface implemented by whatever object I pass in:
public interface CompressedAudioHandler {
void handleCompressedAudio(byte[] buff);
}
I suspect that I am improperly attaining/keep references to these objects, but I haven't found a great way to handle that. Any advice on how I can more correctly handle this?
I am developing a Decoder using android NDK and FFmpeg native libraries. I have put Native Support for the project using Android Tools and I have the C code in videodecoder.cpp file. In the file the following function gives me this problem
JNIEXPORT jint Java_ssrp_android_ffmpegdecoder_H264Decoder_consumeNalUnitsFromDirectBuffer(
JNIEnv* env, jobject thiz, jobject nal_units, jint num_bytes,
jlong pkt_pts) {
DecoderContext *ctx = get_ctx(env, thiz);
void *buf = NULL;
if (nal_units == NULL) {
D("Received null buffer, sending empty packet to decoder");
} else {
buf = env->GetDirectBufferAddress(nal_units);
if (buf == NULL) {
D("Error getting direct buffer address");
return -1;
}
}
AVPacket packet = {.data = (uint8_t*) buf, .size = num_bytes, .pts = pkt_pts };
int frameFinished = 0;
int res = avcodec_decode_video2(ctx->codec_ctx, ctx->src_frame,&frameFinished, &packet);
if (frameFinished)
ctx->frame_ready = 1;
return res;
}
At the line AVPacket packet = {.data = (uint8_t*) buf, .size = num_bytes, .pts = pkt_pts };
It says that `Statement has no effect "AVPAcket" and
At the line int res = avcodec_decode_video2(ctx->codec_ctx, ctx->src_frame,&frameFinished, &packet);
It says that Invalid arguments '
Candidates are:
int avcodec_decode_video2(AVCodecContext *, AVFrame *, int *, const AVPacket *)'
The Problem is
AVPacket packet = {.data = (uint8_t*) buf, .size = num_bytes, .pts = pkt_pts }
as the Compiler does not understand the type / initialization.
This leads to the invalid argument error.
Maybe split the line into:
AVPacket packet;
packet.data = (uint8_t*) buf;
packet.size = num_bytes;
packet.pts = pkt_pts;
This should get more clear error output.
I want to call Java API from NDK C++ thread, but env->FindClass() return 0. But when I call Java API in main thread, it works well. I've already call AttachCurrentThread() in the thread, can anyone help me?
Here is the source code:
JAVA CODE:
public class simple_test extends Activity {
...
// This functin will be called in C++
public void PrintNdkLog(String slog) {
Log.e(logTagNDK, slog);
return;
}
}
C++ CODE:
static JavaVM* g_JavaVM = NULL;
jobject getInstance(JNIEnv *env, jclass obj_class)
{
jmethodID c_id = env->GetMethodID(obj_class, "<init>", "()V");
jobject obj = env->NewObject(obj_class, c_id);
return obj;
}
// JNI OnLoad
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
g_JavaVM = jvm;
return JNI_VERSION_1_6;
}
// Call JAVA API "PrintNdkLog" in this function
void PrintNdkLog(char *lpLog)
{
if (g_JavaVM == NULL)
return;
JNIEnv *env = NULL;
g_JavaVM->GetEnv((void**)&env, JNI_VERSION_1_6);
if (env == NULL)
return;
jclass cls = env->FindClass("com/myndk/simple_test");
if (cls != 0) // **cls will be 0 when PrintNdkLog() is called in thread**
{
LOGE("FindClass error %p", cls);
}
else
{
jmethodID mid;
jobject obj;
obj = getInstance(env, cls);
mid = env->GetMethodID(cls, "PrintNdkLog", "(Ljava/lang/String;)V");
if (mid != 0)
{
jstring jstrMSG = env->NewStringUTF(lpLog);
env->CallVoidMethod(obj, mid, jstrMSG);
}
}
}
// Call JAVA API in thread
static void* thread_test(void* ptr)
{
JNIEnv *envLocal;
int status = g_JavaVM->GetEnv((void **) &envLocal, JNI_VERSION_1_6);
if (status == JNI_EDETACHED)
{
status = g_JavaVM->AttachCurrentThread(&envLocal, NULL);
if (status != JNI_OK)
LOGE("AttachCurrentThread failed %d",status);
}
PrintNdkLog("bbb"); // This JAVA callback failed, and printed "FindClass error"
}
// Create thread
int NdkThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority)
{
PrintNdkLog("aaa"); // This JAVA callback runs well
pthread_t pid;
pthread_create(&pid, NULL, thread_test, pParam);
}
I have solved it now.
In NDK native thread, only can call static Java API. If you call env->FindClass(), it would trigger an exception.
http://android.wooyd.org/JNIExample gived the detail info.
Suggest to take a look on AttachCurrentThread.
Here is a sample code to do that:
// Global variable
JavaVM *g_jvm = NULL; //Get g_jvm from jni main thread use env->GetJavaVM(&g_jvm);
jobject g_obj = NULL; //Where the java function exist. (some activity)
//Get env in thread function and attach the env
JNIEnv *env;
if(g_jvm->AttachCurrentThread(&env, NULL) != JNI_OK)
{
LOGD("%s: AttachCurrentThread() failed", __FUNCTION__);
}
const char * fnName ="somFunctionInYourJava"; //which should be "pulic void somFunctionInYourJava(String input);"
jstring retStr = env->NewStringUTF(str);
jclass cls = env->GetObjectClass(thiz);
jmethodID messageMe = env->GetMethodID(cls, fnName, "(Ljava/lang/String;)V");
env->CallVoidMethod(thiz, messageMe, retStr);
//Detach thread and release related resource
if(g_jvm->DetachCurrentThread() != JNI_OK)
{
LOGD("%s: DetachCurrentThread() failed", __FUNCTION__);
}