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.
Related
I want to add C library to my project. lzfse for decode img via apple algorithm.
I have added c files to project
added CmakeLines file:
externalNativeBuild {
cmake {
path "src/main/lzfse/CMakeLists.txt"
version "3.10.2"
}
}
I have written JNI for decode:
JNIEXPORT jint JNICALL
Java_com_android_Decompressor_decode(
JNIEnv* env, jclass cls, jobject src, jobject dst
) {
uint8_t* src_buffer = (*env)->GetDirectBufferAddress(env,src);
const size_t src_size = (const size_t) (*env)->GetDirectBufferCapacity(env, src);
uint8_t* dst_buffer = (*env)->GetDirectBufferAddress(env,dst);
size_t dst_size = (size_t) (*env)->GetDirectBufferCapacity(env, dst);
jlong test = lzfse_decode_buffer(dst_buffer, dst_size, src_buffer, src_size, NULL);
return (jint) test;
}
then I call from kt decode fun:
val buf = ByteBuffer.wrap(byteArray)
val buf_out = ByteBuffer.allocateDirect(byteArray.size *20)
val size= decode(dstArray = buf_out, srcArray = buf)
But
My app just crashes
2020-02-25 20:12:25.717 28603-28603/com.android A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 in tid 28603 (), pid 28603 ()
Where did I lose?
Wrapping a ByteArray does not result in a direct buffer (at least on Android).
You will need to call GetByteArrayElements to get a (possibly copied) pointer.
I have fixed the crash! After some time I have decided to publish the working code.
So, the call native c++ from project:
private val dstArray: ByteBuffer = ByteBuffer.allocateDirect(DESTINATION_BUFFER_CAPACITY)
private val srcArray: ByteBuffer = ByteBuffer.allocateDirect(SRC_BUFFER_CAPACITY)
decode(srcArray.put(byteArray), dstArray)
Here instead of:
val buf = ByteBuffer.wrap(byteArray)
val size= decode(dstArray = buf_out, srcArray = buf)
I allocate a direct buffer via ByteBuffer.allocateDirect and put the source array in buffer srcArray.put(byteArray).
Because as #Botje said in his post :
Wrapping a ByteArray does not result in a direct buffer (at least on
Android).
my JNI:
JNIEXPORT JNICALL
Java_com_android_Decompressor_decode(
JNIEnv *env, jclass cls, jobject src, jobject dst
) {
uint8_t *src_buffer = (*env)->GetDirectBufferAddress(env, src);
const size_t src_size = (const size_t) (*env)->GetDirectBufferCapacity(env, src);
uint8_t *dst_buffer = (*env)->GetDirectBufferAddress(env, dst);
size_t dst_size = (size_t) (*env)->GetDirectBufferCapacity(env, dst);
lzfse_decode_buffer(dst_buffer, dst_size, src_buffer, src_size, NULL);
}
So I almost haven't changed JNI.
But in my first code sample, I have found that all works until the line:
//...
return (jint) test;
The line caused the crash.
In my case, I didn't need return value so I rewrite JNI function without return any values, but I think it possible to find where I have failed with the return value.
Thanks, guys who tried to give some help, I hope my post also can be helpful for someone.
I am not able to cast back the user self defined class return from C++ jni callback
code snippet as follow:
//Kotlin class
data class class_record(var id:String?,var class_name:String?,var class_type)
// com.example.MainActivity
public native Object Cls(String id);
Vector vec_ classrecord=new Vector();
vec_classrecord=(Vector)Cls("1234");
// c++ jni code
extern "C"
JNIEXPORT jobject JNICALL
Java_com_example_MainActivity_Cls(JNIEnv *env, jobject instance, jstring id,
) {
jclass java_vector_class;
jmethodID java_vector_method;
jobject java_vector_object ;
auto vec_record=// call c++ method that return vector for class record pointer
jstring jni_str;
jclass javaClassRef;
// jni for java.util.Vector
java_vector_class = env->FindClass("java/util/Vector");
java_vector_method_constructor = env->GetMethodID(java_vector_class, "<init>", "()V");
java_vector_object = env->NewObject(java_vector_class, java_vector_method_constructor, "");
for (auto record_it = vec_record.begin(); record_it < vec_record.end(); ++record_it) {
// jni for class_record
jclass java_class = env->FindClass("com/example/class_record");
javaClassRef = (jclass) env->NewGlobalRef(java_class);
jmethodID cls_constructor = env->GetMethodID(javaClassRef, "<init>", "()V");
jobject cls_object = env->NewObject(javaClassRef, cls_constructor, "");
// set id
javaMethodRef = env->GetMethodID(javaClassRef, "setId", "(Ljava/lang/String;)V");
std::string strval = record_it.id;
jni_str = env->NewStringUTF(strval.c_str());
env->CallVoidMethod(cls_object, javaMethodRef, jni_str);
// set class_name
javaMethodRef = env->GetMethodID(javaClassRef, "setClass_name","(Ljava/lang/String;)V");
std::string strval = record_it.class_name;
jni_str = env->NewStringUTF(strval.c_str());
env->CallVoidMethod(cls_object, javaMethodRef, jni_str);
//set class_type
javaMethodRef = env->GetMethodID(javaClassRef, "setClass_type","(Ljava/lang/String;)V");
std::string strval = record_it.class_type;
jni_str = env->NewStringUTF(strval.c_str());
env->CallVoidMethod(cls_object, javaMethodRef, jni_str);
jmethodID java_vector_add = env->GetMethodID(java_vector_class, "addElement","(Ljava/lang/Object;)V");
**env->CallVoidMethod(java_vector_object, java_vector_add, javaClassRef);**
}
return java_vector_object;
}
env->CallVoidMethod(java_vector_object, java_vector_add, cls_object);
Under Kotlin environment , it it much better to express jni c++ callback in ArrayList instead of vector which grow almost double the size with snippet as follow in situation where the returned arraylist is fixed size immuatable.
java_util_class = env->FindClass("java/util/ArrayList");
jmethodID java_add= env->GetMethodID(java_util_class, "add","(Ljava/lang/Object;)Z");
env->CallBooleanMethod(java_object, java_add, cls_object);
sample tutorial
i want to get app signature in native with these code: (source)
// For class Context
jclass native_clazz = (*env)->GetObjectClass(env, context);
// Get the getPackageManager method ID
jmethodID methodID_func = (*env)->GetMethodID(env, native_clazz,
"getPackageManager", "()Landroid/content/pm/PackageManager;");
// Access manager application package
jobject package_manager = (*env)->CallObjectMethod(env, thiz, methodID_func);
// For class PackageManager
jclass pm_clazz = (*env)->GetObjectClass(env, package_manager);
// Get the getPackageInfo method ID
jmethodID methodID_pm = (*env)->GetMethodID(env, pm_clazz,
"getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
// To obtain the application package information
jobject package_info = (*env)->CallObjectMethod(env, package_manager,
methodID_pm, (*env)->NewStringUTF(env, "com.example.hellojni"), 64);
but i got this:
error: 'context' undeclared (first use in this function)
how do i get context in native?
how do i return signature hashcode as string?
You can send the context to JNI in a parameter for example
jstring Java_packagename_stringFromJNI( JNIEnv* env, jobject thiz , jobject context)
And when you call the method in your activity:
public native String stringFromJNI(Context context);
...
Log.d(TAG, "Receive: " + stringFromJNI(this));
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;
}
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);