Start new Activity from JNI - android

I would like use this code from JNI
Intent intent = new Intent(this, TestActivity.class);
startActivity(intent);
So far, i wrote the next code but i'm getting error from the constructor with params i guess ?
jclass intentClass = env->FindClass("android/content/Intent");
jstring actionString =env->NewStringUTF("com.test.package.TestActivity");
jmethodID newIntent = env->GetMethodID(intentClass, "<init>", "(Landroid/content/Context;Ljava/lang/String;)Landroid/content/Intent");
jobject intent = env->NewObject(intentClass,newIntent,context,actionString);
jclass activityClass = env->FindClass("android/app/Activity");
jmethodID startActivity = env->GetMethodID(activityClass,"startActivity", "(Landroid/content/Intent;)V");
env->CallVoidMethod( intent, startActivity);
The error:
JNI DETECTED ERROR IN APPLICATION: JNI NewObjectV called with pending
exception java.lang.NoSuchMethodError: no non-static method
"Landroid/content/Intent;.(Landroid/content/Context;Ljava/lang/String;)Landroid/content/Intent"
The context and actionString params are OK and used somewhere else in code, so could you please help me I'm not familiar with JNI, if i use it , its for security reasons and try to avoid pirac.
Thanks

Thanks to Michael :
The answer working for what i want to do with jobject context from parameter function :
jclass native_context = env->GetObjectClass(context);
jclass intentClass = env->FindClass("android/content/Intent");
jclass actionString = env->FindClass("yourPackage/YourActivity");
jmethodID newIntent = env->GetMethodID(intentClass, "<init>", "(Landroid/content/Context;Ljava/lang/Class;)V");
jobject intent = env->NewObject(intentClass,newIntent,context,actionString);
jmethodID methodFlag = env->GetMethodID(intentClass, "setFlags", "(I)Landroid/content/Intent;");
jobject intentActivity = env->CallObjectMethod(intent, methodFlag, 268435456 );
jmethodID startActivityMethodId = env->GetMethodID(native_context, "startActivity", "(Landroid/content/Intent;)V");
env->CallVoidMethod(context, startActivityMethodId, intentActivity);

Related

How to set IntField from native jni to Java Android 10

After update my project to support Android 10, a crash produced in the JNI level related to non-SDK interface restrictions in Android 10 :
JNI DETECTED ERROR IN APPLICATION: JNI SetIntField called with pending exception java.lang.NoSuchFieldError: no "I" field "value" in class "Ljava/lang/Integer;" or its superclasses
which come from this part of code :
jclass clazz = (*env)->GetObjectClass(env, outputObj);
jfieldID mi = (*env)->GetFieldID(env, clazz, "value", "I");
(*env)->SetIntField(env, outputObj, mi, pListLen);
To fix that i replace it by :
jclass clazz = (*env)->GetObjectClass(env, outputObj);
jmethodID intValueMethod = (*env)->GetMethodID(env, clazz, "intValue", "()I");
jint result = (*env)->CallIntMethod(env, outputObj,intValueMethod,pListLen);
After that my application doesn't crash and find the integer value correctly, but i want to set the result integer on Java code using the same method SetIntField.
Could you please give me a way or method to set the result on the Java part.
As changing the intger field is not a good practice on JNI part, so i changed the way instead changing it by reflection method
jclass clazz = (*env)->GetObjectClass(env, outputBufLen);
jmethodID value_of = (*env)->GetStaticMethodID(env,clazz, "valueOf", "(I)Ljava/lang/Integer;");
jobject result = (*env)->CallStaticObjectMethod(env,clazz, value_of, pOutBufLen);
I set the result under an int Array and i return it :
int result = (*env)->CallStaticObjectMethod(env,clazz, value_of, pOutBufLen);
jintArray resultArray = (*env)->NewIntArray(env, 2);
jint fill[2];
fill[0] = result;
fill[1] = (jint) pOutBufLen;
(*env)->SetIntArrayRegion(env, result, 0, 2, fill);
return (resultArray);

java lang class exception -Unable to cast the java vector<user defined java class> return from c++ jni object callback

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

Get app signature hash code in jni

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 fix JNI crash on env->NewObject()?

Here is my program:
extern "C" {
JNIEXPORT jint Java_android_app_integrity_VerifyIntegrity_checkCrc(JNIEnv *jniEnv,jobject thiz,jstring crcStr) {
jclass clsZipFile = jniEnv->FindClass("java/util/zip/ZipFile");
jmethodID mtdConstruct = jniEnv->GetMethodID(clsZipFile, "<init>", "(Ljava/lang/String;)V");
jmethodID mtdGetEntry = jniEnv->GetMethodID(clsZipFile,"getEntry","(Ljava/lang/String;)Ljava/util/zip/ZipEntry;");
jclass clsZipEntry = jniEnv->FindClass("java/util/zip/ZipEntry");
jmethodID mtdGetCrc = jniEnv->GetMethodID(clsZipEntry,"getCrc","()L");
LOGD("pos2");
jobject objZipFile = jniEnv->NewObject(clsZipFile,mtdConstruct,crcStr);
if (NULL == objZipFile){
LOGD("NULL == objZipFile");
}
LOGD("pos3");
jobject objZipEntry = jniEnv->CallObjectMethod(objZipFile, mtdGetEntry,"classes.dex");
LOGD("pos4");
jlong ret = jniEnv->CallLongMethod(objZipEntry, mtdGetCrc);
LOGD("%ld",(long int)ret);
return 0;
}
};
It only print "pos2". The line below the "LOGD("pos2");" will cause crash!
I can't find the reason. Who can help me? Thx!
Try fixing the following line. It has an invalid signature and will cause an implicit exception to be thrown for MethodNotFound exception and is likely the culprit.
jmethodID mtdGetCrc = jniEnv->GetMethodID(clsZipEntry,"getCrc","()L");
Should be:
jmethodID mtdGetCrc = jniEnv->GetMethodID(clsZipEntry,"getCrc","()J");
However I would second other suggestions to check return values from all FindClass and FindMethod calls as they not only return NULL they also each throw an exception upon failure. Also OutOfMemoryException is thrown when JNI can not allocate a local reference object to return for your jclass lookups.

Sending an Intent from C++ via JNI

I am trying to make an explicit intent call (intent to a specific pkg and class) from C++ via JNI. Everything is fine until the last line where I am actually trying to send the intent:
env->CallVoidMethod(obj, startActivity, intentObject);
I dont get an android error, it is just a full dump/stack trace...which does not give me any helpful info.
Here is the C++ code making the call to start an intent:
JNIEXPORT void JNICALL Java_com_testpkg_test(JNIEnv *java_env, jobject obj) {
jvm->AttachCurrentThread(&java_env, 0);
jclass activityClass = java_env->GetObjectClass(obj);
jmethodID appGetContextId = java_env->GetMethodID(activityClass, "getApplicationContext", "()Landroid/content/Context;");
jobject appContext = java_env->CallObjectMethod(obj, appGetContextId);
//Get an instance of Intent
jclass intentClass = java_env->FindClass("android/content/Intent");
jmethodID newIntent = java_env->GetMethodID(intentClass, "<init>", "()V");
jobject intentObject = java_env->NewObject(intentClass, newIntent);
java_env->CallVoidMethod(intentObject, newIntent);
//Get an instance of the ComponentName class
jclass componentClass = java_env->FindClass("android/content/ComponentName");
jmethodID componentMID = java_env->GetMethodID(componentClass, "<init>", "(Ljava/lang/String;Ljava/lang/String;)V");
jstring pkgName =java_env->NewStringUTF("com.testpkg");
jstring clsName =java_env->NewStringUTF("com.testpkg.test");
jobject componentObj = java_env->NewObject(componentClass, componentMID, pkgName, clsName);
java_env->CallVoidMethod(componentObj, componentMID, pkgName, clsName);
//Calling intent.setComponentName passing in pkg+class name
jmethodID setComponentName = java_env->GetMethodID(intentClass, "setComponent","(Landroid/content/ComponentName;)Landroid/content/Intent;");
java_env->CallObjectMethod(intentObject, setComponentName, componentObj);
//Call getapplicationcontext().startActivity(intent)
jmethodID startActivity = java_env->GetMethodID(activityClass, "startActivity", "(Landroid/content/Intent;)V");
env->CallVoidMethod(obj, startActivity, intentObject);
}
The comments I posted still apply, but here's a problem- don't call java_env->CallVoidMethod(intentObject, newIntent). The NewObject call does it for you. You do this twice, its likely to be screwing up Java's memory management.

Categories

Resources