How send the byteArray from Native C to my Android app - android

I am developing a Native application name test.c and I want to return the arrayofByte from the native C file ,I able to compile and the .so file is generate when i run my Application
05-08 13:04:08.477: D/dalvikvm(945): No JNI_OnLoad found in /data/data/com.ssg.nativelibtest/lib/libnativelibtest.so 0x45f3da78, skipping init
getting this message with out any crash in my application so how to resolve the calling of Nativefile.
for the reference I am giveing my test.c file
this is file written in C I am calling the .so file Java appliaction
jbyteArray Java_com_ssg_nativelibtest_MainActivity_getEncryptionKey(JNIEnv * env, jobject obj)
{
unsigned char ukey[] = { 'H','A','R','D','C','O','D','E','D',' ','K','E','Y','1','2','3'};
int lengthOfArray = (*env)->GetArrayLength(env, ukey);
//jsize lengthOfArray =(*env)->GetArrayLength(env, ukey);
jbyteArray byteKey = (*env)->NewByteArray(env, lengthOfArray);
(*env)->SetByteArrayRegion(env, byteKey, 0, lengthOfArray, (jbyte *)ukey);
return byteKey;
}

See documentation:
The JNI_OnLoad function should look something like this if written in
C++:
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
// Get jclass with env->FindClass.
// Register methods with env->RegisterNatives.
return JNI_VERSION_1_6;
}

Related

Load android native libraries (JNI) in a native c program

I'm gonna write a c program that loads a native android library (.so) this is my code:
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
typedef void (*target_func)(JNIEnv* env, jobject obj, int x);
int main(int argc, char ** argv) {
char *lib = "/path/to/lib.so"
void *handle = dlopen(lib, RTLD_LAZY);
if (NULL == handle) {
printf("load library error\n");
return 1;
}
void *offset_func = dlsym(handle, "Java_com_example_test_MainActivity_myFunc");
if (NULL == offset_func) {
printf("getprocaddress error\n");
return 1;
}
target_func target = (target_func)((unsigned char *)offset_func);
target(nullptr, nullptr, 10); // Here i need to pass JNIEnv pointer
return 0;
}
The only thing that i need is to pass the JNIEnv (the first parameter), Is there any way ??
My program runs on android emulator and because of that i tried to resolve JNIEnv from libart.so but i wasn't success to disassemble libart.so in IDA Pro.
I know when we launch an apk in our device, the ART (or Dalvik) creates JNIEnv for the app but i don't know how can i emulate and create JNIEnv similar to ART.

How to properly and simply use callbacks in JNI NDK Android

I am trying to have Java call a C function in Android and some caveats that I am met with is the documentation for callbacks in general seem very unclear and most answers on SO are just talking about theory and posting broken links with no actual code. So please post code in your answer.
I am trying to create a joystick in java and have it send input to the NDK to perform actions.
if I can just have Java call a C function and pass a int to it ,
that would be good. but I think that wont work because C in running
it's own main loop.
perhaps I need to use C to call a java
function to get controller state and execute code from there.
I did manage to find some code Here but it seems the code while it does compile by itself, but doesn't appear to work when moved to modern code .
How should I implement call backs in a Java C relationship on Android? It seems that from the code example above that it is C that is calling Java functions and these need to be used like getters functions.
Here is some code that I have and I appear to be missing somethings because it is unclear to me how I should have these call backs performed. I will mark this code with function name and where is it located, if java or C++ (CPP) .
I think there are two ways that I would like this to work.
My Javafunction that I want to call
public int Cat(){
Log.e("JniHandler", "CAT WAS CALLED!!!");
return 333;
}
Structure information CPP . Not sure if it is needed.
#include <string.h>
#include <inttypes.h>
#include <pthread.h>
#include <jni.h>
#include <android/log.h>
#include <assert.h>
typedef struct tick_context {
JavaVM *javaVM;
jclass jniHelperClz;
jobject jniHelperObj;
jclass mainActivityClz;
jobject mainActivityObj;
pthread_mutex_t lock;
int done;
} TickContext;
TickContext g_ctx;
This section works. It looks that as soon as the JVM is launched, this will initialize.
JNI_OnLoad CPP
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
memset(&g_ctx, 0, sizeof(g_ctx));
SDL_Log(" DEBUG -- JNI INIT!!!");
g_ctx.javaVM = vm;
if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR; // JNI version not supported.
}
jclass clz = env->FindClass("org/foo/bar/mainactivity");
g_ctx.jniHelperClz = (jclass)env->NewGlobalRef(clz); // hack
jmethodID jniHelperCtor = env->GetMethodID(g_ctx.jniHelperClz,"<init>", "()V");
jobject handler = env->NewObject(g_ctx.jniHelperClz, jniHelperCtor);
g_ctx.jniHelperObj = env->NewGlobalRef(handler);
//queryRuntimeInfo(env, g_ctx.jniHelperObj); // This when getting version returns null.
g_ctx.done = 0;
g_ctx.mainActivityObj = NULL;
return JNI_VERSION_1_6;
}
queryRuntimeInfo CPP
void queryRuntimeInfo(JNIEnv *env, jobject instance) {
SDL_Log("DEBUG QUERYRUNTIME INTO");
// Find out which OS we are running on. It does not matter for this app
// just to demo how to call static functions.
// Our java JniHelper class id and instance are initialized when this
// shared lib got loaded, we just directly use them
// static function does not need instance, so we just need to feed
// class and method id to JNI
jmethodID versionFunc = env->GetStaticMethodID(g_ctx.jniHelperClz, "getBuildVersion", "()Ljava/lang/String;"); // This returns null.
if (!versionFunc) {
SDL_Log("DEBUG versionFunc INTO");
//LOGE("Failed to retrieve getBuildVersion() methodID # line %d",__LINE__);
return;
}
SDL_Log("DEBUG BUILD VERSION INTO");
jstring buildVersion = (jstring)env->CallStaticObjectMethod(g_ctx.jniHelperClz, versionFunc); // hack
const char *version = env->GetStringUTFChars(buildVersion, NULL);
if (!version) {
SDL_Log("DEBUG buildVersion INTO");
//LOGE("Unable to get version string # line %d", __LINE__);
return;
}
//LOGI("Android Version - %s", version);
SDL_Log("DEBUG ANDROID VERSION %s", version);
env->ReleaseStringUTFChars(buildVersion, version);
// we are called from JNI_OnLoad, so got to release LocalRef to avoid leaking
env->DeleteLocalRef(buildVersion);
// Query available memory size from a non-static public function
// we need use an instance of JniHelper class to call JNI
jmethodID memFunc = env->GetMethodID(g_ctx.jniHelperClz, "getRuntimeMemorySize", "()J");
if (!memFunc) {
//LOGE("Failed to retrieve getRuntimeMemorySize() methodID # line %d",__LINE__);
SDL_Log("DEBUG Failed to retrieve memory size");
return;
}
jlong result = env->CallLongMethod(instance, memFunc);
//LOGI("Runtime free memory size: %" PRId64, result);
SDL_Log("DEBUG RUN TIME FREE MEMORY SIZE");
(void)result; // silence the compiler warning
}
This function isn't ever called and I am not sure who is the caller. CPP
extern "C" JNIEXPORT void JNICALL InvokeFunction(JNIEnv *env, jobject instance) {
SDL_Log("JNI CALL START TICKET");
pthread_t threadInfo_;
pthread_attr_t threadAttr_;
pthread_attr_init(&threadAttr_);
pthread_attr_setdetachstate(&threadAttr_, PTHREAD_CREATE_DETACHED);
pthread_mutex_init(&g_ctx.lock, NULL);
jclass clz = env->GetObjectClass(instance);
g_ctx.mainActivityClz = (jclass)env->NewGlobalRef(clz); // hack
g_ctx.mainActivityObj = env->NewGlobalRef(instance);
int result = pthread_create( &threadInfo_, &threadAttr_, UpdateTicks, &g_ctx);
assert(result == 0);
pthread_attr_destroy(&threadAttr_);
(void)result;
}
updateticks CPP // This code is never actually called.
void* UpdateTicks(void* context) {
SDL_Log("DEBUG -- UPDATE TICKS IS CALLED");
TickContext *pctx = (TickContext*) context;
JavaVM *javaVM = pctx->javaVM;
JNIEnv *env;
jint res = javaVM->GetEnv((void**)&env, JNI_VERSION_1_6);
if (res != JNI_OK) {
res = javaVM->AttachCurrentThread(&env, NULL);
if (JNI_OK != res) {
//LOGE("Failed to AttachCurrentThread, ErrorCode = %d", res);
SDL_Log("FAILED TO ATTACH THREAD");
return NULL;
}
}
jmethodID statusId = env->GetMethodID(pctx->jniHelperClz, "updateStatus", "(Ljava/lang/String;)V");
//sendJavaMsg(env, pctx->jniHelperObj, statusId, "TickerThread status: initializing...");
// get mainActivity updateTimer function
jmethodID timerId = env->GetMethodID(pctx->mainActivityClz, "updateTimer", "()V");
struct timeval beginTime, curTime, usedTime, leftTime;
const struct timeval kOneSecond = {
(__kernel_time_t)1,
(__kernel_suseconds_t) 0
};
//sendJavaMsg(env, pctx->jniHelperObj, statusId, "TickerThread status: start ticking ...");
while(1) {
gettimeofday(&beginTime, NULL);
pthread_mutex_lock(&pctx->lock);
int done = pctx->done;
if (pctx->done) {
pctx->done = 0;
}
pthread_mutex_unlock(&pctx->lock);
if (done) {
break;
}
env->CallVoidMethod(pctx->mainActivityObj, timerId);
gettimeofday(&curTime, NULL);
timersub(&curTime, &beginTime, &usedTime);
timersub(&kOneSecond, &usedTime, &leftTime);
struct timespec sleepTime;
sleepTime.tv_sec = leftTime.tv_sec;
sleepTime.tv_nsec = leftTime.tv_usec * 1000;
if (sleepTime.tv_sec <= 1) {
nanosleep(&sleepTime, NULL);
} else {
//sendJavaMsg(env, pctx->jniHelperObj, statusId, "TickerThread error: processing too long!");
}
}
//sendJavaMsg(env, pctx->jniHelperObj, statusId, "TickerThread status: ticking stopped");
javaVM->DetachCurrentThread();
return context;
}

Getting error while GetEnv in JNI_OnLoad() and app crashing when removed these lines, error goes away

App is crashing in JNI_OnLoad(). I have checked the status it is zero. Still, the app is crashing. I want to create env variable to cache method IDs to use them for callback for java functions. I have tried caching jvm in context struct and doing same in callback() method but App is crashing. I am new bie to this concept. Can anybody explain what I am missing here.
Please find Attached code:
#include <jni.h>
#include <string>
#include "Numbers.h"
#include <android/log.h>
#define LOG_TAG "logs"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
typedef struct number_ctx{
JavaVM *javaVM;
jclass mathsClass;
jobject mathsObj;
jclass mainActivityClass;
jobject mainActivityObj;
pthread_mutex_t lock;
} NumbersCtx;
NumbersCtx g_ctx;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
memset(&g_ctx, 0, sizeof(g_ctx));
g_ctx.javaVM = vm;
int status = vm ->GetEnv((void **)env, JNI_VERSION_1_6);
LOGD("Status is %d ", status);
if (vm ->GetEnv((void **)env, JNI_VERSION_1_6) != JNI_OK) {
LOGD("Some error");
return JNI_ERR; // JNI version not supported.
}
if(env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
/*
jclass clz = env->FindClass(
"com/example/maths/Maths1");
g_ctx.mathsClass = static_cast<jclass>(env->NewGlobalRef(clz));
jmethodID jniHelperCtor = env->GetMethodID(g_ctx.mathsClass,
"printMessage", "()V");
jobject handler = env->NewObject(g_ctx.mathsClass,
jniHelperCtor);
g_ctx.mathsObj = env->NewGlobalRef(handler);
g_ctx.mainActivityObj = NULL;*/
return JNI_VERSION_1_6;
}
void callback() {
}
You forgot to use the Address-Of operator (&) when calling GetEnv. GetEnv expects a pointer-to-a-pointer, but you're just passing it a pointer that you've cast to a pointer-to-a-pointer, which is not the same thing.
So instead of
vm ->GetEnv((void **)env, JNI_VERSION_1_6)
you should be using
vm ->GetEnv((void **)&env, JNI_VERSION_1_6)
The end result of leaving out the Address-Of operator in this case is that GetEnv will store the JNIEnv* who-knows-where, and your env variable most likely won't contain a valid JNIEnv*.

How to call JNI Function from a method in C library

I'm currently developping an android app with android studio, for this project i need to use a custom library written in c/c++ .
So i in order to do that i needed to use NDK.
The library contain methods that i need to implements in order to have access to specifics fonctions in android
My question is : how can I call my jni method inside the method existing in c
which will call a java method to store session key in the android system
So for a pratical exemple since it's maybe not clear:
In a file called lib_exemple.c i have three methods
the first one is the initialisation call to the library (jni to library c) -->working
JNIEXPORT void JNICALL
Java_com_example_libExemple_Libs_ExempleLib_lib_1Exemple_1init
(JNIEnv *env, jobject instance) {
lib_Example_init('0'); // c func of the library
}
then the second one is the jni who call a java method (jni to java) -> working
JNIEXPORT jint
JNICALL Java_com_example_libExemple_Libs_ExempleLib_lib_1Exemple_1store_1session_1key
(JNIEnv * env, jobject jobject1, jbyte jbyte1, jchar jchar1, jbyte jbyte2){
jclass clazz = (*env)->FindClass(env, "com/example/libExemple/Libs/ExempleLib");
jmethodID mCurrentActivityId = (*env)->GetMethodID(env, clazz, "KeyStoreSessionKey", "(BCB)I");
jint result = (*env)->CallIntMethod(env, jobject1, mCurrentActivityId, jbyte1, jchar1, jbyte2);
return result;
}
and the third method is the one the library c have in it (library C to jni)
int lib_Exemple_store_session_key(uint8_t Session, P_KEY_ST_T pKey, uint8_t keyType) {
//i want to call jni func here , so the librairy can access the native android function
return 0;
}
then to configure the ndk in a file called ExempleLib.java i have defined
static {
System.loadLibrary("LibExemple");
}
then the prototype of the initialisation of the library
public native void libExemple_init();
the prototype of the first method in lib_exemple.c
public native int lib_Exemple_store_session_key(byte pKey, char key_length, byte keyIndex);
and the java fonction called by it
protected int KeyStoreSessionKey(byte pKey, char key_length, byte keyIndex) {
...
}
my mainActivity contain
ExempleLib LibFunc = new ExempleLib();
Log.d(TAG, "init lib" );
LibFunc.lib_Exemple_init();
My goal is that after initialising my library and call an init function , it can call the first method in the second method of lib_exemple.c (the one used in the library )
thanks
EDIT :
The solution of my problem is when i initialize the lib , i need to save jvm
so i can access the JNIenv in the library method directly
jobject Savedinstance = NULL;
static JavaVM *cachedJVM;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
cachedJVM = jvm;
return JNI_VERSION_1_6;
}
JNIEnv *AttachJava() {
JavaVMAttachArgs args = {JNI_VERSION_1_6, 0, 0};
JNIEnv *java;
(*cachedJVM)->AttachCurrentThread(cachedJVM, &java, &args);
return java;
}
JNIEXPORT jint JNICALL
Java_com_example_vgosselin_libExemple_Libs_ExempleLib_lib_1init(JNIEnv *env,
jobject instance) {
Savedinstance = instance;
void *element = 0;
jint result;
result = lib_Exemple_init_();
return result;
}
so i can call the java method in
int lib_Exemple_store_session_key(uint8_t Session, P_KEY_ST_T pKey, uint8_t keyType) {
JNIEnv *NewEnv = AttachJava();
jclass clazz = (*NewEnv)->FindClass(NewEnv,"com/example/vgosselin/libExemple/Libs/ExempleLib");
jmethodID mCurrentActivityId = (*NewEnv)->GetMethodID(NewEnv,clazz,"KeystoreStoreKey","([BCB)I");
jint result;
jbyteArray Data = 0;
jchar Key = 0;
jbyte Type = 0;
result = (*NewEnv)->CallIntMethod(NewEnv, Savedinstance, mCurrentActivityId, Data, Key, Type);
return result;
}
and now i don't need the method in jni syntax anymore
JNIEXPORT jint JNICALL Java_com_example_libExemple_Libs_ExempleLib_lib_1Exemple_1store_1session_1key

App's path for external storage in native code

I am trying to write a native library for my application so that i can do all file operation in the native code. I read that getExternalStorageDirectory() give the path of the external storage of directory.
My question is how can i access the same without hard-coding the location to some string? Is there any function in android ndk that can give the same function as getExternalStorageDirectory() of java in C++ code?
JNI is your friend, and this isn't too complicated, as getExternalStorageDirectory is a static method. This function gets the value, and changes the working directory to it, for good measure.
#include <jni.h>
#include <unistd.h> // chdir()
#include <sys/param.h> // MAXPATHLEN
// To call Java methods when running native code inside an Android activity,
// a reference is needed to the JavaVM.
static JavaVM *gJavaVM;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
gJavaVM = vm;
return JNI_VERSION_1_6;
}
int cdToExtStorage(void) {
// Make JNI calls to get the external storage directory, and cd to it.
// To begin, get a reference to the env and attach to it.
JNIEnv *env;
int isAttached = 0;
int ret = 0;
jthrowable exception;
if (((*gJavaVM)->GetEnv(gJavaVM, (void**)&env, JNI_VERSION_1_6)) < 0) {
// Couldn't get JNI environment, so this thread is native.
if (((*gJavaVM)->AttachCurrentThread(gJavaVM, &env, NULL)) < 0) {
fprintf(stderr, "Error: Couldn't attach to Java VM.\n");
return (-1);
}
isAttached = 1;
}
// Get File object for the external storage directory.
jclass classEnvironment = (*env)->FindClass(env, "android/os/Environment");
if (!classEnvironment) goto bailAndroid;
jmethodID methodIDgetExternalStorageDirectory = (*env)->GetStaticMethodID(env, classEnvironment, "getExternalStorageDirectory", "()Ljava/io/File;"); // public static File getExternalStorageDirectory ()
if (!methodIDgetExternalStorageDirectory) goto bailAndroid;
jobject objectFile = (*env)->CallStaticObjectMethod(env, classEnvironment, methodIDgetExternalStorageDirectory);
exception = (*env)->ExceptionOccurred(env);
if (exception) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
}
// Call method on File object to retrieve String object.
jclass classFile = (*env)->GetObjectClass(env, objectFile);
if (!classFile) goto bailAndroid;
jmethodID methodIDgetAbsolutePath = (*env)->GetMethodID(env, classFile, "getAbsolutePath", "()Ljava/lang/String;");
if (!methodIDgetAbsolutePath) goto bailAndroid;
jstring stringPath = (*env)->CallObjectMethod(env, objectFile, methodIDgetAbsolutePath);
exception = (*env)->ExceptionOccurred(env);
if (exception) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
}
// Extract a C string from the String object, and chdir() to it.
const char *wpath3 = (*env)->GetStringUTFChars(env, stringPath, NULL);
if (chdir(wpath3) != 0) {
fprintf(stderr, "Error: Unable to change working directory to %s.\n", wpath3);
perror(NULL);
} else if (path) {
if (chdir(path) != 0) {
fprintf(stderr, "Error: Unable to change working directory to %s.\n", path);
perror(NULL);
}
}
(*env)->ReleaseStringUTFChars(env, stringPath, wpath3);
goto retAndroid;
bailAndroid:
fprintf(stderr, "Error: JNI call failure.\n");
ret = -1;
retAndroid:
if (isAttached) (*gJavaVM)->DetachCurrentThread(gJavaVM); // Clean up.
return (ret);
}
I'm not sure the existence of that function, but I think you can achieve it by reading /proc/mounts then get info of external storage,e.g /storage/sdcardx on JellyBean, mnt/sdcardx on older versions. You can check in *.rc file, maybe it can be defined a symlink for backward compatiblility. There exist another environment variable which is used to define external storage, EXTERNAL_STORAGE so you can try getenv(EXTERNAL_STORAGE) to get mount point. Hope it can help some ways.

Categories

Resources