How to prevent JNI from overwriting GetLastError() - java

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;
}

Related

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.

JNI IMU code not compiling "base operand of ‘->’ has non-pointer type ‘JNIEnv’"

I'm working on a native module to allow use of the RPI sense hat in java and my native will not compile:
At the moment I'm just trying to return the data as strings, if you have a simpler way please show me.
Here is the source file (SenseHat.cpp):
#include <jni.h>
#include <stdio.h>
#include "SenseHat.h"
#include "RTIMULib.h"
RTIMU *imu;
RTPressure *pressure;
RTHumidity *humidity;
JNIEXPORT void JNICALL Java_SenseHat_init(JNIEnv *env, jobject thisObj) {
RTIMUSettings *settings = new RTIMUSettings("RTIMULib");
imu = RTIMU::createIMU(settings);
pressure = RTPressure::createPressure(settings);
humidity = RTHumidity::createHumidity(settings);
if ((imu == NULL) || (imu->IMUType() == RTIMU_TYPE_NULL)) {
printf("No IMU found\n");
exit(1);
}
// This is an opportunity to manually override any settings before the call IMUInit
// set up IMU
imu->IMUInit();
// this is a convenient place to change fusion parameters
imu->setSlerpPower(0.02);
imu->setGyroEnable(true);
imu->setAccelEnable(true);
imu->setCompassEnable(true);
// set up pressure sensor
if (pressure != NULL)
pressure->pressureInit();
// set up humidity sensor
if (humidity != NULL)
humidity->humidityInit();
}
JNIEXPORT jstring JNICALL Java_SenseHat_getTemperature(JNIEnv *env, jobject thisObj) {
RTIMU_DATA imuData = imu->getIMUData();
if (humidity != NULL)
humidity->humidityRead(imuData);
return ((*env)->NewStringUTF(env,imuData.temperature));
}
JNIEXPORT jstring JNICALL Java_SenseHat_getHumidity(JNIEnv *env, jobject thisObj) {
RTIMU_DATA imuData = imu->getIMUData();
if (humidity != NULL)
humidity->humidityRead(imuData);
return ((*env)->NewStringUTF(env,imuData.humidity));
}
JNIEXPORT jstring JNICALL Java_SenseHat_getOrientation(JNIEnv *env, jobject thisObj) {
RTIMU_DATA imuData = imu->getIMUData();
return ((*env)->NewStringUTF(env,imuData.fusionPose));
}
The full errors is:
SenseHat.cpp: In function ‘_jstring* Java_SenseHat_getTemperature(JNIEnv*, jobject)’:
SenseHat.cpp:53:18: error: base operand of ‘->’ has non-pointer type ‘JNIEnv’
return ((*env)->NewStringUTF(env,imuData.temperature));
^
SenseHat.cpp: In function ‘_jstring* Java_SenseHat_getHumidity(JNIEnv*, jobject)’:
SenseHat.cpp:60:18: error: base operand of ‘->’ has non-pointer type ‘JNIEnv’
return ((*env)->NewStringUTF(env,imuData.humidity));
^
SenseHat.cpp: In function ‘_jstring* Java_SenseHat_getOrientation(JNIEnv*, jobject)’:
SenseHat.cpp:66:18: error: base operand of ‘->’ has non-pointer type ‘JNIEnv’
return ((*env)->NewStringUTF(env,imuData.fusionPose));
^
Compile code:
gcc -I /usr/lib/jvm/jdk-8-oracle-arm32-vfp-hflt/include/ -I /usr/lib/jvm/jdk-8-oracle-arm32-vfp-hflt/include/linux/ -shared -o libSenseHat.so SenseHat.cpp
JNIEnv is declared a bit different for C and C++, see jni.h for details. In few words in C we use
(*env)->NewStringUTF(env,imuData.temperature);
In C++ it should be
env->NewStringUTF(imuData.temperature);
As you see C++ version doesn't need env dereference and passing it as first parameter to JNI functions.

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.

Parameter passing in native a method [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
jni converting jstring to char *
There is a function on С (traverser.c module)
long int
Traverser(const char * sTraversingRoot)
{
long int nCount;
struct stat rStatBuf;
time_t nTime;
char sActualPath[512];
PGconn *pConn;
// Open DB connection
sprintf(sConnInfo,
"hostaddr=%s port=%s connect_timeout=50 dbname=%s user=%s password=%s",
sIP, sPort, sDBName, sLogin, sPassword);
pConn = PQconnectdb(sConnInfo);
if (PQstatus(pConn) == CONNECTION_BAD) {
AddErrorToLog("No connect\n");
return 0;
}
GetActualPath(sActualPath, sTraversingRoot);
if (*sActualPath) {
stat(sActualPath, &rStatBuf);
} else {
stat("/", &rStatBuf);
}
if (nClock)
nTime = time(NULL);
if(S_ISREG(rStatBuf.st_mode)) {
nCount = 1;
ProcessFile(pConn, sActualPath);
}
if(S_ISDIR(rStatBuf.st_mode)) {
nCount = _Traverser(pConn, sActualPath);
}
if (nClock)
fprintf(stdout, "Total time : %u second(s)\n", time(NULL) - nTime);
// Close DB connection
PQfinish(pConn);
return nCount;
}
I want to create native with the same name a method on Java
public native void Traverser(String path)
Respectively in the traverser.c module there will be a function
JNIEXPORT void JNICALL Java_ParallelIndexation_Traverser(JNIEnv *env, jobject obj, jstring path)
The Java_ParallelIndexation_Traverser function is a Traverser function wrapper from the traverser.c module.The question is: How to call a module from Traverser traverser.c in Java_ParallelIndexation_Traverser, passing it the parameter jstring path, thus converting it to a const char * (signature Traverser see above)?
Did I understand your question correctly: how to implement Java_ParallelIndexation_Traverser so that it calls the unmanaged Traveser function?
If so, here's how:
JNIEXPORT void JNICALL Java_ParallelIndexation_Traverser(JNIEnv* env, jobject obj, jstring path)
{
const jbyte* path2 = env->GetStringUTFChars(path, nullptr);
if (path2 == nullptr)
return;
::Traverser(path2);
env->ReleaseStringUTFChars(path, path2);
}
Edit:
Explanation: JNIEnv::GetStringUTFChars converts a jstring to a byte array. You then need to call JNIEnv::ReleaseStringUTFChars to deallocate that byte array.

How to use C function on android with NDK

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);

Categories