As you can see, I get jbyte *str form the utf string. Then each character of string has two jbytes else one byte?
JNIEXPORT jstring JNICALL
Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt) {
char buf[128];
const jbyte *str;
str = (env)->GetStringUTFChars(env, prompt, NULL);
if (str == NULL) {
return NULL; / OutOfMemoryError already thrown */
}
printf("%s", str);
(*env)->ReleaseStringUTFChars(env, prompt, str);
/* We assume here that the user does not type more than * 127 characters */
scanf("%s", buf);
return (*env)->NewStringUTF(env, buf);
}
Here:
str = (env)->GetStringUTFChars(env, prompt, NULL);
you receive buffer of single byte chars. You may even edit this like here:
const char *str = (env)->GetStringUTFChars(env, prompt, NULL);
because GetStringUTFChars() is declared as returning const char *.
Related
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.
Android Studio 2.3.2
Gradle experimental 0.9.2
Last NDK.
I wrote a small function in C that is invoked through JNI in Java but returns a string that precedes characters that should not be there.
This is the C function:
char * text() {
char * txt = "a";
char * ret = malloc(strlen(a)+2);
strcat(ret,txt);
strcat(ret,"b");
return ret;
}
This is the JNI C to return to Java
JNIEXPORT jstring JNICALL
Java_com_myapp_Main_getMsg(JNIEnv *env, jobject instance, jstring var)
{
char * msg = text();
return (*env)->NewStringUTF(env, msg );
}
This is Java call:
static {
System.loadLibrary("myapp");
}
public native String getMsg(String var);
String stringreturnedfromC = getMsg("eee");//eee is not used
This show this:
_Main_getMsgab
but should return only "ab".
This is a strange behavior. Why? And how can I get just the right string?
Noticed your function text() is not correct.
Do something like this
char * text() {
char * txt = "a";
char * ret = (char *)malloc(strlen(txt)); //malloc(strlen(a)+2); is incorrect statment
strcat(ret,txt);
strcat(ret,"b");
return ret;
}
How to get pdf meta-data using MuPdf in Android ? I'm using MuPdf V1.7.
I can get Author & PDF name but I cannot get creation date, creator and etc. I used below function to get information:
fz_lookup_metadata(ctx, glo->doc, FZ_META_INFO_TITLE, info, sizeof(info));
fz_lookup_metadata(ctx, glo->doc, FZ_META_INFO_AUTHOR, info, sizeof(info));
Can anybody help?
hi all i can get creation date from pdf by the below code.
add this into document.h
#define FZ_META_INFO_CREATIONDATE "info:CreationDate"
paste the below code into mupdf.c
JNIEXPORT jstring
JNICALL JNI_FN(MuPDFCore_metaPublishDate)(JNIEnv * env, jobject thiz)
{
char info[64];
globals *glo = get_globals(env, thiz);
fz_context *ctx = glo->ctx;
pdf_document *idoc = pdf_specifics(ctx, glo->doc);
fz_lookup_metadata(ctx, glo->doc, FZ_META_INFO_CREATIONDATE, info, sizeof(info));
return (*env)->NewStringUTF(env, info);
}
then we can able to get this by core.metaPublishDate().
you can easily do this with mupdf library.This function returns a string array which contains metadata information, respectively to keys in keys array. If there is no such info for a key, it returns an empty string
JNIEXPORT jobjectArray JNICALL
JNI_FN(MuPDFCore_metadataInternal)(JNIEnv * env, jobject thiz)
{
char info[64];
globals *glo = get_globals(env, thiz);
jobjectArray arr;
jclass stringClass;
const int nkeys = 4;
const char *keys[nkeys];
int i;
keys[0] = "Title";
keys[1] = "Author";
keys[2] = "Subject";
keys[3] = "Keywords";
stringClass = (*env)->FindClass(env, "java/lang/String");
arr = (*env)->NewObjectArray(env, nkeys, stringClass, NULL);
LOGI("Getting metadata");
for(i=0; idoc, FZ_META_INFO, info, sizeof(info));
LOGI("%s : %s", keys[i], info);
jstring s = (*env)->NewStringUTF(env, info);
if (s != NULL) {
(*env)->SetObjectArrayElement(env, arr, i, s);
}
(*env)->DeleteLocalRef(env, s);
}
return arr;
}
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.
OK, so I have the native code below.
I'm trying to return an array of FilePermissionInfo from it, populated with some data returned by stat().
The problem is that I get the following error when NewObject is called the first time:
06-15 20:25:17.621: W/dalvikvm(2287): Invalid indirect reference
0x40005820 in decodeIndirectRef 06-15 20:25:17.621: E/dalvikvm(2287):
VM aborting
It's odd, because the only reference object I have is the jclass (for FilePermissionInfo) and I turn it to a global reference.
The code is:
JNIEXPORT jobjectArray JNICALL
Java_com_mn_rootscape_utils_NativeMethods_getFilesPermissions( JNIEnv* env, jobject thizz, jobjectArray filePathsArray )
{
jobjectArray result;
int size = (*env)->GetArrayLength(env, filePathsArray);
jboolean isCopy;
jclass filePermInfoCls = (*env)->FindClass(env, kFilePermissionInfoPath);
if(!filePermInfoCls)
{
LOGE("getFilesPermissions: failed to get class reference.");
return NULL;
}
gFilePermInfoClass = (jclass)(*env)->NewGlobalRef(env, filePermInfoCls);
LOGI("got gFilePermInfoClass");
jmethodID filePermInfoClsConstructor = (*env)->GetMethodID(env, gFilePermInfoClass, "<init>", kFilePermInfoConstructorSig);
if(!filePermInfoClsConstructor)
{
LOGE("getFilesPermissions: failed to get method reference.");
return NULL;
}
struct stat sb;
LOGI("starting...");
result = (jobjectArray)(*env)->NewObjectArray(env, size, gFilePermInfoClass, NULL);
for(int i = 0; i != size; ++i)
{
jstring string = (jstring) (*env)->GetObjectArrayElement(env, filePathsArray, i);
const char *rawString = (*env)->GetStringUTFChars(env, string, &isCopy);
if(stat(rawString, &sb) == -1)
{
LOGE("stat error for: %s", rawString);
}
LOGI("%ld %ld %ld %ld %ld %ld %ld %ld", sb.st_dev, sb.st_mode, sb.st_nlink, sb.st_uid, sb.st_gid, sb.st_atime, sb.st_mtime, sb.st_ctime);
jobject permInfo = (*env)->NewObject(env,
gFilePermInfoClass,
filePermInfoClsConstructor,
(long)sb.st_dev,
(long)sb.st_mode,
(long)sb.st_nlink,
(long)sb.st_uid,
(long)sb.st_gid,
(long)sb.st_atime,
(long)sb.st_mtime,
(long)sb.st_ctime,
"",
"",
1,
"");
LOGI("xxx1");
(*env)->SetObjectArrayElement(env, result, i, permInfo);
LOGI("xxx2");
(*env)->ReleaseStringUTFChars(env, string, rawString);
LOGI("xxx3");
}
(*env)->DeleteLocalRef(env, filePermInfoCls);
return result;
}
The Java class constructor signature and path are:
const char* kFilePermissionInfoPath = "com/mn/rootscape/utils/FilePermissionInfo";
const char* kFilePermInfoConstructorSig = "(JJJJJJJJLjava/lang/String;Ljava/lang/String;ZLjava/lang/String;)V";
Please note that if I call NewObject on the default constructor then it works fine.
OK, found it.
It was a problem with the jstring parameters. It turns out you cannot pass empty strings (or even NULL for that matter) as a jstring.
Instead I used (*env)->NewStringUTF(env, NULL) to create a NULL jstring.
Seems to work OK now.
Since this question generated somewhat a high activity, I'm posting the final solution below. Note that the nullString variable is being deallocated at the end of its scope (or when you're done using it):
jstring nullString = (*env)->NewStringUTF(env, NULL);
...
jobject permInfo = (*env)->NewObject(env,
gFilePermInfoClass,
filePermInfoClsConstructor,
(jbyte)permsOwner,
(jbyte)permsGroup,
(jbyte)permsOthers,
(jlong)sb.st_uid,
(jlong)sb.st_gid,
(jlong)sb.st_atime,
(jlong)sb.st_mtime,
(jlong)sb.st_ctime,
nullString,
nullString,
(jboolean)1,
nullString);
...
(*env)->DeleteLocalRef(env, nullString);