How to print native C log inside JNI-C code? - c

I have the following JNI C code
JNIEXPORT jint JNICALL
Java_XYZ_addData
(JNIEnv * env, jobject callingObject, jlong sensorId)
{
int status =
getSensorMeasurement ((void *) sensorId);
printf ("status_code %d \n", status);
}
I have bunch of log messages inside getSensorMeasurement(..) C code
When I invoke the JNI-C code from Java code;
it only prints status_code
but the log messages inside getSensorMeasurement(..) are not printed in console.
How can I get hold of the console logs from internal C code ?
Thanks
Kaniska

Related

Why is my C code not keeping the state of static variables?

So, all information I've found online points to JNI keeping state of global variables defined on C.
However, In my particular case, this isn't working.
C Code:
//*snip* (just the includes)
static int sockfd = -2;
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
printf("Calling onLoad\n");
return JNI_VERSION_1_4;
}
JNIEXPORT jboolean JNICALL cinit
(JNIEnv * env, jobject obj){
sockfd = 5; //This would normally be the socket initialization code, but this is a lot simpler and also fails.
printf("Open %d\n",sockdf); // This prints 5.
return 1;
}
JNIEXPORT jboolean JNICALL cwrite
(JNIEnv * env, jobject obj, jbyteArray bytes){
// a is a valid array.
printf("Write %d\n",sockfd); // This prints -2!!!!
if ((b = write(sockfd, a, sizeof(a))) < 0){
perror("write");
return 0;
}
return 1;
}
The Java code just inits the library with a single loadLibrary inside a static block, and then calls open, then write in sucession.
Any idea of what am I doing wrong here? Thank you in advance!
The prints I get from those are:
Calling onLoad
Open 5
Write -2
EDIT: simplified the example a bit more, and added the suggestions from the comments.

jstring comparison in .c language what to do [duplicate]

This question already has an answer here:
Jstring comparison in.c language
(1 answer)
Closed 6 years ago.
How to compare two jstrings in c language.I need this for jni execution in Which the logic is written in .c file for comparing two names
** EDIT **
per #JonasVautherin comment:
While not causing a bug, this is one of the rare cases where use of strncmp can be replaced with strcmp without fear of reading out of bounds of the string.
The reason for that is native strings fetched from jvm are valid null terminated strings so no overflow will occur in this case, which is what strncmp guarantees.
This is also the reason why strncmp in this case will not cause a bug - it will terminate on \0 on either strings, or until the number of characters read is reached (reference: https://www.cplusplus.com/reference/cstring/strncmp/)
If you want to use pure native:
JNIEXPORT void JNICALL Java_ClassName_MethodName(JNIEnv *env, jobject obj, jstring javaString1, jstring javaString2)
{
const char *nativeString1 = (*env)->GetStringUTFChars(env, javaString1, 0);
const char *nativeString2 = (*env)->GetStringUTFChars(env, javaString2, 0);
/* Now you can compare nativeString1 with nativeString2*/
int res = strncmp(nativeString1, nativeString2, strlen(nativeString1);
/* And don't forget to release the handles */
(*env)->ReleaseStringUTFChars(env, javaString1, nativeString1);
(*env)->ReleaseStringUTFChars(env, javaString2, nativeString2);
}
If you want to use Java from native code (same answer as this one all credit goes to the original author):
JNIEXPORT void JNICALL Java_ClassName_MethodName(JNIEnv *env, jobject obj, jstring javaString1, jstring javaString2)
{
/* Get java class String handle */
jclass cls = (*env)->GetObjectClass(env, javaString1);
/* Get method ID equals from String handle */
jmethodID mID = (*env)->GetMethodID(env, cls, "equals", "(Ljava/lang/Object;)Z");
/* Compare both methods, store in jboolean which can be case to uint8_t or bool if you're using C99*/
jboolean equals = (*env)->CallBooleanMethod(env, javaString1, mID, javaString2);
}

char* value changes after function return

Didn't find similar question, so opening a new one.
I'm trying to pass a Java object to JNI layer in my program and extract Java String field in it. The extracted field is set in the C struct.
I'm seeing a strange behavior. I'm able to extract the required fields successfully and memcpy it in the struct field. But when my utility function returns, the field set in struct get corrupted.
I suspected a memory leak and rechecked, but nothing looks suspicious (I'm not doing much dynamic memory stuff code..just one malloc and free.)
Here goes the code:
Header File:
typedef struct Job {
char* job_id;
} Job;
C File:
JNIEXPORT jint JNICALL Java_com.test.JobHandler__1submitJob(
JNIEnv *env, jobject this, jobject job) {
current_job = (Job *) malloc(sizeof(Job));
convert_job(env, job, current_job);
free(current_job);
}
void convert_job(JNIEnv *env, jobject javajob, Job *job) {
jclass cls = (*env)->GetObjectClass(env, javajob);
getVoidStringField(env, cls, javajob, job->job_id, "getJob_id");
//job->job_id gets corrupted here
}
void getVoidStringField(JNIEnv *env, jclass cls, jobject obj, char *jobStr, char *methodName) {
jmethodID mid = (*env)->GetMethodID(env, cls, methodName, "()Ljava/lang/String;");
jobject js = (*env)->CallObjectMethod(env, obj, mid);
const char *str = (*env)->GetStringUTFChars(env, js, 0);
int len = (*env)->GetStringUTFLength(env, js);
jobStr = (char*) malloc(len);
memcpy(jobStr, str, len);
jobStr[len] = '\0';
(*env)->ReleaseStringUTFChars(env, js, str);
//jobStr is fine till here.
}
I removed other stuff from my code and reduced to above version to simply debugging, still same issue.
If I pass modify getVoidStringField(...) a bit to accept Job object and then work on job->job_id, it works fine.
Curious to know. I doubt it has something to do with JNI.
Couple of things :
When you pass in job->job_id, you are passing in the VALUE of that pointer, not the pointer itself. That means when you do the jobStr = (char*) malloc(len);, you are not changing job->job_id, but just that function's local variable.
So, what you need to pass in is &(job->job_id), and the parameter should be char **jobstr
Your malloc would then be (not forgetting to allow for the null terminator):
*jobStr = (char*) malloc(len +1);
and, of course, the following statements should refer to *jobstr

Bad arguments for libopus create encoder

currently I'm trying to build JNI-bindings for libopus. My current problem ist that I have a C-method like this:
JNIEXPORT jobject JNICALL
Java_de_akuz_android_libopus_OpusCodecFactory_encodercreate(JNIEnv * env, jint samplingRate, jint channels, jint application)
{
jclass resultClass = (*env)->FindClass(env,"de/akuz/android/libopus/OpusFactoryResult");
jobject result = (*env)->AllocObject(env,resultClass);
jfieldID pointerFieldID = (*env)->GetFieldID(env, resultClass,"pointer","J");
jfieldID errorFieldID = (*env)->GetFieldID(env, resultClass,"errorCode","I");
int error;
OpusEncoder* encoder = opus_encoder_create(samplingRate, channels, application, &error);
char buf[100];
sprintf(buf, "Pointer address: %p", encoder); // puts string into buffer
__android_log_print(ANDROID_LOG_INFO, "OpusNative", buf);
(*env)->SetLongField(env, result, pointerFieldID, encoder);
(*env)->SetIntField(env, result, errorFieldID, error);
return result;
}
When I call this method from JNI with the values 48000 for sample rate, 2 for channels and 2049 as id for application (taken from opus defines) a always get the error code -1 and null pointer back. I already tried casting the sample rate to an opus_int32 but it didn't change anything. Also I tried to pass the defined OPUS_APPLICATION_AUDIO directly instead if its integer value but it also didn't change anything. Unfortunately I'm more of a Java guy so I would appreciate any held on this problem(s) in my C code.
I'm really sorry, but obviously I'm blind (or stupid). My method declaration is all wrong
Java_de_akuz_android_libopus_OpusCodecFactory_encodercreate(JNIEnv * env, jint samplingRate, jint channels, jint application)
I have the parameter for the JNI environment but not the parameter for the calling object. It seems that C isn't interested in the amount of arguments of the native method in the Java code or the type of the arguments. Because of the missing jobject after JNIEnv all my parameters were shifted by one with samplingRate having the value of the pointer for the missing jobject.
The correct method declaration would be
Java_de_akuz_android_libopus_OpusCodecFactory_encodercreate(JNIEnv * env, jobject object, jint samplingRate, jint channels, jint application)
I hope that anyone running into the same problem will find my answer useful.

jni callback works for java types, but not c types

I have followed the advice at
registering java function as a callback in C function and can callback with "simple" types such as integer and string, e.g.:
jstring js = (*env)->NewStringUTF(env, "hello");
(*env)->CallStaticVoidMethod(env, cls, methodid, js);
However, if I am trying to do the same with C datatypes which have been wrapped with SWIG, I am only getting null pointers in Java. In the C part they are definitely not 0. Do they need to be treated differently?
[EDIT:]
Some more information:
As stated above, char*/string is working for me as well. I am looking for a solution for C struct's, which have been wrapped by SWIG and have been allocated in Java.
E.g.:
typedef struct {
unsigned short length;
unsigned short value;
} lv_t;
is getting wrapped by SWIG, so I can use it in Java:
lv_t lv;
lv = modulename.modulename_new_lv();
lv.setLength(1);
lv.setValue(2);
Then I will give this struct from Java to C:
modulename.send(lv);
C will send it over the network, receive some reply and change the values in lv. Now, this should give the modified lv back to Java.
void jni_call_received_hook(lv_t* lv){
JNIEnv* m_env;
(*m_vm)->AttachCurrentThread(m_vm, (void**) &m_env, NULL );
jclass cls = (*m_env)->FindClass( m_env, "gui/StateMachine" );
jmethodID mid = (*m_env)->GetStaticMethodID(m_env, cls, "callReceivedEvent", "(Lcom/something/modulename/jni/lv_t;)V");
if (mid == 0){
log(E, "Unable to find method for callback");
return;
}
// what to do here to create a jobject?
jobject lv_j = ...;
(*m_env)->CallStaticVoidMethod(m_env, cls, mid, lv_j);
}
Which calls:
public static void messageHandler(lv_t lv) {
System.out.println("messageHandler().");
System.out.println("lv " + lv);
}
Sorry but I'm not able to comment in your question yet, so this is more a comment rather than an answer. Anyway, I've recently done something like that.
My callback works and is implemented as:
void jni_call_received_hook(char* username){
JNIEnv* m_env;
(*m_vm)->AttachCurrentThread(m_vm, (void**) &m_env, NULL );
jclass cls = (*m_env)->FindClass( m_env, "gui/StateMachine" );
jmethodID mid = (*m_env)->GetStaticMethodID(m_env, cls, "callReceivedEvent", "(Ljava/lang/String;)V");
if (mid == 0){
log(E, "Unable to find method for callback");
return;
}
(*m_env)->CallStaticVoidMethod(m_env, cls, mid, (*m_env)->NewStringUTF(m_env, username));
}
The variable m_vm is an instance of the JVM I've kept upon calling a method that registered this callback, like this:
JNIEXPORT void JNICALL Java_gui_StateMachine_setCallReceivedCallback(JNIEnv *e, jobject o){
(*e)->GetJavaVM(e, &m_vm );
set_call_received_hook(jni_call_received_hook);
}
Maybe your missing something. Let me know if this isn't clear enough. Hope it helps.

Resources