I'm calling native function from Java:
String pathTemp = Environment.getExternalStorageDirectory().getAbsolutePath()+Const.PATH_TEMP
String pathFiles = Environment.getExternalStorageDirectory().getAbsolutePath()+Const.PATH_FILES
engine.init(someInt, pathTemp, pathFiles);
And I have the native function:
extern "C" JNIEXPORT void Java_com_engine_init(JNIEnv *env, jobject __unused obj, jint someInt, jstring pathTemp, jstring pathFiles) {
const char *pathTemp_ = env->GetStringUTFChars(pathTemp, JNI_FALSE);
const char *pathFiles_ = env->GetStringUTFChars(pathFiles, JNI_FALSE); // <-- CRASH
// More init code
env->ReleaseStringUTFChars(pathTemp, pathTemp_);
env->ReleaseStringUTFChars(pathRecording, pathRecording_);
}
The problem: pathTemp is arriving good, but pathFiles==NULL in native function.
Rechecked, and confirmed - both strings are non NULL in java.
One more strange thing - The problem is on LG-G3 (android 6.0).
On Meizu PRO 5 (android 7.0) - everything works good - both strings are intact.
What is this JNI magic? Any clue?
I had the same problem as this and while I can't guarantee this is the same, I found a better solution than re-ordering the parameters.
tldr; Ensure the code works for 32bit and 64bit platforms as pointers have different sizes. I was running 32bit native code and passed nullptr as a parameter and java expected a long which resulted in all parameters after the nullptr to be invalid.
(JJLjava/lang/String;Z)V -> (final long pCallback, final long pUserPointer, final String id, final boolean b)
pCallback was always set to a valid value (pointer casted to jlong in c++) and pUserPointer was always nullptr. I found this answer and tried switching the order around and it 'just worked' but I knew that fix was never going to be approved.
After looking at the JNI documentation on the Android website again (https://developer.android.com/training/articles/perf-jni) I took note of the "64-bit considerations" section and took a stab at my assumption of the data size. This feature was developed with a 64bit device (Pixel 3) but issues had been reported on a 32bit device (Amazon Fire Phone) so nullptr would be 32bit but the java function still expected a long (64bit).
In my situation the offending parameter was always unused so I could safely remove it and everything "just worked" (including some other parameters which were broken).
An alternative would be to have a define/function/macro for JniLongNullptr which is just 0 casted to jlong.
Not an answer, but workaround. Moved the strings (in parameters) to be before int parameter. Now it's working. I have no idea why is this.
Related
I am following the Google tips to implement a JNI layer between my Android app and my C++ library. It suggests to use the following code to register native methods when the library is loaded:
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
...
// Register your class' native methods.
static const JNINativeMethod methods[] = {
{"nativeFoo", "()V", reinterpret_cast<void*>(nativeFoo)},
{"nativeBar", "(Ljava/lang/String;I)Z", reinterpret_cast<void*>(nativeBar)},
};
int rc = env->RegisterNatives(c, methods, sizeof(methods)/sizeof(JNINativeMethod));
...
}
I am quite new to C++ so I decided to use clang-tidy to ensure my C++ code is modern and safe. clang-tidy reports:
error: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast,-warnings-as-errors]
According to the clang-tidy documentation:
cppcoreguidelines-pro-type-reinterpret-cast
This check flags all uses of reinterpret_cast in C++ code.
Use of these casts can violate type safety and cause the program to
access a variable that is actually of type X to be accessed as if it
were of an unrelated type Z.
This rule is part of the “Type safety” profile of the C++ Core
Guidelines, see
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-type-reinterpretcast.
So I have a few options:
Disable this check and risk using reinterpret_cast inappropriately elsewhere
Ignore the check everywhere where I need to use it and create a messy codebase
Find some alternative way of implementing this more safely
I would like to do 3 if it's possible but I'm not quite sure where to start.
It’s not clear why GetEnv wants a void** rather than a JNIEnv** (what other type would you use?), but you can avoid the reinterpret_cast and the concomitant undefined behavior(!) with a temporary variable:
void *venv;
if (vm->GetEnv(&venv, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
const auto env=static_cast<JNIEnv*>(venv);
Note that a static_cast from void* is every bit as dangerous as a reinterpret_cast, but its use here is correct, unavoidable, and shouldn’t produce linter warnings.
In the function pointer case there’s nothing to be done: reinterpret_cast is the only, correct choice (and for void* rather than some placeholder function pointer type like void (*)() its correctness is not guaranteed by C++, although it works widely and POSIX does guarantee it). You can of course hide that conversion in a function (template) so that the linter can be told to ignore only it, but make sure to use a clear name to avoid “hiding” the conversion.
One of the ways to register native functions in JNI is by using the function RegisterNatives(). An example (although seemingly with some errors) can be found in the Android documentation here. Here's a code example demonstrating this technique:
#include <jni.h>
void JNICALL test(JNIEnv* const environment, jobject const objectOrClass) {
}
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* const vm, void* const reserved) {
JNIEnv* environment;
vm->GetEnv(reinterpret_cast<void**>(&environment), JNI_VERSION_1_6);
auto const classRef = environment->FindClass("com/example/test/MainActivity");
JNINativeMethod method;
method.name = "test";
method.signature = "()V";
method.fnPtr = reinterpret_cast<void*>(test);
environment->RegisterNatives(classRef, &method, 1);
return JNI_VERSION_1_6;
}
Using RegisterNatives() requires casting (non-member) function pointers to type void*, as shown above. According to this documentation, this isn't necessarily portable in C++. It may be guaranteed to work in some environments (such as POSIX), but the behavior is otherwise implementation-dependent.
I wouldn't expect this functionality to be exposed this way if it wasn't reliable, but does either JNI or Android offer any guarantees that this can be counted on to work? Is it just assumed that JNI and/or Android will always be running in environments where casting between function pointers and void* is supported?
EDIT:
After further consideration, it seems JNI would almost have to do something non-portable with respect to native functions, irrespective of the cast issue. The only information about the function JNI has (other than the address) is a string describing the argument and return types, so it seems unlikely that the void* will ever be cast back to a valid function pointer and used in the conventional way. Presumably the calls are instead made via low-level platform-specific code.
It still seems like a requirement that function-pointer-to-void* casts be well-behaved, so if anyone knows if or how JNI guarantees that, I'd still be interested to know.
I am a developer and trying to block a specific model of the Samsung Galaxy Note4 in Google Play Developer Console, problem is I can't find a correlation between the device model to what they write in the console.
For example, I want to know what SM-N910C translates to from the below list I took from the console:
Any ideas how to do that? Manually or programmatically...
I don't think it's part of the http://developer.android.com/reference/android/os/Build.html
So this is undoubtedly too late for you, but I have recently had the same question, so I decided to look into it, and I've developed a working theory.
I believe that the two strings that appear in the Google Play Developer Console are Android system properties. The more user-friendly one is "ro.product.model" and the other is "ro.product.device". This mapping appears to work, at least with the devices I have available right now. If anyone finds that these two values do not match what Google provides, please comment to that effect!
Programmatically reading Android system properties requires a JNI call into native code, like so:
package com.example;
class Native {
// pass a String[2]
public static native void readModelAndDevice(String[] _results);
}
-
#include <jni.h>
#include <sys/system_properties.h>
char model[256], device[256];
extern "C" JNIEXPORT void JNICALL com_example_Native_readModelAndDevice
(
JNIEnv * _java, jclass _class, jobjectArray _array
)
{
__system_property_get("ro.product.model", model);
__system_property_get("ro.product.device", device);
jstring jmodel = _java->NewStringUTF(model);
jstring jdevice = _java->NewStringUTF(device);
_java->SetObjectArrayElement(_array, 0, jmodel);
_java->SetObjectArrayElement(_array, 1, jdevice);
return;
}
Anyone who has never done JNI in Android Studio before should complete a JNI tutorial before trying this example.
I have spent a ridiculous amount of time trying to figure this out and I am at an absolute loss.
I am working with the JUCE library and have modified one of their sample projects. My goal is to have a very simple Android app that is written in C++ and then ported to Android. I need a function in C++ that I can call that will then call a function on the Android side that will return my heap size and other characteristics to my C++ code so that I can manage memory there.
If anyone has a simple solution that would be amazing. Right now my current snag is this:
char const buf[] = "From JNI";
jstring jstr = env->NewStringUTF(buf);
jclass clazz = env->FindClass("android/os/Debug");
But I keep getting an error saying that 'NewStringUTF' is not a _JNIEnv member... but if I right click on the method and jump to the definition, I see it in my jni.h file... any suggestions? I'm working in Xcode by the way...
Is it plain C, not C++? Perhaps your file has a .c extension.
If it's plain C it should be
JNIEnv* env;
JNI_CreateJavaVM(&jvm, (void **)&env, &args);
(*env)->NewStringUTF(env, buf);
I try to use the pocketsphinx package for my app and need to rename the demo package name to something usefull (eg com.myname.foo)
I spent hours on figuring out, but I simply can't get it to work.
The problem is, that the project runs fine if I leave the package name and works without any problems (apart from random crashes), but when I rename it, i get the error
FATAL EXCEPTION: main
java.lang.UnsatisfiedLinkError: new_Config__SWIG_0
I already tried modifying the Swig command, but it didn't work either.
Any ideas?
I only changed the Manifest's package name declariation and the package folder of the normal Activity.
You need to change on the c/c++ side there are two posible ways depending on how your JNI is implemented.
A. The function name contains the full classpath
JNIEXPORT jlong JNICALL Java_"package with underscore instead of .""class""method"(JNIEnv *env, jclass class,...
e.g.
JNIEXPORT jlong JNICALL Java_com_android_mms_transaction_NativeSms_send(JNIEnv *env, jclass class,...
match method send in class NativeSms in package com.android.mms.transaction
B. There is a string supplied back to dalvik/javaVM with the classpath. Look for someting like this:
static int registerMethods(JNIEnv* env) {
static const char* const kClassName =
"com/example/android/platform_library/PlatformLibrary";
jclass clazz;
/* look up the class */
clazz = env->FindClass(kClassName);
if (clazz == NULL) {
LOGE("Can't find class %s\n", kClassName);
return -1;
}
/* register all the methods */
if (env->RegisterNatives(clazz, gMethods,
sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)
{
LOGE("Failed registering methods for %s\n", kClassName);
return -1;
}
...
Edit 2011-12-07 Clarified first example
Sooo, I found the problem; I spend 20 ****ing hours just to find out, that I actually forgot to add
static {
System.loadLibrary("pocketsphinx_jni");
}
to the Activity class. I can't believe I didn't see that, but thanks for all the answers! +1 for everyone helping me :]
If you have link command issue it's most likely you forgot to change the SWIG launch properties. The file is
.externalToolBuilders/SWIG.launch
Those properties have several places to mention edu.cmu.sphinx package.
If you changed something it's recommended to describe the changes more precisely. Most likely you just forgot some small thing. For example you can pack whole changed code into archive and upload it somewhere.