user1443721
user1443721

Reputation: 1260

Android C/C++ native calls Java APIs

There are lots of examples that Android C/C++ native calls Java APIs. However, all of these examples I have read are the Android Java APIs call native first and then the native calls other Java APIs by using the passed JNI-ENV.

Without the passed JNI-ENV, how can the C/C++ get it? Is it possible that C/C++ native calls Java APIs without JNI-ENV?

Can you give an example or a link for it if it is possible?

Thanks!

Upvotes: 1

Views: 1609

Answers (1)

Tomasz W
Tomasz W

Reputation: 2086

You need to include jni.h first. This brings a ton of useful calls; with newer android releases you'll also need JniInvocation.h. To enable this:

LOCAL_C_INCLUDES += ${JNI_H_INCLUDE}

That certainly works with source tree, not sure about NDK, but should be fine, too.

Second, pretty important thing is to have proper signal chain lib selected. Art or Dalvik will load libsigchain.so, which is a stub and abort()s every time any of its methods are being called. On Android it's done with a little hack: local symbols are being exported to global symbol table, so that Art picks exec's symbols instead of loading shared lib. Here's how it's done:

# Enable native helper calls
LOCAL_SHARED_LIBRARIES += libnativehelper

# Include all of the Android's libsigchain symbols
LOCAL_WHOLE_STATIC_LIBRARIES += libsigchain

# Export only libsigchain symbols to global symbol table.
LOCAL_LDFLAGS += \
    -Wl,--export-dynamic \
    -Wl,--version-script,art/sigchainlib/version-script.txt

Link your executable now. Double check that the required symbols are indeed exported:

% readelf -a <output_binary> | grep InitializeSignalChain
654: 0002cab1   211 FUNC    GLOBAL PROTECTED   12 InitializeSignalChain

Done? things get simpler now:

  1. Initialize the JNI (so that your code uses proper VM)

    JniInvocation invocation;
    if (invocation.Init(nullptr)) return;
    
  2. Create JavaVM:

    JavaVM* vm;
    JNIEnv* env;
    JavaVMInitArgs* args;
    
    args.version = JNI_VERSION_1_4;  // _5, _6
    args.options = nullptr;
    args.nOptions = 0;
    args.ignoreUnrecognized = JNI_FALSE;
    
    if (JNI_CreateJavaVM(&vm, &env, &args) < 0) return;
    
  3. At this point your vm and env are ready to use. have fun.

  4. Check for exceptions, if any

    if (env->ExceptionCheck()) {
        // ...
    }
    
  5. When you're done, clean up

    vm->DetachCurrentThread();
    vm->DestroyJavaVM();
    

More interesting stuff can be found here and DalvikVM Main is probably the best source of knowledge. Good luck!

Upvotes: 3

Related Questions