How to use C function on android with NDK - java

I want to use function written in C in an Android app. The function is taken from here. I've learned some basics of android NDK, I can manage to use the function in my Java source, but the application freezes when I call it, than I have to wait until android offers me to kill the app.
The JNI signature is this:
JNIEXPORT void JNICALL Java_pda_lupa_callbacks_MySurfaceCallback_NativeYuv2rgb
(JNIEnv *env, jclass clazz,
jbyteArray imageIn, jint widthIn, jint heightIn,
jobject imageOut, jint widthOut, jint heightOut) {
jbyte *cImageIn = (*env)->GetByteArrayElements(env, imageIn, NULL);
jbyte *cImageOut = (jbyte*)(*env)->GetDirectBufferAddress(env, imageOut);
toRGB565((unsigned short*)cImageIn, widthIn, heightIn, (unsigned int*)cImageOut, widthOut, heightOut);
(*env)->ReleaseByteArrayElements(env, imageIn, cImageIn, JNI_ABORT);
}
Then I load it in Java like this:
static {
System.loadLibrary("rgb");
}
public native void NativeYuv2rgb(byte[] imageIn, int widthIn, int heightIn,
java.nio.Buffer imageOut, int widthOut, int heightOut);
And when I call it, app freezes (I don't get any error):
NativeYuv2rgb(this.cameraFrame, this.prevX, this.prevY,
this.rgb, this.prevX, this.prevY);
I guess I might use wrong variable types for the imageIn and imageOut, but I don't know...
Thanks for your help!
edit: This is output from GDB:
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 4731]
0x8083f0b0 in ?? ()

OK. Segmentation fault, is a sign of bad pointer manipulation.
Change the the code to the example below , and test to see if that works for you:
You should change all the
jbyte *cImageIn = env->GetByteArrayElements(env, imageIn, NULL);
jbyte *cImageOut = (jbyte*) env->GetDirectBufferAddress(env, imageOut);
...
env->ReleaseByteArrayElements(env, imageIn, cImageIn, JNI_ABORT);

Related

when i build a thread to call the JNI C++ function, the program crashed

i want to use these three functions in the sub thread, but the program crashes.
Here are the tree C++ function:
//here are three JNI interface
public native byte[] readImage(String str);
public native byte[] FPGAEnhancement(String str, int flag);
public native byte[] FPGASobel(String str, int flag);
These three function can be used correctly in the android program, but when i build a sub thread and use these three functions, the program crashed.
Through test,it seems that this line crashes, the result cant be returned.
env->SetByteArrayRegion(jarrRV, 0, length, data);
here are the readImage function:
Java_wearablepc_ftcl_hit_edu_cn_fpga_MainActivity_readImage(
JNIEnv *env, jobject, jstring jstr) {
FILE *fp;
fp = fopen(jstringTostring(env, jstr), "rb");
if (fp != NULL) {
int length = getFileLength(fp);
Log_i("fp length");
if(length<=0) {
jbyteArray jarrRV = env->NewByteArray(1);
return jarrRV;
} else {
jbyteArray jarrRV = env->NewByteArray(length);
jbyte data[length];
readImage(fp, data, length);
fclose(fp);
env->SetByteArrayRegion(jarrRV, 0, length, data);
return jarrRV;
}
} else {
jbyteArray jarrRV = env->NewByteArray(1);
return jarrRV;
}
I guess there might be some thing wrong the way i call the C++ function in the sub thread, but i dont know exactly what it is.Thanks!!
When the image file is big, allocating data[length] on stack may exhaust the stack, and case crashes. You actually don't need this array at all, you can safely access the bytes of newly created jarrRV directly:
jbyteArray jarrRV = env->NewByteArray(length);
jboolean isCopy;
jbyte data = env->GetByteArrayElements(env, jarrRV, &isCopy);
readImage(fp, data, length);
fclose(fp);
env->ReleaseByteArrayElements(jarrRV, data, 0);
Some other comments on your C++ code:
it seems that in some cases, the file will not be closed.
In all cases, jstringTostring() is likely to leak both native and Java objects. Please don't underestimate the effect of such leak in multithreaded environment.

Pass Array of Mat to NDK

I followed same steps of taking native address in array and passed to NDK.Arraylist pass java to ndk
And in the native side i done as given below,
JNIEXPORT jint JNICALL Java_com_app_android_flowerhgram_1woutcondition_TemplateMatch_MatchTemplate
(JNIEnv *env, jclass, jlong addrProcessed, jlongArray templates, jobject out){
Mat& mProcesseed = *(Mat *)addrProcessed;
vector <Mat> trainimgs;
jint retVal=0;
int num=0,temp=0;
jclass alCls = env->FindClass("org/opencv/core/Mat");
jmethodID jMatCons = env->GetMethodID(alCls,"<init>","()V");
jmethodID alGetId = env->GetMethodID(alCls, "getNativeObjAddr", "()J");
jmethodID sizeMethodID = env->GetMethodID(alCls, "size", "()I");
jlong *traindata = env->GetLongArrayElements(templates,0);
int intValue = *(int*) sizeMethodID;
for(int k=0;k < intValue ; k++)
{
Mat & newimage=*(Mat*)traindata[k];
trainimgs.push_back(newimage);
}
env->ReleaseLongArrayElements(templates,traindata,0);
return retVal;
}
For this i am not facing any build error, but while compiling i getting error in size
Pending exception java.lang.NoSuchMethodError: no non-static method "Lorg/opencv/core/Mat;.size()I".
please guide me for this or any other solution will be appreciated.
After long study, i found the solution for this question. Hope this may useful for others
To get length of array the below line can be used,
jsize a_len = env->GetArrayLength(templates);
And a_len can be used in for loop
This solved my problem for my above question.

SIGSEGV when calling Java method from native pthread

In a Java project that uses C code via JNI I have a piece of native C code that obtains references to an object and one of its methods, then starts a native thread, passing these references to it in a struct. When the thread tries to call the method, the code crashes with a SIGSEGV. Calling that same method from the main thread works.
Doing some research I learned that the env reference is only valid within the thread and that any other native thread must be attached first. I did this but the code still crashes on the first call to the method.
Strangely, when I call the same method from the main thread before I create the other thread (just uncomment the line in the main code), things work for some time. The output thread loops for some 10,000 times before it crashes.
The method call is to DataOutputStream.writeShort(). The thread in question is the only one writing to the DataOutputStream. However, the DataOutputStream is connected to a DataInputStream.
Simplified native code:
static void write_output(struct output_state *s) {
int i;
jint sample;
for (i = 0; i < 2 * s->result_len; i += 2) {
sample = (s->result[i] << 8) + s->result[i+1];
(*(s->env))->CallVoidMethod(s->env, s->tunerOut, s->writeShort, sample);
}
}
static void *output_thread_fn(void *arg)
{
struct output_state *s = arg;
(*(s->jvm))->AttachCurrentThread(s->jvm, &(s->env), NULL);
while (!do_exit) {
// use timedwait and pad out under runs
safe_cond_wait(&s->ready, &s->ready_m);
pthread_rwlock_rdlock(&s->rw); //sync access to s with producer thread
write_output(s);
pthread_rwlock_unlock(&s->rw);
}
(*(s->jvm))->DetachCurrentThread(s->jvm);
return 0;
}
JNIEXPORT jboolean JNICALL Java_eu_jacquet80_rds_input_SdrGroupReader_open
(JNIEnv *env, jobject self) {
jclass clsSelf = (*env)->GetObjectClass(env, self);
jfieldID fTunerOut = (*env)->GetFieldID(env, clsSelf, "tunerOut", "Ljava/io/DataOutputStream;");
jobject tunerOut = (*env)->GetObjectField(env, self, fTunerOut);
jclass cls = (*env)->GetObjectClass(env, tunerOut);
jmethodID writeShortID = (*env)->GetMethodID(env, cls, "writeShort", "(I)V");
if (!writeShortID || !cls)
return 0;
(*env)->GetJavaVM(env, &(output.jvm));
output.tunerOut = tunerOut;
output.writeShort = writeShortID;
// (*env)->CallVoidMethod(env, tunerOut, writeShortID, 0); // just for testing
pthread_create(&controller.thread, NULL, controller_thread_fn, (void *)(&controller));
usleep(100000);
pthread_create(&output.thread, NULL, output_thread_fn, (void *)(&output));
pthread_create(&demod.thread, NULL, demod_thread_fn, (void *)(&demod));
pthread_create(&dongle.thread, NULL, dongle_thread_fn, (void *)(&dongle));
return 1;
}
Are JNI references such as jclass, jfieldID, jobject and jmethodID subject to the same limitations as JNIEnv, i.e. valid only within the same thread?
Suspecting this, I moved the JNI reference stuff from open() to output_thread(), right after the call to AttachCurrentThread(). However, I still need to pass a jobject reference (self) across thread borders, and the call to GetObjectClass() crashes.
What is the correct way to create a thread native code and have that thread call a particular method of a given class instance?
Turns out my suspicion was correct: jobject and jclass references are indeed local, i.e. valid only within the same thread and only until the current native method returns. See http://developer.android.com/training/articles/perf-jni.html#local_and_global_references.
My approach of moving the reference-related code to the thread function was correct, except that I need to first convert self into a global reference by calling NewGlobalRef().
Updated code:
static void write_output(struct output_state *s) {
int i;
jint sample;
for (i = 0; i < 2 * s->result_len; i += 2) {
sample = (s->result[i] << 8) + s->result[i+1];
(*(s->env))->CallVoidMethod(s->env, s->tunerOut, s->writeShort, sample);
}
}
static void *output_thread_fn(void *arg)
{
struct output_state *s = arg;
(*(s->jvm))->AttachCurrentThread(s->jvm, &(s->env), NULL);
jclass clsSelf = (*(s->env))->GetObjectClass(s->env, s->self);
jfieldID fTunerOut = (*(s->env))->GetFieldID(s->env, clsSelf, "tunerOut", "Ljava/io/DataOutputStream;");
s->tunerOut = (*(s->env))->GetObjectField(s->env, s->self, fTunerOut);
jclass cls = (*(s->env))->GetObjectClass(s->env, s->tunerOut);
s->writeShort = (*(s->env))->GetMethodID(s->env, cls, "writeShort", "(I)V");
while (!do_exit) {
// use timedwait and pad out under runs
safe_cond_wait(&s->ready, &s->ready_m);
pthread_rwlock_rdlock(&s->rw); //sync access to s with producer thread
write_output(s);
pthread_rwlock_unlock(&s->rw);
}
(*(s->jvm))->DetachCurrentThread(s->jvm);
return 0;
}
JNIEXPORT jboolean JNICALL Java_eu_jacquet80_rds_input_SdrGroupReader_open
(JNIEnv *env, jobject self) {
jclass clsSelf = (*env)->GetObjectClass(env, self);
if (!writeShortID || !cls)
return 0;
output.self = (*env)->NewGlobalRef(env, self);
(*env)->GetJavaVM(env, &(output.jvm));
(*env)->CallVoidMethod(env, tunerOut, writeShortID, 0); // just for testing
pthread_create(&controller.thread, NULL, controller_thread_fn, (void *)(&controller));
usleep(100000);
pthread_create(&output.thread, NULL, output_thread_fn, (void *)(&output));
pthread_create(&demod.thread, NULL, demod_thread_fn, (void *)(&demod));
pthread_create(&dongle.thread, NULL, dongle_thread_fn, (void *)(&dongle));
return 1;
}
One thing still missing is a call to DeleteGlobalRef() when the output thread is done. This is to make sure the global reference is released when it is no longer needed, so that the garbage collector can pick it up.

How to prevent JNI from overwriting GetLastError()

We are implementing a one-to-one mapping of some winapi methods, for the xidobi serial port project. The mapping of the C methods to java works as expected, but for an unknown reason GetLastError() get cleared.
Here is the C-Code:
// CreateFile ////////////////////////////////////////////////////////////
JNIEXPORT jint JNICALL
Java_org_xidobi_OS_CreateFile(JNIEnv *env, jobject this,
jstring lpFileName,
jint dwDesiredAccess,
jint dwShareMode,
jint lpSecurityAttributes,
jint dwCreationDisposition,
jint dwFlagsAndAttributes,
jint hTemplateFile) {
const char* fileName = (*env)->GetStringUTFChars(env, lpFileName, NULL);
HANDLE handle = CreateFile(fileName,
dwDesiredAccess,
dwShareMode,
(LPSECURITY_ATTRIBUTES) lpSecurityAttributes,
dwCreationDisposition,
dwFlagsAndAttributes,
(HANDLE) hTemplateFile);
(*env)->ReleaseStringUTFChars(env, lpFileName, fileName);
return (jint) handle;
}
// GetLastError ////////////////////////////////////////////////////////////
JNIEXPORT jint JNICALL
Java_org_xidobi_OS_GetLastError(JNIEnv *env, jobject this) {
return (jint) GetLastError();
}
In Java we call the mapped native methods in like this:
int handle = os.CreateFile("\\\\.\\" + portName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if (handle != INVALID_HANDLE_VALUE)
return handle;
int lastError= os.GetLastError(); //-> sometimes 0 (ERROR_SUCCESS)
We figured out that if we call GetLastError() in C right after CreateFile(..) the correct error code is returned. Since the one-to-one mapping is dead simple we assume that JNI or the VM calls SetLastError() itself and clears our last error.
We don't want to give up the one-to-one mapping design, so what can we do to solve this puzzle?
Here is a similar question that doesn't help in this case: CreateFile() returns INVALID_HANDLE_VALUE but GetLastError() is ERROR_SUCCESS
There is no guarantee that GetLastError() will survive whatever the JVM is doing between your native call to CreateFile and your native call to GetLastError. Therefore you should immediately call GetLastError after CreateFile and save the value in a thread local slot of your own.
Then your implementation of GetLastError will retrieve it from wherever you stored it.
You may wish to rename it LastXidobiError or some such as it will only retrieve an error which was set by a call in your library.
// Space to store last error ////////////////////////////////////////////
static DWORD dwTlsIndexLastError = 0;
BOOL WINAPI DllMain(
_In_ HINSTANCE hinstDLL,
_In_ DWORD fdwReason,
_In_ LPVOID lpvReserved
){
switch(fdwReason){
case DLL_PROCESS_ATTACH:
dwTlsIndexLastError = TlsAlloc();
break;
case DLL_PROCESS_DETACH:
TlsFree(dwTlsIndexLastError);
dwTlsIndexLastError = 0;
break;
}
return TRUE;
}
///// Save the last error.
///// Call this function after the "real" function whose error you want to report.
void SaveLastError()
{
TlsSetValue(dsTlsIndexLastError, (LPVOID)(DWORD_PTR)GetLastError());
}
// GetLastError ////////////////////////////////////////////////////////////
JNIEXPORT jint JNICALL
Java_org_xidobi_OS_GetLastError(JNIEnv *env, jobject this) {
return (jint) TlsGetValue(dsTlsIndexLastError);
}
// CreateFile ////////////////////////////////////////////////////////////
JNIEXPORT jint JNICALL
Java_org_xidobi_OS_CreateFile(JNIEnv *env, jobject this,
jstring lpFileName,
jint dwDesiredAccess,
jint dwShareMode,
jint lpSecurityAttributes,
jint dwCreationDisposition,
jint dwFlagsAndAttributes,
jint hTemplateFile) {
const char* fileName = (*env)->GetStringUTFChars(env, lpFileName, NULL);
HANDLE handle = CreateFile(fileName,
dwDesiredAccess,
dwShareMode,
(LPSECURITY_ATTRIBUTES) lpSecurityAttributes,
dwCreationDisposition,
dwFlagsAndAttributes,
(HANDLE) hTemplateFile);
// Save the value of GetLastError for the relevant function
SaveLastError();
(*env)->ReleaseStringUTFChars(env, lpFileName, fileName);
return (jint) handle;
}

JNI ReferenceTable overflow: how to release ObjectArray containing Strings

I am having a frustrating time resolving this issue and the JNI documentation is woefully sparse. Assistance would be greatly appreciated!
Am assigned a project on an Android networking app. The native side stores information about the users you are connected to and I need to send this user list to the Java side to update UI info. Essentially I need to compose a string array and a boolean array which contain the names and flags of connected users, but later more info may be sent. My difficulty is in releasing & cleaning up the Object array containing the String information. I am not clear on how to do this. Here is what I have:
void name_list_cb(struct user_info* user_list, size_t count, void *userdata)
{
jobject callbacks = (jobject)userdata;
JNIEnv *env;
(*g_vm)->GetEnv(g_vm, (void**)&env, JNI_VERSION_1_4);
jclass cls = (*env)->GetObjectClass(env, callbacks);
jmethodID method = (*env)->GetMethodID(env, cls, "user_list", "([Ljava/lang/String;[Z)V");
int i;
jobjectArray name_list;
jbooleanArray connected_list;
name_list = (jobjectArray)(*env)->NewObjectArray(env, count, (*env)->FindClass(env, "java/lang/String"), (*env)->NewStringUTF(env, ""));
connected_list = (jbooleanArray)(*env)->NewBooleanArray(env, count);
uint8_t boolean_arr[count];
for(i = 0; i < count; i++) {
(*env)->SetObjectArrayElement(env, name_list, i, (*env)->NewStringUTF(env, user_list[i].name));
boolean_arr[i] = user_list[i].connected;
}
(*env)->SetBooleanArrayRegion(env, connected_list, 0, count, (jboolean *)boolean_arr);
(*env)->CallVoidMethod(env, callbacks, method, name_list, connected_list);
(*env)->ReleaseBooleanArrayElements(env, connected_list, (jboolean *)boolean_arr, 0);
for(i = 0; i < count; i++) {
(*env)->ReleaseStringUTFChars(env, (*env)->GetObjectArrayElement(env, name_list, i), user_list[i].name);
//(*env)->ReleaseObjectArrayElements(env, name_list, count, 0);
}
(*env)->DeleteLocalRef(env, boolean_arr);
(*env)->DeleteLocalRef(env, name_list);
(*env)->DeleteLocalRef(env, connected_list);
(*env)->DeleteLocalRef(env, cls);
}
I get either a "referencetable overflow", or a "signal 11 (SIGSEGV), fault addr deadbaad". The overflow/memleak is the main prob. Basically I am not releasing the UTFChars and the Object elements. Although I have seen references to it online, my JNI version does not have ReleaseObjectArrayElement[s]. I have been researching how to do this exactly but no luck so far!
I think the problem is (*env)->DeleteLocalRef(env, boolean_arr); because boolean_arr is C function stack variable.
typedef jarray jobjectArray;
void (*DeleteLocalRef)(JNIEnv*, jobject);
So I think use DeleteLocalRef could release the jobjectArray.
Or you can just new the jobjectArray just one time in an initialize method.

Categories