C++ Multithread Java JNI method Call - java

I have a simple class in Java:
public class MyClass
{
public static void dummyTest()
{
}
}
And in C++ I do the following JNI Call:
void c_call_function()
{
JNIEnv *env ...// the JNIEnv initialization in JNI...
jclass clazz ...// the class initialization in JNI...
jmethodID mid_dummyTest = env->GetStaticMethodID(clazz, "dummyTest", "()V");
env->CallStaticIntMethod(clazz, mid_dummyTest);
}
If a single program calls the static method c_call_function() it is ok.
But if a multithread program calls the c_call_function(), it gives me the following message, when passes the line env->CallStaticIntMethod(clazz, mid_dummyTest);:
Access violation at 0x000000006FC77154 read to 0x0000000000000000
If the program is multithread, it uses the same JNIEnv variable.
But I also tried to load the same JNIEnv via AttachCurrentThread method and it gives me the same problem.
What is the restriction when calling the method bellow by multiple threads as long as I don't create any local references or even delete or modify anything?
env->CallStaticIntMethod(clazz, mid_dummyTest);

env should be obtained separately for each thread. You should use AttachCurrentThread() if and only if a thread was *not started by JVM. For each thread that issues AttachCurrentThread(), you must call DetachCurrentThread().
clazz should be obtained separately for each thread, or you can save a global reference to the result received from FindClass().
mid_dummyTest can be saved in a global variable: this id is independent from the thread, env and clazz.

I a able to run similar code (look below) where I have multiple threads accessing the same JVM (macOS). I am using pthread.
Few things that are important
attaching thread to JVM ("The JNI interface pointer (JNIEnv) is valid only in the current thread. Should another thread need to access the Java VM, it must first call AttachCurrentThread() to attach itself to the VM and obtain a JNI interface pointer.")
joining threads
i guess it's a good idea to use mutex as well to prevent multiple threads being attached
main.c
#include <stdio.h>
#include <jni.h>
#include <pthread.h>
#define NUM_THREADS 6
pthread_mutex_t mutexjvm;
pthread_t threads[NUM_THREADS];
struct JVM {
JNIEnv *env;
JavaVM *jvm;
};
void invoke_class(JNIEnv* env);
void *jvmThreads(void* myJvm) {
struct JVM *myJvmPtr = (struct JVM*) myJvm;
JavaVM *jvmPtr = myJvmPtr -> jvm;
JNIEnv *env = myJvmPtr -> env;
pthread_mutex_lock (&mutexjvm);
printf("I will call JVM\n");
(*jvmPtr)->AttachCurrentThread(jvmPtr, (void**) &(env), NULL);
invoke_class( env );
(*jvmPtr)->DetachCurrentThread(jvmPtr);
pthread_mutex_unlock (&mutexjvm);
pthread_exit(NULL);
}
JNIEnv* create_vm(struct JVM *jvm)
{
JNIEnv* env;
JavaVMInitArgs vm_args;
JavaVMOption options;
options.optionString = "-Djava.class.path=./";
vm_args.options = &options;
vm_args.ignoreUnrecognized = 0;
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
int status = JNI_CreateJavaVM(&jvm->jvm, (void**)&env, &vm_args);
if (status < 0 || !env)
printf("Error\n");
return env;
}
void invoke_class(JNIEnv* env)
{
jclass Main_class;
jmethodID fun_id;
Main_class = (*env)->FindClass(env, "Main");
fun_id = (*env)->GetStaticMethodID(env, Main_class, "fun", "()I");
(*env)->CallStaticIntMethod(env, Main_class, fun_id);
}
int main(int argc, char **argv)
{
struct JVM myJvm;
myJvm.env = create_vm(&myJvm);
if(myJvm.env == NULL)
return 1;
pthread_mutex_init(&mutexjvm, NULL);
for(int i=0; i<NUM_THREADS; i++){
pthread_create(&threads[i], NULL, jvmThreads, (void*) &myJvm);
pthread_join(threads[i], 0);
}
(*myJvm.jvm)->DestroyJavaVM(myJvm.jvm);
}
Main.java
public class Main {
public static void main(String[] args){
System.out.println("Hello, world");
}
public static int fun() {
System.out.println("From JVM");
return 0;
}
}
Makefile
all: Main.class main
Main.class: Main.java
javac Main.java
main.o: main.c
llvm-gcc -c main.c \
-I${JAVA_HOME}/include \
-I${JAVA_HOME}/include/darwin/ \
main: main.o
ld -o main -L${JAVA_HOME}/jre/lib/server/ \
-ljvm \
-rpath ${JAVA_HOME}/jre/lib/server \
-L/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk \
-demangle -dynamic -arch x86_64 \
-macosx_version_min 10.12.0 \
-lSystem \
-rpath ${JAVA_HOME}/jre/lib/server/ \
main.o
clean:
rm -f Main.class main main.o
Once you run the code, you get following result:
./main
I will call JVM
From JVM
I will call JVM
From JVM
I will call JVM
From JVM
I will call JVM
From JVM
I will call JVM
From JVM
I will call JVM
From JVM

Related

How to return ArrayList<Boolean> in JNI java?

I'm new to JNI and I want to create an ArrayList<Boolean> from the C++ side.
I have the following situation:
import java.util.ArrayList;
public class createArrayJNI {
static {
System.loadLibrary("libnative");
}
public static void main(String[] args) {
createArrayJNI jni = new createArrayJNI();
ArrayList<Boolean> array = jni.creatArray();
System.err.println(array);
}
public native ArrayList<Boolean> creatArray();
}
and my cpp file is:
...
JNIEXPORT jobject JNICALL Java_createArrayJNI_creatArray
(JNIEnv \*env, jobject thisObject) {
jclass java_util_class = env->FindClass("java/util/ArrayList");
jmethodID java_util_method_constructor = env->GetMethodID(java_util_class, "<init>", "()V");
jmethodID java_add_method = env->GetMethodID(java_util_class, "add", "(Ljava/lang/Object;)Z");
jobject java_util_object = env->NewObject(java_util_class, java_util_method_constructor, "");
jboolean a = true;
env->CallBooleanMethod(java_util_object, java_add_method, a);
return java_util_object;
}
It shows me:
#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x00000001099110ac, pid=24207, tid=4355
#
# JRE version: OpenJDK Runtime Environment Homebrew (19.0.1) (build 19.0.1)
# Java VM: OpenJDK 64-Bit Server VM Homebrew (19.0.1, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, bsd-aarch64)
# Problematic frame:
# V \[libjvm.dylib+0x1f10ac\] AccessInternal::PostRuntimeDispatch\<G1BarrierSet::AccessBarrier\<598116ull, G1BarrierSet\>, (AccessInternal::BarrierType)2, 598116ull\>::oop_access_barrier(void\*)+0xc
#
# No core dump will be written. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# An error report file with more information is saved as:
# /Users/taki/Desktop/Preparation doctorat ERM/Projects/Julia4C/hs_err_pid24207.log
#
# If you would like to submit a bug report, please visit:
# https://github.com/Homebrew/homebrew-core/issues
#
zsh: abort /usr/bin/env --enable-preview -XX:+ShowCodeDetailsInExceptionMessages -cp
I'm pretty sure that the error comes from env->CallBooleanMethod(java_util_object, java_add_method, a); because the add method requires an object, and a is jboolean.
I tried to cast the a variable but it didn't work as well.
ArrayList<Boolean> is the same as just ArrayList. At runtime, the <Boolean> doesn't exist. This is called "type erasure".
The next thing to notice is that you did not try to add a Boolean (a kind of Object) but a primitive boolean. This probably gets interpreted as an object pointer and since it's not null it's the address of an Object - but true (the number 1) isn't a valid address of anything, and when the JVM dereferences it it crashes.
You need to get the static field Boolean.TRUE and add that to the ArrayList.
I found a way to instantiate a java Boolean Object from c++ using
Boolean.valueOf() function.
I first get staticMethodId and then use it to create jobject and use the same object in the add method as follow:
JNIEXPORT jobject JNICALL Java_createArrayJNI_creatArray
(JNIEnv *env, jobject thisObject) {
std::vector<bool> cpp_vec;
cpp_vec.push_back(true);
cpp_vec.push_back(false);
cpp_vec.push_back(true);
jclass java_util_class = env->FindClass("java/util/ArrayList");
jmethodID java_util_method_constructor = env->GetMethodID(java_util_class, "<init>", "()V");
jmethodID java_add_method = env->GetMethodID(java_util_class, "add", "(Ljava/lang/Object;)Z");
jobject java_util_object = env->NewObject(java_util_class, java_util_method_constructor, "");
jclass bool_class = env->FindClass("java/lang/Boolean");
jmethodID bool_init = env->GetStaticMethodID(bool_class, "valueOf", "(Z)Ljava/lang/Boolean;");
for (int i=0; i< cpp_vec.size(); i++){
jboolean curr_element = cpp_vec[i];
jobject bool_object = env->CallStaticObjectMethod(bool_class, bool_init, (jboolean) cpp_vec[i]);
if (bool_object == NULL){
std::cout << "Cannot instantiate the Boolean object";
return NULL;
}
env->CallBooleanMethod(java_util_object, java_add_method, bool_object);
}
return java_util_object;
}

Java Invocation API: Call the C function back from the java code

I have a C (navive) program and a jar file with the main() method. From my native program I am initializing the JVM, and calling the main() method. I have no problems with this, everything is completely fine. But then I wanted to call back a C function from my java code.
The C function is defined in the native code in the same module as the one, that have created the JVM. The header is auto-generated, and the body is as simple as this:
JNIEXPORT void JNICALL Java_eu_raman_chakhouski_NativeUpdaterBus_connect0(JNIEnv* env, jclass clazz)
{
return;
}
So, from the java code I'm calling NativeUpdaterBus.connect0(), continuosly getting an UnsatisfiedLinkError. I have no System.loadLibrary() calls in my java code, because I thought, that there will be no problems calling the native code back from the java code if the target module is (possibly?) already loaded.
Well, maybe my approach is completely incorrect, but I can't see any obvious defects, maybe you could help?
What possibly could help (but I didn't tried any of these approaches, because I'm still not quite sure)
Use a kind of a "trampoline" dynamic library with these JNI methods, load it from the java code, then marshal native calls through it.
Define a java.lang.Runnable's anonymous inheritor, created with jni_env->DefineClass() but this involves some bytecode trickery.
Use an another, less invasive approach, like sockets, named pipes, etc. But in my case I'm using only one native process, so this might be an overkill.
I'm using OpenJDK 11.0.3 and Windows 10. My C program is compiled with the Microsoft cl.exe 19.16.27031.1 for x64 (Visual Studio 2017).
One possibility, as others have already mentioned, is to create a shared library (.dll) and call it from the native code and from Java to exchange data.
However, if you want to callback to a C function defined in the native code in the same module as the one the JVM originally created, you can use RegisterNatives.
Simple Example
C program creates JVM
it calls a Main of a class
the Java Main calls back a C function named connect0 in the calling C code
to have a test case the native C function constructs a Java string and returns it
the Java side prints the result
Java
package com.software7.test;
public class Main {
private native String connect0() ;
public static void main(String[] args) {
Main m = new Main();
m.makeTest(args);
}
private void makeTest(String[] args) {
System.out.println("Java: main called");
for (String arg : args) {
System.out.println(" -> Java: argument: '" + arg + "'");
}
String res = connect0(); //callback into native code
System.out.println("Java: result of connect0() is '" + res + "'"); //process returned String
}
}
C Program
One can create the Java VM in C as shown here
(works not only with cygwin but still with VS 2019) and then register with RegisterNatives native C callbacks. So using the function invoke_class from the link above it could look like this:
#include <stdio.h>
#include <windows.h>
#include <jni.h>
#include <stdlib.h>
#include <stdbool.h>
...
void invoke_class(JNIEnv* env) {
jclass helloWorldClass;
jmethodID mainMethod;
jobjectArray applicationArgs;
jstring applicationArg0;
helloWorldClass = (*env)->FindClass(env, "com/software7/test/Main");
mainMethod = (*env)->GetStaticMethodID(env, helloWorldClass, "main", "([Ljava/lang/String;)V");
applicationArgs = (*env)->NewObjectArray(env, 1, (*env)->FindClass(env, "java/lang/String"), NULL);
applicationArg0 = (*env)->NewStringUTF(env, "one argument");
(*env)->SetObjectArrayElement(env, applicationArgs, 0, applicationArg0);
(*env)->CallStaticVoidMethod(env, helloWorldClass, mainMethod, applicationArgs);
}
jstring connect0(JNIEnv* env, jobject thiz);
static JNINativeMethod native_methods[] = {
{ "connect0", "()Ljava/lang/String;", (void*)connect0 },
};
jstring connect0(JNIEnv* env, jobject thiz) {
printf("C: connect0 called\n");
return (*env)->NewStringUTF(env, "Some Result!!");
}
static bool register_native_methods(JNIEnv* env) {
jclass clazz = (*env)->FindClass(env, "com/software7/test/Main");
if (clazz == NULL) {
return false;
}
int num_methods = sizeof(native_methods) / sizeof(native_methods[0]);
if ((*env)->RegisterNatives(env, clazz, native_methods, num_methods) < 0) {
return false;
}
return true;
}
int main() {
printf("C: Program starts, creating VM...\n");
JNIEnv* env = create_vm();
if (env == NULL) {
printf("C: creating JVM failed\n");
return 1;
}
if (!register_native_methods(env)) {
printf("C: registering native methods failed\n");
return 1;
}
invoke_class(env);
destroy_vm();
getchar();
return 0;
}
Result
Links
Creating a JVM from a C Program: http://www.inonit.com/cygwin/jni/invocationApi/c.html
Registering Native Methods: https://docs.oracle.com/en/java/javase/11/docs/specs/jni/functions.html#registering-native-methods
System.loadLibrary() is essential for the jni lookup to work. You also have a more flexible System.load() alternative.
Make sure that the native method implementation is declared with extern "C" and is not hidden by linker.

Java JVMTI Agent crashes on GetEnv()

I'm at a total loss here. I'm trying to get a JVMTI agent library running but it keeps crashing for some reason.
I've narrowed it down this line:
(*jvm)->GetEnv(jvm, (void**)jvmti, JVMTI_VERSION_1_0);
this is the full code of the agent lib (in C):
#include <jvmti.h>
#include <stdlib.h>
jvmtiEnv* jvmti = NULL;
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{
printf("Agent started.\n");
_flushall();
jint err = (*jvm)->GetEnv(jvm, (void**)jvmti, JVMTI_VERSION_1_0);
if (err != JNI_OK)
{
printf("Failed to get JVMTI env!\n");
_flushall();
return err;
}
return JNI_OK;
}
JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved)
{
return JNI_OK;
}
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
}
As I tried to isolate what the issue was I wrote a very simple java app to test
this with:
public class Test
{
public static void main(String[] args)
{
System.out.println("Hello from java!");
}
}
If I run this from netbeans with the VM arg -agentpath set to my .dllcontaining the code above, the app seems to crash when it tries to call GetEnv().
I've made sure of the following things:
- The JVM and the dll are both 64bit.
- The library is most definitely being found and loaded (the printf output is visible before the crash.)
I don't know what else could probably be causing this, do I have to link against some JVMTI API lib that I don't know about?
Or could this be an issue with the java installation on my PC?
Thanks
You should be passing address of jvmti to GetEnv() as in:
jint err = (*jvm)->GetEnv(jvm, (void**) &jvmti, JVMTI_VERSION_1_0);

what is wrong with this call to the java method?

I am trying to call a Java method from the code. C code listens to either Escape, Shift, Ctrl key press, then it calls the Java method telling which key was pressed. Following are the snippets that play a role in this.
C Snippet:
mid = (*env)->GetMethodID(env,cls,"callBack","(Ljava/lang/String;)V");
Env = env;
if(called)
switch(param) {
case VK_CONTROL:
printf("Control pressed !\n");
(*Env)->CallVoidMethodA(Env,Obj,mid,"11"); // calling the java method
break;
case VK_SHIFT:
printf("Shift pressed !\n");
(*Env)->CallVoidMethodA(Env,Obj,mid,"10"); // calling the java method
break;
case VK_ESCAPE:
printf("Escape pressed !\n");
(*Env)->CallVoidMethodA(Env,Obj,mid,"1B"); // calling the java method
break;
default:
printf("The default case\n");
break;
}
Java Snippet:
public void callBack(String key) {
String x = KeyEvent.getKeyText(Integer.parseInt(key, 16));
System.out.println(x);
}
When I run the program and press the Escape key I get this on the console:
Escape pressed !
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x5c8b809a, pid=7588, tid=8088
#
# JRE version: 7.0
# Java VM: Java HotSpot(TM) Client VM (20.0-b01 mixed mode, sharing windows-x86 )
# Problematic frame:
# V [jvm.dll+0x19809a]
#
# An error report file with more information is saved as:
# W:\UnderTest\NetbeansCurrent\KeyLoggerTester\build\classes\hs_err_pid7588.log
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
#
I know I am calling the Java function the wrong way, but I don't know where I am wrong. As from the output, it satisfies the case when I press the Escape key and then an unexpected error occurs.
Link to the LOG FILE
EDIT:
After the answer by mavroprovato I still get the same errors.
I edited this way:
(*Env)->CallVoidMethodA(Env,Obj,mid,(*Env)->NewStringUTF(Env,"1B"));
EDIT:
COMPLETE CODE version 1
COMPLETE CODE version 2
The JVM is crashing because the JNIEnv that is used is not a valid one. There are other issues with the code as well.
The Sun JNI documentation is providing very good information regarding threads.
Here comes some parts that are obvious:
Create a JNI_OnLoad function in your code. It will be called when the library is loaded. Then cache the JavaVM pointer because that is valid across threads. An alternative is to call (*env)->GetJavaVM in the initializeJNIVars function but I prefer the first one.
In your initializeJNIVars you can save the obj reference by calling Obj = (*env)->NewGlobalRef(obj).
In the LowLevelKeyboardProc you will have to get the env pointer:
AttachCurrentThread(JavaVM *jvm, JNIEnv &env, NULL);
Edit
OK, here are the code that you should add to get it working, I have tried it myself and it works. NB: I have not analyzed what your code is actually doing so I just did some fixes to get it working.
Add these variables among your other global variables:
static JavaVM *javaVM = NULL;
static jmethodID callbackMethod = NULL;
static jobject callbackObject = NULL;
You can remove your cls, mid, Env and Obj variables and use mine instead.
Create the JNI_OnLoad method where you cache the JavaVM pointer:
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
JNIEnv *env = 0;
if ((*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_4)) {
return JNI_ERR;
}
javaVM = jvm;
return JNI_VERSION_1_4;
}
Alter your initializeJNIVars to look like the following:
void Java_keylogger_TestKeys_initializeJNIVars(JNIEnv *env, jobject obj) {
jclass cls = (*env)->GetObjectClass(env,obj);
callbackMethod = (*env)->GetMethodID(env, cls, "callBack", "(Ljava/lang/String;)V");
callbackObject = (*env)->NewGlobalRef(env, obj);
if(cls == NULL || callbackMethod == NULL) {
printf("One of them is null \n");
}
called = TRUE;
}
And finally in your LowLoevelKeyboardProc code you will have to add the following:
...
WPARAM param = kbhook->vkCode;
JNIEnv *env;
jint rs = (*javaVM)->AttachCurrentThread(javaVM, (void**)&env, NULL);
if (rs != JNI_OK) {
return NULL; // Or something appropriate...
}
...
case VK_ESCAPE:
printf("Escape pressed !\n");
jstring message = (*env)->NewStringUTF(env, "1B");
(*env)->CallVoidMethod(env, callbackObject, callbackMethod, message);
break;
...
In your unregisterWinHook you should delete the global reference so that objects can be GC'd.
...
(*env)->DeleteGlobalRef(env, callbackObject);
And that's it.
I believe you cannot call a java method that takes a String parameter and pass it a char*. You should call NewStringUTF first.
I think it is due to the UAC feature enabled on your Operating System. This was a bug for Java 6. Read this for further reference.
The reason I say this is because the event to the escape key is fired correctly and the problem only begins as soon as the call to the java method is done.

Programmatically (in Java) keep OS X from going asleep

OS X Lion still goes to sleep, even if programmatic mouse "wiggles" are being periodically issued from a Java Robot (this is a standard approach to keeping machines from going to sleep and is documented elsewhere in stackoverflow, and it worked for me prior to OS X Lion).
Can anyone suggest an alternative approach, which does not require permission escalation, of keeping the machine awake from a Java application?
((The fallback solution is obviously to change the Energy Saving preferences so that the machine never sleeps, but we'd like to be smarter than that because requiring the machine to stay awake is the exception rather than the rule)).
It turns out that it is no longer possible to do this with pure-Java hacks, and one must resort to implementing a JNI I/O listener, the code of which would contain something like
http://developer.apple.com/library/mac/#qa/qa1340/_index.html
An alternative, if you can tolerate calling a command line tool, is to call pmset noidle in a daemon thread.
Do you need to prevent display sleep or system sleep? If the latter, you can prevent system sleep with disk activity - e.g. read from or write to a file.
Hard disk activity does not prevent display sleep, but it does prevent
system sleep. It is not uncommon for the display to sleep before the
system goes to sleep if both types of sleep are set to occur after the
same length of inactivity.
http://support.apple.com/kb/ht1776
This answer points to Objective-C code that can be used to prevent sleep, using the UpdateSystemActivity call. Could you create a simple JNI wrapper on this which you could invoke as a native method from your Java code?
It also seems possible to disable and re-enable sleep using applescript. Could you invoke such a script using Runtime.exec() from your Java code?
Following JNI solution works for macOS 10.5 / GCC
I. Definition Java Native Interface
// File KalleInterface.java
public class KalleInterface
{
static
{
// java.library.path set to libKalleInterface.dylib
System.loadLibrary("KalleInterface");
}
public static native boolean preventSleep();
public static native boolean allowSleep();
}
II. Generate Equivalent C++ Interface
Assume compliled java class file is ./bin/KalleInterface.class
javah -jni -d "${PWD}" -classpath "${PWD}/bin" KalleInterface
generates a C++ header KalleInterface.h as
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class KalleInterface */
#ifndef _Included_KalleInterface
#define _Included_KalleInterface
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: KalleInterface
* Method: preventSleep
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_KalleInterface_preventSleep(JNIEnv *, jclass);
/*
* Class: KalleInterface
* Method: allowSleep
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_KalleInterface_allowSleep(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
III. Implement C++ Interface
#include "esc_util_KalleInterface.h"
#include <IOKit/pwr_mgt/IOPMLib.h>
// IOPMAssertionCreateWithName ...
// kIOPMAssertionTypeNoDisplaySleep prevents display sleep,
// kIOPMAssertionTypeNoIdleSleep prevents idle sleep
//reasonForActivity is a descriptive string used by the system whenever it needs
// to tell the user why the system is not sleeping. For example,
// "Mail Compacting Mailboxes" would be a useful string.
// IOPMAssertionDeclareUserActivity ...
// claims user activity
#ifdef __cplusplus
extern "C" {
#endif
CFStringRef reasonForActivity= CFSTR("User Activity Type");
// NOTE: IOPMAssertionCreateWithName limits the string to 128 characters.
IOPMAssertionID assertionID;
static bool active = false;
/*
* Class: KalleInterface
* Method: preventSleep
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_esc_util_KalleInterface_preventSleep(JNIEnv*, jclass)
{
IOReturn success = 0;
if (active)
{
return (jboolean)true;
}
//success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
//success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
success = IOPMAssertionDeclareUserActivity(reasonForActivity, kIOPMUserActiveLocal, &assertionID);
active =(success == kIOReturnSuccess);
return (jboolean)active;
//Add the work you need to do without
// the system sleeping here.
}
/*
* Class: KalleInterface
* Method: allowSleep
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_esc_util_KalleInterface_allowSleep(JNIEnv *, jclass)
{
IOReturn success = 0;
if (!active)
{
return (jboolean)true;
}
success = IOPMAssertionRelease(assertionID);
active = !(success == kIOReturnSuccess);
//The system will be able to sleep again.
return (jboolean)(!active);
}
#ifdef __cplusplus
}
#endif
IV. Build Shared Library
Command line steps for building libKalleInterface.dylib are
g++ -c -fPIC -I ${JAVA_HOME}/include -I ${JAVA_HOME}/include/darwin -o KalleInterfaceCPP.o KalleInterfaceCPP.cpp
g++ -dynamiclib -framework CoreFoundation -framework IOKit -o libKalleInterface.dylib KalleInterfaceCPP.o -lc
V. Test Class
// File KalleTest.java
public class KalleTest
{
public static void main(String...args)
{
boolean ok1 = KalleInterface.preventSleep();
System.out.println(ok1);
try
{
Thread.sleep(60*10*1000);
}
catch (Exception x)
{
x.printStackTrace();
}
boolean ok2 = KalleInterface.allowSleep();
System.out.println(ok2);
}
}
Assuming compiled class is ./bin/KalleTest.class and shared library is ./libKalleInterface.dylib, test is invoked by
java -Djava.library.path=${PWD} -classpath ./bin KalleTest
Expected behaviour: Mac remains active for 10 minutes, each of preventSleep(), allowSleep() produce result/output true.

Categories

Resources