Android: CallStaticVoidMethod gets called only once - java

I have a JNI code and I am trying to call a static method in Java.
The OnLoad in JNI looks like below
extern "C" jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
JNIEnv *e;
mJvm = jvm;
if (jvm->GetEnv((void**) &e, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("Error getting Env var");
return JNI_ERR;
}
ALOG_ASSERT(env, "Could not retrieve the env!");
app = e->FindClass("xyz");
updateService = e->GetStaticMethodID(app, "updatestatus", "(I)V");
jniRegisterNativeMethods(e, "xyz",
Methods, NELEM(Methods));
return JNI_VERSION_1_4;
}
I have following global variables
JavaVM *mJvm;
jclass app;
jmethodID updateHDToService;
The callback where I am trying to call the Java method looks like this
JNIEnv *e;
if (!mIsListenerAttached) {
if (mJvm->AttachCurrentThread(&e, NULL) != JNI_OK) {
ALOGE("Error attaching native thread to java");
mIsListenerAttached = false;
return;
}
ALOGI("Aattached native thread to java");
mIsListenerAttached = true;
}
e->CallStaticVoidMethod(app, updateHDToService, radioContext->signalStat);
The static method in Java is called successfully only one time. Next time the same callback is executed it does not call the java method.
What am I doing wronG?

Please have a look at http://developer.android.com/training/articles/perf-jni.html. You don't need the protection variable, Calling AttachCurrentThread on an already-attached thread is a no-op..
But don't forget to call mJvm->detachCurrentThread() before the native thread exits!

Related

Need info regarding java.io.UnixFileSystem.getspace method, which unix command is executed?

Our Spring boot application hanged on production Unix environment. After getting the thread dump we were able to find that getSpace method of UnixFileSystem was locked. Can anyone help me with the Unix command that is called when java calls the getSpace method.
http-nio-8080-exec-1" - Thread t#45
java.lang.Thread.State: RUNNABLE
at java.io.UnixFileSystem.getSpace(Native Method)
at java.io.File.getUsableSpace(File.java:1885)
Basically a native call is made in java.io.UnixFileSystem to getSpace(...):
/* -- Disk usage -- */
public native long getSpace(File f, int t);
This native method ends up calling via JNI the method defined in this file of the JDK's repository:
https://github.com/openjdk/jdk/blob/master/src/java.base/unix/native/libjava/UnixFileSystem_md.c
In this file, at line 466, you find the following implementation (parts inside the if/else statements have been omitted for brevity):
JNIEXPORT jlong JNICALL
Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this,
jobject file, jint t)
{
jlong rv = 0L;
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
#ifdef MACOSX
struct statfs fsstat;
#else
struct statvfs64 fsstat;
int res;
#endif
memset(&fsstat, 0, sizeof(fsstat));
#ifdef MACOSX
if (statfs(path, &fsstat) == 0) {
switch(t) {
// omitted
}
}
#else
RESTARTABLE(statvfs64(path, &fsstat), res);
if (res == 0) {
switch(t) {
// omitted
}
}
#endif
} END_PLATFORM_STRING(env, path);
return rv;
}
So as you can see, if you are using MACOSX the C library function statfs is called, otherwise the function statvfs64 is called.

How to call getStackTrace method from java native interface (jni)

I am trying to understand the jni with some examples. I am trying to get the java stack trace using jni and so this is what I was doing
HelloJNI.java
package test.com.jni;
public class HelloJNI {
static {
System.loadLibrary("hello"); // Load native library at runtime
}
private native StackTraceElement[] getStackTraceNative(Throwable throwable);
private native void printStackTraceNative(Throwable throwable);
public static void main(String[] args) {
test();
}
public static void test() {
new HelloJNI().printStackTraceNative(new Throwable()); //Invoke native method
new HelloJNI().getStackTraceNative(new Throwable());
}
}
Native code (keeping the error handling out for simplicity)
test_com_jni_HelloJNI.c
JNIEXPORT jobjectArray JNICALL Java_test_com_jni_HelloJNI_getStackTraceNative (JNIEnv * env, jobject object, jthrowable exception) {
jclass exceptionClazz = (*env)->GetObjectClass(env, exception);
jmethodID getStackTraceMethod = (*env)->GetMethodID(env, exceptionClazz, "getStackTrace", "()[Ljava.lang.StackTraceElement");
jobjectArray stacktraces = (*env)->CallObjectMethod(env, exception, getStackTraceMethod);
return stacktraces;
}
JNIEXPORT void JNICALL Java_test_com_jni_HelloJNI_printStackTraceNative (JNIEnv * env, jobject object, jthrowable exception) {
jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail
jmethodID printStackTraceMethod = (*env)->GetMethodID(env, exceptionClazz, "printStackTrace", "()V");
(*env)->CallVoidMethod(env, exception, printStackTraceMethod);
}
Now in this code native printStackTraceNative method works and it prints the stack trace however, getStackTraceNative doesn't. When I check the core dump file it says that java has thrown an exception java/lang/NoSuchMethodError for getStackTrace. I am confused because the parameter I am passing to getStackTraceNative method is of the type throwable and throwable should have the method getStackTrace.
What concept I possibly be missing here, any help on this is appreciated. Thanks
You got the method signature wrong. In method signatures the periods in the qualified class name are replaced by forward slashes. The signature for getStackTrace is therefore ()[Ljava/lang/StackTraceElement; (also note the semicolon)
You can get the method signatures from class files with javap with -s option:
javap -classpath '/path/to/jre/lib/rt.jar' -s java.lang.Throwable

Toast not displaying when C function is called

I have a service that can either be started with a launcher or runs on boot. The problem is that when I call a C function with JNI, all the Toast notifications are no longer displayed, whereas if I call only the java method, the Toast notifications display just fine. What causes this and how can I fix it?
Note:
If the C function does something simple like calls back to java and terminates, the Toast notifications will display. My function however, has an infinite loop used to handle interrupts.
ServiceLauncher
public class ServiceLauncher extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
Toast.makeText(getBaseContext(), "onCreate", Toast.LENGTH_LONG).show();
super.onCreate(savedInstanceState);
startService(new Intent(this, MyService.class));
finish();
}
}
MyService
public class MyService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
public void onDestroy() {
Toast.makeText(this, "Terminated", Toast.LENGTH_LONG).show();
}
#Override
public int onStartCommand(Intent intent,int flags, int startid) {
Toast.makeText(this, "Running", Toast.LENGTH_LONG).show();
javaMethod(); /*toast displays if this is called*/
cMethodFromJNI(); /*toast does not display if this is called*/
return START_STICKY;
}
}
C code structure
JNIEXPORT jint JNICALL
Java_className_cMethodFromJNI(JNIEnv *env, jclass type) {
jmethodID mid = (*env)->GetStaticMethodID(env, type, "javaMethod", "(I)V");
// open and read some fd (not shown)
for (;;) {
// wait on change in fd and act on it (not shown)
callJavaAgain(env,type,mid)
}
}
//close fd (not shown)
exit(0);
}
EDIT: I've changed the code like so, following the answer below. Seems like the situation is exactly the same as before, perhaps I am missing something.
Revised C code structure
JNIEXPORT jint JNICALL
Java_className_cMethodFromJNI(JNIEnv * env, jclass type) {
/*cache JVM*/
int status = ( * env) - > GetJavaVM(env, & jvm);
if (status != 0) {
LOGD("failed to retrieve *env");
exit(1);
}
attachedThread();
}
void attachedThread() {
/* get a new environment and attach a new thread */
JNIEnv * newEnv;
JavaVMAttachArgs args;
args.version = JNI_VERSION_1_6; // choose your JNI version
args.name = NULL; // if you want to give the java thread a name
args.group = NULL; // you can assign the java thread to a ThreadGroup
( * jvm) - > AttachCurrentThread(jvm, & newEnv, & args);
jclass cls = ( * newEnv) - > FindClass(newEnv, "fully/qualified/class/name");
jmethodID mid = ( * newEnv) - > GetStaticMethodID(newEnv, cls, "callJavaAgain", "(V)V");
// open and read some fd (not shown)
for (;;) {
// wait on change in fd and act on it (not shown)
intermediaryFunction(newEnv, cls, mid);
}
}
//close fd (not shown)
exit(0);
}
void intermediaryFunction(JNIEnv * newEnv, jclass cls, jmethodID mid) {
//do some stuff (not shown)
( * newEnv) - > CallStaticVoidMethod(newEnv, cls, mid);
}
okay so this topic requires some careful attention. Android docs talk about JavaVM and JNIEnv and how to interface with them. What you will need to do is store a global reference to the JavaVM that's accessible from all threads in the C code. The JVM allows a thread to get access to the JNIEnv which can get you access to a class's methods and what not. The next key problem is that you need to stash a jobject 'pointer' to the android object you call back to from the C code (which you probably already know). Make sure you call JNIEnv->DetachThread on the thread that acquired the JNIEnv reference.
so again:
get JavaVM reference and store it in c code
have java call c code to store the jobject reference for your callback
start a thread to do looping, acquire a reference to a JNIEnv on that thread to call to java
call DetachThread before terminating thread execution
another point of trouble is accessing c++ objects from java code, there's a neat trick where you can have c++ code create the java objects, and assign into a private long _cppPointer member on the created Java class, then add this object to some collection on a java object you have already pointer to (like an activity or something)
good luck.
edit: i finally recalled another great source of information about JNI work

Is it possible to make a Java JNI which calls jdbc?

I am new to jni and very confused if I can use jni to achieve what I need done. I want to make a java api what will use jdbc to update database, but this particular api will be called from C++ program.
So I think I probably should write jni code which access the database via jdbc (is that even possible?), create C++ code and generate dll so other C++ programs can just call the dll to update database. Is this all possible? If so, how do I really call jdbc in jni?
If this dll is finally made, can Fortran call it as well?
My other thought is maybe I should make a regular java program to update the database, then use say ikvm to wrap the java class into C++ dll?
The thing is I have to use access database using Java. Our C++ programs will not access database at all, and it would be better if this java api can be accessed via system call.
Or is there any better way to do it?
I hope I explained it well. I am not all familiar with what I am assigned here and cannot find much relevant reference.
Thank you so much!!
UPDATED:
The problem is not all computers have C++ postgresql driver installed but they do have Java postgresql driver installed. We don't want to force everyone to install the C++ db driver and no major changes in those C++ program will be made. So it will make sense to come up something in Java to access database. The java system service (preferred, like dll?) /API basically is called to record start time and end time of a C++ program. C++ program will make a "function" call (with pass-in parameter and returned value) to this system service/Java API to record start/end time.
I'm not going to lecture you on the right or wrong way to approach what you are trying to do. However, if you are trying to invoke Java code (JDBC.jar) then the following is for you.. Otherwise feel free to downvote.
JVM.hpp:
#ifndef JVM_HPP_INCLUDED
#define JVM_HPP_INCLUDED
#include "../java/jni.h"
#include <windows.h>
#include <iostream>
#include <stdexcept>
#include <algorithm>
class Jvm
{
private:
JavaVM* jvm;
JNIEnv* env;
JavaVMInitArgs jvm_args;
jclass systemClassLoader;
public:
Jvm(std::string ClassPath = ".");
~Jvm();
inline JavaVM* GetJVM() const {return jvm;}
inline JNIEnv* GetENV() const {return env;}
inline jclass GetSystemClassLoader() const {return systemClassLoader;}
void DestroyJVM();
void PrintStackTrace();
jclass DefineClass(const char* FullClassName, const void* ClassBuffer, std::uint32_t BufferLength);
jclass DefineClass(const char* FullClassName, jobject ClassLoader, const void* ClassBuffer, std::uint32_t BufferLength);
void RegisterNativeMethod(const char* MethodName, const char* MethodSignature, void* func_ptr);
void RegisterNativeMethod(jobject ClassLoader, const char* MethodName, const char* MethodSignature, void* func_ptr);
void RegisterNativeMethods(JNINativeMethod* Methods, std::uint32_t MethodCount);
void RegisterNativeMethods(jobject ClassLoader, JNINativeMethod* Methods, std::uint32_t MethodCount);
protected:
void InitClassLoader();
};
#endif // JVM_HPP_INCLUDED
JVM.cpp:
#include "JVM.hpp"
Jvm::~Jvm()
{
env->DeleteGlobalRef(this->systemClassLoader);
jvm->DestroyJavaVM();
}
Jvm::Jvm(std::string ClassPath) : jvm(NULL), env(NULL), jvm_args(), systemClassLoader(NULL)
{
JavaVMOption* options = new JavaVMOption[2];
jvm_args.version = JNI_VERSION_1_6;
JNI_GetDefaultJavaVMInitArgs(&jvm_args);
options[0].optionString = const_cast<char*>("-Djava.compiler=NONE");
options[1].optionString = const_cast<char*>(("-Djava.class.path=" + ClassPath).c_str());
jvm_args.nOptions = 2;
jvm_args.options = options;
jvm_args.ignoreUnrecognized = false;
if (JNI_CreateJavaVM(&jvm, reinterpret_cast<void**>(&env), &jvm_args))
{
delete[] options;
throw std::runtime_error("Failed To Create JVM Instance.");
}
delete[] options;
}
void Jvm::InitClassLoader()
{
if (!this->systemClassLoader)
{
jclass classloader = env->FindClass("Ljava/lang/ClassLoader;");
if (!classloader)
{
throw std::runtime_error("Failed To find ClassLoader.");
}
jmethodID SystemLoaderMethod = env->GetStaticMethodID(classloader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
jobject loader = env->CallStaticObjectMethod(classloader, SystemLoaderMethod);
this->systemClassLoader = reinterpret_cast<jclass>(env->NewGlobalRef(loader));
}
}
void Jvm::PrintStackTrace()
{
if (env->ExceptionOccurred())
{
env->ExceptionDescribe();
env->ExceptionClear();
}
}
jclass Jvm::DefineClass(const char* FullClassName, const void* ClassBuffer, std::uint32_t BufferLength)
{
this->InitClassLoader();
return this->DefineClass(FullClassName, this->systemClassLoader, ClassBuffer, BufferLength);
}
jclass Jvm::DefineClass(const char* FullClassName, jobject ClassLoader, const void* ClassBuffer, std::uint32_t BufferLength)
{
return ClassLoader ? env->DefineClass(FullClassName, ClassLoader, static_cast<const jbyte*>(ClassBuffer), BufferLength) : NULL;
}
void Jvm::RegisterNativeMethod(const char* MethodName, const char* MethodSignature, void* func_ptr)
{
JNINativeMethod method;
method.name = const_cast<char*>(MethodName);
method.signature = const_cast<char*>(MethodSignature);
method.fnPtr = func_ptr;
this->RegisterNativeMethods(&method, 1);
}
void Jvm::RegisterNativeMethod(jobject ClassLoader, const char* MethodName, const char* MethodSignature, void* func_ptr)
{
JNINativeMethod method;
method.name = const_cast<char*>(MethodName);
method.signature = const_cast<char*>(MethodSignature);
method.fnPtr = func_ptr;
this->RegisterNativeMethods(ClassLoader, &method, 1);
}
void Jvm::RegisterNativeMethods(JNINativeMethod* Methods, std::uint32_t MethodCount)
{
this->InitClassLoader();
this->RegisterNativeMethods(this->systemClassLoader, Methods, MethodCount);
}
void Jvm::RegisterNativeMethods(jobject ClassLoader, JNINativeMethod* Methods, std::uint32_t MethodCount)
{
if (ClassLoader)
{
env->RegisterNatives(static_cast<jclass>(ClassLoader), Methods, MethodCount);
}
}
You can then create an instance of it that loads your jar.
int main()
{
Jvm VM("C:/Users/Brandon/IdeaProjects/Eos/out/production/Eos/Bot.jar");
jclass jMain = VM.GetENV()->FindClass("eos/Main");
if (jMain != nullptr)
{
jmethodID mainMethod = env->GetStaticMethodID(jMain, "main", "([Ljava/lang/String;)V");
jclass StringClass = env->FindClass("java/lang/String");
jobjectArray Args = env->NewObjectArray(0, StringClass, 0);
env->CallStaticVoidMethod(jMain, MainMethod, Args);
}
}
Now this just shows how to run a jar with a Main Method. However, you can access ANY class from the jar and invoke it with however many parameters needed. It doesn't need a main.
Now it IS a lot MORE work to do this, but I won't lecture you. The question was whether or not is was possible and the answer is YES.. it is. So long as you create an instance of the "JVM". After that, it's a matter of accessing the class via the "Package/Class" NOT "Package.Class" as done in Java.. then invoking whatever methods you want.

JNI: Callback from JVM to C++ fails to run

I start JVM from C++ program.
C++ code:
JNIEXPORT jobject JNICALL com_javelin_JavelinMarketData_callBackIntoNative(JNIEnv* env, jobject obj, jlong ptr)
{
std::cout << "com_javelin_JavelinMarketData_callBackIntoNative called" << std::endl;
}
int main()
{
JavaVM* jvm;
JNIEnv* env;
...
JNI_CreateJavaVM(&jvm, (void **)&env, &args);
jmethodID mainMethod = env->GetStaticMethodID(helloWorldClass, "main", "([Ljava/lang/String;)V");
env->CallStaticVoidMethod(helloWorldClass, mainMethod, ...);
}
then I need my C++ function called back from java:
Java code:
native void callBackIntoNative(long ptr);
public void onResponse(long param)
{
System.out.println("Calling callBackIntoNative()");
callBackIntoNative(param);
}
When I run my C++ program, JVM starts correctly, but after it prints "Calling callBackIntoNative()" the following acception appears:
Exception during callBackIntoNative(): 'com.javelin.JavelinMarketData.callBackIntoNative(J)V'
java.lang.UnsatisfiedLinkError: com.javelin.JavelinMarketData.callBackIntoNative(J)V
Can you please help?
Thanks, but I found a solution: I should have registered my C++ function as a native method using the code:
const JNINativeMethod methods[] = { { "callBackIntoNative", "(J)V", (void*)&com_javelin_JavelinMarketData_callBackIntoNative } };
const int methods_size = sizeof(methods) / sizeof(methods[0]);
jclass jClass = env->FindClass("com/javelin/JavelinMarketData");
env->RegisterNatives(jClass, methods, methods_size);
Now it works fine.
I would compile your callback apart into a dynamic library (.dll, .so, whatever your OS) and put it accesible to your java program. Then you just load your library by using JNI and call from your java classes whatever functionality you have in the library.

Categories