JNI - transfer from jstring to byte, from byte to string issue - android

I have a problem with converting in JNI.
In C++ I'm creating some cipher using AES (Library CryptoPP). I'm converting result to string and returning it. This is how the code getting the string looks like:
JNIEXPORT jbyteArray JNICALL Java_com_example_androidake_MutualAuthenticateChip_prepareEncryptionCPP
(JNIEnv *env, jobject thisObj, jboolean hmm, jboolean jinit) {
string encryption= mac->EncryptCertKey();
jbyteArray returns = env->NewByteArray(encryption.size());
env->SetByteArrayRegion(returns, 0, encryption.length(), (jbyte*) encryption.c_str());
return returns;
};
Above string is converting to jbyteArray which is returned. First I wanted just return string using
env->NewStringUTF(encryption.c_str());
but the application has been crashing. I think it is caused by content of variable 'encryption'. I'm using env->NewStringUTF(encryption.c_str()); in other functions, where returned string is for example just a number or something like that.
Then in Java I'm doing conversion from byte to string:
byte[] cipher = mac_A.prepareEncryptionCPP(true, true);
string cipher_str = new String(cipher);
And I'm putting that string again to the C++ object and compare old cipher with the cipher which is sent from Java:
//Java
boolean result = mac_A.compareEncryption(true, cipher);
//JNI
JNIEXPORT jboolean JNICALL Java_com_example_androidake_MutualAuthenticateChip_compareEncryption
(JNIEnv * env, jobject thisObj, jboolean jinit, jstring cipher){
bool init = jinit;
bool result;
jsize length = env->GetStringUTFLength(cipher);
const char *inCStr_ek = env->GetStringUTFChars(cipher, 0);
string s(inCStr_ek, length);
result = mac->CompareCipher(s);
env->ReleaseStringUTFChars(cipher, inCStr_ek);
return result;
};
Comparing in C++ :
bool MyClass::CompareCipher(std::string cipher_2){
if(cipher == cipher_2){
return true;
}else{
return false;
}
}
And it always returns false. I do not know what I'm doing wrong. I've even sent this cipher from Java to C++ and take it back to Java and the strings are equals, but in C++ side are not.

on your java side code you have
byte[] cipher = mac_A.prepareEncryptionCPP(true, true);
boolean result = mac_A.compareEncryption(true, cipher);
compareEncryption jni function is defined with jstring , not jbytearray.
So from JNI side you are sending a byte array to java, and from java side you send back the same byte array to native side (but uses jstring in call), but then you are using env->GetStringUTFChars(cipher, 0) which converts that byte array into a modified UTF-8 string, so its technically not that same byte array anymore.
if you need strings do the conversions in java side, and just use with same plain byte arrays between jni and java. See this for string encoding issues in Android JNI.

Related

Send Array of Strings to Native Code in Android

I call a function in C++ from java. In java I have an array of Strings that I want to use in my C++-function.
I have in C++:
std::string names[6]; // Global variable
extern "C"
JNIEXPORT void JNICALL
Java_com_erikbylow_mycamera3_JNIUtils_updateStandingBoard(JNIEnv *env, jobject type, std::string *names, jint nbrElements){
memcpy(standingText, names, 6* sizeof(std::string));
nbrStandText = nbrElements;
}
In `Java`:
public static void updateStanding( String resultArray[]){
updateStandingBoard(resultArray, resultArray.length);
}
What is the simplest way of achieving what I want? When I try this and different variants it either crashes or yields nonsense data.
JNI is a primarily a C API, it doesn't know anything about std::string as you can validate by calling javah on the Java source file contained the native methods declaration.
Also Java isn't C, there is no need to pass the array size as additional parameter.
So your native void updateStandingBoard(String[] result, int size) should actually be native void updateStandingBoard(String[] result)
With this in mind, the JNI code should be
std::vector<std::string> names; // much safer or use std::array as alternative
extern "C"
JNIEXPORT void JNICALL
Java_com_erikbylow_mycamera3_JNIUtils_updateStandingBoard(JNIEnv *env, jobject type, jobjectArray names) {
jint nbrElements = env->GetArrayLength(names);
// now copy the strings into C++ world
for (int i = 0; i < nbrElements ; i++) {
// access the current string element
jobject elem = env->GetObjectArrayElement(names, i);
jsize length = env->GetStringLength(elem);
// pin it to avoid GC moving it around during the copy
const jchar *str = env->GetStringChars(elem, nullptr);
names.push_back(std::string(str, length));
// make it available again to the VM
env->ReleaseStringChars(elem, str);
}
}
This was just for the basic strings, if you are interested in UTF strings, then you should make use of std::wstring and the UTF variants of the above JNI functions.

Passing Json char to C using JNI to get Jansson dump of calculated values, Pointer problems

I have implemented Jansson in Android with C and made a function which calculates values from json and that works in C, I tried to use that code in NDK with JNI it builds with no errors, but as i tried to arrange the code to work with JNI it gives me pointer error warning: return from incompatible pointer type. I have read that i need to use jlong for pointers but i cant figure out how that works, it is my first time working in it.
This is my code from C (gives no errors and compiles)
char *doCalc (char *invoice_str) {
json_error_t error;
json_t *invoice = json_loads (invoice_str, JSON_DISABLE_EOF_CHECK, &error);
...
char *result = json_dumps (json_data, JSON_PRESERVE_ORDER);
return result;
}
C code Arranged to work with JNI (gives me error warning: return from incompatible pointer type, which if im correct is because of jchar)
JNIEXPORT jchar JNICALL *Java_com_example_test_doCalc (JNIEnv* env, jobject obj,char const *invoice_str) {
json_error_t error;
json_t *invoice = json_loads (invoice_str, JSON_DISABLE_EOF_CHECK, &error);
...
char *result = json_dumps (json_data, JSON_PRESERVE_ORDER);
return result;
}
Then in my Activity I like to would run doCalc(charJ);, charJ has Json in it. Which would then give me dump of calculated values.
Also I might be looking at this completely wrong, any help is appreciated.
Try to use jstring instead of char*
JNIEXPORT jchar JNICALL * Java_com_example_test_doCalc(JNIEnv * env, jobject obj, jstring invoice_jstring) {
//convert invoice_jstring to char* link bellow
json_error_t error;
json_t * invoice = json_loads(invoice_str, JSON_DISABLE_EOF_CHECK, & error);
...
char * result = json_dumps(json_data, JSON_PRESERVE_ORDER);
return result;
}
for conversion jstring to char* you can use this answer:
JNI converting jstring to char *

Android passing file path to OpenCV imread method

I am trying to use openCV in my android project and trying to run this native code but I don't know how to use this parameter
JNIEXPORT jint JNICALL Java_com_example_helloopencvactivity_nativecalls_filepath
(JNIEnv * env, jobject jo, jstring str1, jstring str2) {
cv::Mat img1 = cv::imread("");
}
I tried using this
const char *nativeString = (*env)->GetStringUTFChars(env, str1, 0);
cv::Mat img1 = cv::imread(nativeString);
but i am getting this error error: no matching function for call to '_JNIEnv::GetStringUTFChars
I need to pass the file path from android file system to openCV's native code for processing, the passing element is string and should be read by imread
first in the java side, my codes looks like:
private String path="/mnt/sdcard/";
InitFeature(width,height,path);
public native void InitFeature(int width, int height,String path);
then in the jni side, it's:
//passed from the java part and release after InitFreature
const char* nPath;
//to save the globle path
char* g_path;
JNIEXPORT void JNICALL Java_org_opencv_samples_tutorial6_Tutorial2Activity_InitFeature(JNIEnv* env, jobject,jint width,jint height,jstring path);
JNIEXPORT void JNICALL Java_org_opencv_samples_tutorial6_Tutorial2Activity_InitFeature(JNIEnv* env, jobject,jint width,jint height,jstring path)
{
//jsize path_size;
nPath=env->GetStringUTFChars(path,NULL);
//path_size=env->GetArrayLength(path);
LOGD("path: %s \n",nPath);
int length=strlen(nPath);
LOGD("length: %d \n",length);
g_path=new char[length+1];
memcpy(g_path,nPath,length+1);
LOGD("path_2: %s \n",g_path);
LOGD("length: %d \n",strlen(g_path));
char l_path[128];
strcpy(l_path,g_path);
strcat(l_path,"color.jpg");
LOGD("path_3: %s \n",l_path);
LOGD("length: %d \n",sizeof(l_path));
m_width=width;
m_height=height;
center.x=float (m_width/2.0+0.5);//float (Img_tmp->width/2.0+0.5);
center.y=float (m_width/2.0+0.5);//float (Img_tmp->height/2.0+0.5);
env->ReleaseStringUTFChars(path,nPath);
}
since I have different native calls, one to initiate features(shown here) and others to process every frame, and after you env->ReleaseStringUTFChars(path,nPath); the string would be invisible to jni part. I have the copy the string to a global char* g_path;
and a little sample is here as well, the file path is "/mnt/sdcard/color.jpg" and check those logs.
then you can use imread() to get this jpg.
I use other libs like libjpg, so I am not showing the codes here.

Android NDK get object from Java

I want to know how to get a custom object from Java to c++?
I need to implement a method in c++ for get performance. I already have the method working in java but I want to port to c++.
On Java a I call the method like this:
private native boolean P(Mat Previous, String Name);
On CPP file I need to get the mat object. Getting string is easy! But how can I get the custom mat object similar to c++(cv::Mat)? I need to get java Mat into a cv::Mat.
Here the cpp file:
JNIEXPORT bool JNICALL Java_br_raphael_detector_SimpsonDetector_P
(JNIEnv* env,jobject thiz, jobject Previous, jstring Name){
jboolean sim = false;
const char* N = env->GetStringUTFChars(Name,0);
std::string Nome = N;
//Release
env->ReleaseStringUTFChars(Name,N);
//Then Return
return sim;
}
A java Mat object is a totally different thing from a native cv::Mat, you can't get one directly from the other.
That said, if you know what fields are inside Mat, and you know the corresponding fields in cv::Mat, you can write a conversion function that copies the contents of the fields one-by-one.
// First get the Mat class
jclass Mat = (*env)->GetObjectClass(env, Previous);
// To get a field
jfieldId field = (*env)->GetFieldID(env, Mat, "fieldName", field type);
// To get a method
jmethodId method = (*env)->GetMethodID(env, Mat, "methodName", method signature);
from there you can read the values of fields, or call methods
// getting a field
(*env)->GetObjectField(env, Previous, field);
// calling a method
(*env)->CallObjectMethod(env, Previous, method, parameters);
refer to http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html for details
I'm one year late to the party, but the way you pass a Mat to C is with a jlong with the address of the Java's Mat. You can do something like this:
private static native boolean P(long matAddress, String name);
In C:
JNIEXPORT jboolean JNICALL Java_br_raphael_detector_SimpsonDetector_P
(JNIEnv* env,jobject thiz, jlong matAddress, jstring Name)
{
cv::Mat* image = (cv::Mat*)matAddress;
// Do stuff with image.
}
Then you would call the method in Java like this:
P(myMat.getNativeObjAddr(), name);
OpenCV exposes this method just for cases like this. (I would link to the documentation's page but the website is not loading here, sorry.)

getArrayLength() is returning a huge number...

I am trying to understand jni, so I started hacking up hellojni, and I ran into this problem.
My java code looks like this:
short[] buf = new short[16];
Log.d("hello", "before!");
write(buf, 0, 16);
and my C code looks like this:
jint
Java_com_example_hellojni_HelloJni_write(JNIEnv* env, jshortArray buf, jint off, jint len)
{
char debug[1024];
int ii = 0;
jsize cbuflen = (*env)->GetArrayLength(env, buf);
sprintf(debug, "array length: %d", cbuflen);
LOGD(debug);
...
...
The output is:
array length: 1079082088
Why is the array length so big?
Could you show your entire JNI file? You are not declaring the target object in your JNI function. Usually the arguments are JNIEnv* env, jobject javaObject, etc. This means that what you believe is the jshortArray is actually the pointer to a Java object, which would explain the weird results you are getting.

Categories

Resources