i'm trying to access getPackageManager.getApplicationInfo in jni.
const char* getNativeLibPath(JNIEnv* env, jobject thiz, const char* libraryName, const char* packageName) {
jclass contextClass = env->GetObjectClass(thiz);
jmethodID getPackageManager = env->GetMethodID(contextClass, "getPackageManager", "()Landroid/content/pm/PackageManager;");
jobject instantiatePackageManager = env->CallObjectMethod(thiz, getPackageManager);
jclass packageManagerClass = env->GetObjectClass(instantiatePackageManager);
jmethodID getApplicationInfo = env->GetMethodID(packageManagerClass, "getApplicationInfo", "(Ljava/lang/String;I)Landroid/content/pm/ApplicationInfo;");
jobject instantiateApplicationInfo = env->CallObjectMethod(thiz, getApplicationInfo, packageName, 0);
jclass applicationInfoClass = env->GetObjectClass(instantiateApplicationInfo);
jfieldID nativeLibraryDir = env->GetFieldID(applicationInfoClass, "nativeLibraryDir", "Ljava/lang/String;");
auto string = (jstring) env->GetObjectField(instantiateApplicationInfo, nativeLibraryDir);
const char* returnValue = env->GetStringUTFChars(string, nullptr);
std::string appendedResult = std::string(returnValue) + std::string("/") + std::string(libraryName);
return appendedResult.c_str();
}
This is my code for it. However for some reason i'm getting this error: JNI ERROR (app bug): accessed stale WeakGlobal 0x74eecd21ff (index 1324143135 in a table of size 38) JNI DETECTED ERROR IN APPLICATION: use of deleted weak global reference 0x74eecd21ff
Any help is appreciated!
Your code has at least three problems:
You call getApplicationInfo with a const char * which expects a Java string:
jobject instantiateApplicationInfo = env->CallObjectMethod(instantiatePackageManager, getApplicationInfo, env->NewStringUTF(packageName), 0);
You need to call env->ReleaseStringUTF(returnValue) to release the string on the Java side
You cannot return a const char * like that. Either return the std::string directly, or allocate memory with new char[] and let the caller free it.
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.
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 *
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.
I have a method in JNI C/C++ which takes jstring and returns back jstring some thing like as below,
NATIVE_CALL(jstring, method)(JNIEnv * env, jobject obj, jstring filename)
{
// Get jstring into C string format.
const char* cs = env->GetStringUTFChars (filename, NULL);
char *file_path = new char [strlen (cs) + 1]; // +1 for null terminator
sprintf (file_path, "%s", cs);
env->ReleaseStringUTFChars (filename, cs);
reason_code = INTERNAL_FAILURE;
char* info = start_module(file_path);
jstring jinfo ;
if(info==NULL)
{
jinfo = env->NewStringUTF(NULL);
}
else
{
jinfo = env->NewStringUTF(info);
}
delete info;
info = NULL;
return jinfo;
}
The code works perfectly with prior android 4.0 versions like 2.2,2.3 and so on. With ICS 4.0 check JNI is on by default and because of it the app crashes throwing the following error
08-25 22:16:35.480: W/dalvikvm(24027): **JNI WARNING: input is not valid Modified UTF-8: illegal continuation byte 0x40**
08-25 22:16:35.480: W/dalvikvm(24027):
08-25 22:16:35.480: W/dalvikvm(24027): ==========
08-25 22:16:35.480: W/dalvikvm(24027): /tmp/create
08-25 22:16:35.480: W/dalvikvm(24027): ==========
08-25 22:16:35.480: W/dalvikvm(24027): databytes,indoorgames,drop
08-25 22:16:35.480: W/dalvikvm(24027): ==========���c_ag����ϋ#�ډ#�����#'
08-25 22:16:35.480: W/dalvikvm(24027): in Lincom/inter /ndk/comNDK;.rootNDK:(Ljava/lang/String;)Ljava/lang/String; **(NewStringUTF)**
08-25 22:16:35.480: I/dalvikvm(24027): "main" prio=5 tid=1 NATIVE
08-25 22:16:35.480: I/dalvikvm(24027): | group="main" sCount=0 dsCount=0 obj=0x40a4b460 self=0x1be1850
08-25 22:16:35.480: I/dalvikvm(24027): | sysTid=24027 nice=0 sched=0/0 cgrp=default handle=1074255080
08-25 22:16:35.490: I/dalvikvm(24027): | schedstat=( 49658000 26700000 48 ) utm=1 stm=3 core=1
08-25 22:16:35.490: I/dalvikvm(24027): at comrootNDK(Native Method)
I am clueless as to where i am wrong. If you see above NewStringUTF is adding some garbage value to the c Char* bytes .
Any idea about why this is happening
Any alternative solution to achieve the above is welcome
I really appreciate if one of you can help me in . Thanks in advance
regds
me
The cause of this problem is directly related to a known UTF-8 bug in the NDK/JNI GetStringUTFChars() function (and probably related functions like NewStringUTF). These NDK functions do not convert supplementary Unicode characters (i.e., Unicode characters with a value of U+10000 and above) correctly. This leads to incorrect UTF-8 and subsequent crashes.
I encountered the crash when handling user input text that contained emoticon characters (see the corresponding Unicode chart). Emoticon characters lie in the Supplementary Unicode character range.
Analysis of the Problem
The Java client passes a string containing a supplementary Unicode
character to JNI/NDK.
JNI uses the NDK function GetStringUTFChars() to extract the contents of the Java string.
GetStringUTFChars() returns the string data as incorrect and invalid UTF-8.
There is a known NDK bug whereby GetStringUTFChars() incorrectly converts supplementary Unicode characters, producing an incorrect and invalid UTF-8 sequence.
In my case, the resulting string was a JSON buffer. When the buffer was passed to the JSON parser, the parser promptly failed because one of the UTF-8 characters of the extracted UTF-8 had an invalid UTF-8 prefix byte.
Possible Workaround
The solution I've used can be summarized as follows:
The goal is to prevent GetStringUTFChars() from performing the
incorrect UTF-8 encoding of the supplementary Unicode character.
This is done by the Java client encoding the request string as
Base64.
The Base64-encoded request is passed to JNI.
JNI calls GetStringUTFChars(), which extracts the Base64-encoded
string without performing any UTF-8 encoding.
The JNI code then decodes the Base-64 data, producing the original
UTF-16 (wide char) request string, including the supplementary
Unicode character.
In this way we circumvent the problem of extracting supplementary Unicode characters from the Java string. Instead, we convert the data to Base-64 ASCII before calling GetStringUTFChars(), extract the Base-64 ASCII characters using GetStringUTFChars(), and convert the Base-64 data back to wide characters.
This is how I done this.
1- Char Array to JByteArray.
2- JByteArray to JString.
3- Return jstring to java side.
JNI Code; (.c) format
jstring Java_com_x_y_z_methodName(JNIEnv *env, jobject thiz) {
int size = 16;
char r[] = {'P', 'K', 'd', 'h', 't', 'X', 'M', 'm', 'r', '1', '8', 'n', '2', 'L', '9', 'K'};
jbyteArray array = (*env)->NewByteArray(env, size);
(*env)->SetByteArrayRegion(env, array, 0, size, r);
jstring strEncode = (*env)->NewStringUTF(env, "UTF-8");
jclass cls = (*env)->FindClass(env, "java/lang/String");
jmethodID ctor = (*env)->GetMethodID(env, cls, "<init>", "([BLjava/lang/String;)V");
jstring object = (jstring) (*env)->NewObject(env, cls, ctor, array, strEncode);
return object;
}
Java Code;
native String methodName();
Other Approach Not Works For Me;
I also tried return (*env)->NewStringUTF(env, r) but returns some characters that are not in the char array, at the end of the string where with the warning of JNI WARNING: input is not valid Modified UTF-8: illegal continuation byte 0x40.
Example; PKdhtXMmr18n2L9K�ؾ�����-DL
Edit:
C++ version
jstring clientStringFromStdString(JNIEnv *env,const std::string &str){
// return env->NewStringUTF(str.c_str());
jbyteArray array = env->NewByteArray(str.size());
env->SetByteArrayRegion(array, 0, str.size(), (const jbyte*)str.c_str());
jstring strEncode = env->NewStringUTF("UTF-8");
jclass cls = env->FindClass("java/lang/String");
jmethodID ctor = env->GetMethodID(cls, "<init>", "([BLjava/lang/String;)V");
jstring object = (jstring) env->NewObject(cls, ctor, array, strEncode);
return object;
}
I resolved this issue by returning byte array instead of String. On the Java side i am now converting the Byte array to Strings .Works fine! Stay away from using NewStringUTF() for Android 4.0 and above as there is already a bug reported on Google Android NDK.
I had this problem when I change the file Application.mk
From this line:
APP_STL := stlport_static
To:
APP_STL := gnustl_static
Once I changed it back again it fixed the issue.
Strings that you pass to NewStringUTF() need to be valid Modified UTF-8. It looks like the string returned by your start_Inauthroot() function is in some other encoding, or is just returning an invalid string. You need to convert the string to UTF-8 before passing it to JNI functions. Or you could use one of the charset-aware String constructors to build the String object instead.
In my opinion, its not a bug.
NewStringUTF Constructs a new java.lang.String object from an array of characters in modified UTF-8 encoding.
Modified UTF-8 is not standard UTF-8. See Modified UTF-8
In most cases, UTF-8 encoded string is valid Modified UTF-8. Because Modified UTF-8 and UTF-8 are quite similar. However when it comes to Unicode string beyond Basic Multilingual Plane, they are not compatible.
Solution:
pass UTF-8 bytes to Java layer and new String(bytes, "UTF-8") then pass jstring to JNI.
For me, the solution was to place the content on a const char*:
const char* string = name_sin.c_str();
jstring utf8 = env_r->NewStringUTF(string);
and the function:
jclass cls_Env = env_r->FindClass(CLASS_ACTIVITY_NAME);
jmethodID mid = env_r->GetMethodID(cls_Env, "Delegate",
"(Ljava/lang/String;)V");
//todo importante hacerlo asi, si pasas directamente c_str a veces da error de carater no UTF 8
const char* string = name_sin.c_str();
jstring utf8 = env_r->NewStringUTF(string);
env_r->CallVoidMethod(*object_r, mid, utf8);
env_r->DeleteLocalRef(utf8);
I also struggled with the same problem from the last day. Finally figured out a solution after a day .. I hope this reply may save someone's day..
The problem was I was calling another function within the native function, used the returned string directly and which caused crash in android older versions
So firstly I saved the string returned from another function to a variable then used it, and the problem gone :D
The below example may clear your concept
//older code with error
//here key_ is the string from java code
const char *key = env->GetStringUTFChars(key_, 0);
const char *keyx = getkey(key).c_str();
return env->NewStringUTF(keyx);
And here is how I solved this error
//newer code which is working
//here key_ is the string from java code
const char *key = env->GetStringUTFChars(key_, 0);
string k = getkey(key);
const char *keyx = k.c_str();
return env->NewStringUTF(keyx);
Happy coding :D
This works for me in c++
extern "C" JNIEXPORT
jstring Java_com_example_ndktest_MainActivity_TalkToJNI(JNIEnv* env, jobject javaThis, jstring strFromJava)
{
jboolean isCopy;
const char* szHTML = env->GetStringUTFChars(strFromJava, &isCopy);
std::string strMine;
strMine = szHTML;
strMine += " --- Hello from the JNI!!";
env->ReleaseStringUTFChars(strFromJava, szHTML);
return env->NewStringUTF(strMine.c_str());
}
c android ndk is working as follows
JNIEXPORT jstring JNICALL
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz,jstring str )
{
jboolean isCopy;
const char* szHTML = (*env)->GetStringUTFChars(env, str, &isCopy);
return (*env)->NewStringUTF(env, szHTML);
}