I've been investigating on a Qt5/Android/Location issue and found a nullpointer access which makes me think GPS with Qt5 cannot work on any machine.
In my case when I try to run
auto source = QGeoPositionInfoSource::createDefaultSource(this);
the method AttachedJNIEnv::AttachedJNIEnv() defined in src/plugins/position/android/src/jnipositioning.cpp (Qt5 sources) crashes with the following call:
if (javaVM->GetEnv((void**)&jniEnv, JNI_VERSION_1_6) < 0) {
On at least three devices I've tried, javaVM is NULL so I'm pretty sure it's not just an issue with my device.
javaVM is a global pointer (what a shame..) and it should get a value in Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *, void *) also defined in jnipositioning.cpp
My question is now:
Obviously this is a bug in Qt5 and I'll make a bug report. But in the meanwhile I'd like to get positioning working with some sort of fix/workaround.
So what can be the reason for JNI_OnLoad() not being called? Did I forget to somehow initialize Qt::Location? Who should call JNI_OnLoad()? Can I do this manually in some way?
Update:
I've reported a Qt bug now.
Related
I am trying to hook into the Huawei AREngine in a native renderer. the library is connected and I have managed to call a function from the API, but I cant get anything that needs the JNIEnv or context or Activity to work without throwing a SIGSEGV.
Am I missing something obvious? the pointers are showing address values which appear to be consistant from run to run. I am passing the addresses as void* which is shown in the Example app.
Thanks for any advice.
in my activity, this code:
protected void onResume()
{
super.onResume();
CloudEngine.onCreate(getApplicationContext(), this);
call into the JNI which calls into this
void HWSensor::Initialise(void* env, void* context, void* activity)
{
HwArEnginesAvaliblity engines = HwArEnginesSelector_checkAllAvailableEngines(env, activity);
LOGI("HelloArApplication-----jds added ---1--- OnResume(): Check_EnginesAvailability, engines=%d",
engines);
}
I should see the available 'engines' but instead this function HwArEnginesSelector_checkAllAvailableEngines causes a fatal error.
Hello there? There is one question.
nexus5 lollipop5.1's issue.
surfaceflinger there is a code that comes in response to the device screen information in ScreenshotClient update by using.
The code looks like the following.
unsigned int sw, sh, xsize, gsize, stride;
... ...
ScreenshotClient sc;
sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
if( sc.update(display,Rect(), sw,sh, false) != NO_ERROR) {
... ...
sc.release();
return 0;
}
stride = sc.getStride();
mapbase = sc.getPixels();
... ...
The above code, in other lollipop version is operating normally. Of course, nexus7 the same version also operate normally.
However, the change in nexus5. My code to reference, but continue to code loop is. And because you try to get to continue the screen. First I code in nexus5 is operating normally. However, when a certain count is, update is not came under the other screen to return the error. More ...! After complete finished my code, re-run, after it is another well, the same phenomenon occurs when it comes to a certain number. If you are ability who know about this?
I'm sorry it is not so good in English.
I had solve. ScreenshotClient sc and sp < IBinder > display should be declared as a global variable. If the class is declared error occurs several times. (example. It declared in the function to be repeated.)
I am developing a C++ game on Android NDK (android-ndk-r9b). If I write this:
class Test
{
char c[1024*1024*1024];
};
//in main
try {
Test* p;
while (1) {
p = new Test();
}
} catch (bad_alloc) {
cout << "bad_alloc\n";
}
it doesn't throw. If I try this:
void no_memory_by_new() {
cout << "no_memory_by_new\n";
throw bad_alloc();
}
//in main
set_new_handler(no_memory_by_new);
Test* p;
while (1) {
p = new Test();
}
it doesn't work either. Finally, if I try this:
//in main
set_new_handler(no_memory_by_new);
int* p;
while (1) {
p = new int[1024*1024*1024];
}
then no_memory_by_new is called. I'm really confused about this. Can anyone help me?
This is bug in Android build of GNU libstdc++. If you look into operator new implementation, you'll see it call _GLIBCXX_THROW_OR_ABORT if malloc return NULL. Next, if you look on definition of _GLIBCXX_THROW_OR_ABORT, you'll see it throw bad_alloc only if __EXCEPTIONS defined; otherwise, it just call abort. For some reason, __EXCEPTIONS macro is not defined in Android build of GNU libstdc++, so it call abort - exactly what you see in your case.
I've checked this behaviour with both Android NDK r10d and CrystaX NDK 10.1 - and it's the same in both cases. I've filed ticket to fix this in CrystaX NDK. For fixing that in Google's NDK, you should also file ticket in Google's NDK bug tracker
UPD: It seems that situation is not so simple... Investigating it further, I've found more details pointing to the fact there is something bit more complicated than I've described above. Looking further; will update answer when have strict results.
UPD2: After deep investigation I've found that my previous answer was completely wrong. In fact, __EXCEPTIONS are defined when building GNU libstdc++, so operator new actually throw bad_alloc if malloc return NULL. The problem is in your code actually, but it was bit tricky to figure out. See explanation below.
TL;DR: operator new return pointer to the "allocated" memory (so from its point of view, there's no reason to throw std::bad_alloc), but first access to that memory cause crash because actually those pages are not available.
More detailed explanation:
Here is complete code I've used for testing:
#include <new>
#include <stdio.h>
#include <string.h>
class Test
{
public:
Test() {
::fprintf(stderr, "ctor start\n");
//memset(c, 0, sizeof(c));
::fprintf(stderr, "ctor finish\n");
}
private:
char c[1024*1024*1024];
};
int main()
{
try {
while (1) {
Test *p = new Test();
if (!p)
return 1;
}
return 1;
} catch (std::bad_alloc) {
return 0;
}
}
If you compile this test and run it on device, you'll get std::bad_alloc on some iteration (I get it on third iteration). But if you uncomment memset in constructor of Test, application will crash on the first memset call. If you remove constructor of Test completely, it will crash too - just because in this case compiler will generate constructor, which do zero-initialization of all members - i.e. the same as we do with memset.
The difference here is that malloc (used internally in operator new) returns pointer to the "allocated" memory, but actually it is not allocated; this region is just marked as "need to be allocated in future, when application will actually refer it". This is how Linux kernel handle it, for performance reasons. On next step, when you (or compiler) fill array with zeros, application actually access those pages but, unfortunately, there's just no free memory in system, so Linux kernel call OOM killer, killing the process as result.
This is not Android-specific. In fact, the same happens on GNU/Linux systems; the only difference is amount of memory available to the system (on Android it much lower than on servers, for obvious reasons).
In order to have working exceptions, you need to build using one of the other C++ standard libraries, other than the default one. Add e.g. APP_STL := gnustl_static to jni/Application.mk. Additionally, you need to enable exceptions, add LOCAL_CPP_FEATURES += exceptions to your Android.mk, or add APP_CPPFLAGS += -fexceptions to jni/Application.mk.
I have an Android app that uses NDK - a regular Android Java app with regular UI and C++ core. There are places in the core where I need to call Java methods, which means I need a JNIEnv* for that thread, which in turn means that I need to call JavaVM->AttachCurrentThread() to get the valid env.
Previously, was just doing AttachCurrentThread and didn't bother to detach at all. It worked fine in Dalvik, but ART aborts the application as soon as a thread that has called AttachCurrentThread exits without calling DetachCurrentThread. So I've read the JNI reference, and indeed it says that I must call DetachCurrentThread. But when I do that, ART aborts the app with the following message:
attempting to detach while still running code
What's the problem here, and how to call DetachCurrentThread properly?
Dalvik will also abort if the thread exits without detaching. This is implemented through a pthread key -- see threadExitCheck() in Thread.cpp.
A thread may not detach unless its call stack is empty. The reasoning behind this is to ensure that any resources like monitor locks (i.e. synchronized statements) are properly released as the stack unwinds.
The second and subsequent attach calls are, as defined by the spec, low-cost no-ops. There's no reference counting, so detach always detaches, no matter how many attaches have happened. One solution is to add your own reference-counted wrapper.
Another approach is to attach and detach every time. This is used by the app framework on certain callbacks. This wasn't so much a deliberate choice as a side-effect of wrapping Java sources around code developed primarily in C++, and trying to shoe-horn the functionality in. If you look at SurfaceTexture.cpp, particularly JNISurfaceTextureContext::onFrameAvailable(), you can see that when SurfaceTexture needs to invoke a Java-language callback function, it will attach the thread, invoke the callback, and then if the thread was just attached it will immediately detach it. The "needsDetach" flag is set by calling GetEnv to see if the thread was previously attached.
This isn't a great thing performance-wise, as each attach needs to allocate a Thread object and do some internal VM housekeeping, but it does yield the correct behavior.
I'll try a direct and practical approach (with sample code, without use of classes) answering this question for the occasional developer that came up with this error in android, in cases where they had it working and after a OS or framework update (Qt?) it started to give problems with that error and message.
JNIEXPORT void Java_com_package_class_function(JNIEnv* env.... {
JavaVM* jvm;
env->GetJavaVM(&jvm);
JNIEnv* myNewEnv; // as the code to run might be in a different thread (connections to signals for example) we will have a 'new one'
JavaVMAttachArgs jvmArgs;
jvmArgs.version = JNI_VERSION_1_6;
int attachedHere = 0; // know if detaching at the end is necessary
jint res = jvm->GetEnv((void**)&myNewEnv, JNI_VERSION_1_6); // checks if current env needs attaching or it is already attached
if (JNI_EDETACHED == res) {
// Supported but not attached yet, needs to call AttachCurrentThread
res = jvm->AttachCurrentThread(reinterpret_cast<JNIEnv **>(&myNewEnv), &jvmArgs);
if (JNI_OK == res) {
attachedHere = 1;
} else {
// Failed to attach, cancel
return;
}
} else if (JNI_OK == res) {
// Current thread already attached, do not attach 'again' (just to save the attachedHere flag)
// We make sure to keep attachedHere = 0
} else {
// JNI_EVERSION, specified version is not supported cancel this..
return;
}
// Execute code using myNewEnv
// ...
if (attachedHere) { // Key check
jvm->DetachCurrentThread(); // Done only when attachment was done here
}
}
Everything made sense after seeing the The Invocation API docs for GetEnv:
RETURNS:
If the current thread is not attached to the VM, sets *env to NULL, and returns JNI_EDETACHED. If the specified version is not supported, sets *env to NULL, and returns JNI_EVERSION. Otherwise, sets *env to the appropriate interface, and returns JNI_OK.
Credits to:
- This question Getting error "attempting to detach while still running code" when calling JavaVm->DetachCurrentThread that in its example made it clear that it was necessary to double check every time (even though before calling detach it doesn't do it).
- #Michael that in this question comments he notes it clearly about not calling detach.
- What #fadden said: "There's no reference counting, so detach always detaches, no matter how many attaches have happened."
I'm currently porting Gingerbread code to ICS. The communication between C and Java happens properly in Gingerbread. But the same thing crashes in ICS. Not able to figure out.
What are the major changes in ICS jni.?
My current problem,
1.Get the Class Instance and convert it into global reference and store it.
jclass myWrapperClass = (*env)->FindClass(env,"com/test/mypackage/Wrapper");
if (voipWrapperClass == NULL) {
// error - handling and returns
}
WrapperClass = (jclass)(*env)->NewGlobalRef(env,myWrapperClass);
2.From a JNI call the flow goes to below stack and returns callback to jni. From JNI to java the below function call
void response(void* ptr, int result){
JNIEnv *envPtr= NULL;
JavaVM* vmPtr= p_pdb->vm;
if ((*vmPtr)->GetEnv(vmPtr,(void**) &envPtr, JNI_VERSION_1_4) == JNI_EDETACHED) {
(*vmPtr)->AttachCurrentThread(vmPtr,(void**)&envPtr,NULL);
}
if (ptr->WrapperClass == NULL) {
// error- handling and return
}
RespMethodId = (*envPtr)->GetMethodID(envPtr,ptr->WrapperClass, "resp","(Z)V");
// this method is always 0 ... prev for gingerbread it returned a valid id..
}
Please give me a solution how to proceed further.?
I have found the solution finally. I used to compile my code in android code-base (2.3.3) but from ICS if you need to build a separate shared library use NDK and build a separate library. Else place the code in frameworks folder. This solves the problem. :)