I'm trying to 'catch' a crash (SIGSEGV) in java.
This crash happens in the C code and I would like to report that failure correctly in java.
So that the whole app doesn't crash when the native code crashes I managed to move the call to the native code to another process inside a 'Service' (by use of android:process=":ffmpeg"
in AndroidManifest.xml). This works fine.
Right now I tried:
- to add a signer handler in the C code and throw a java exception from there.
throwFfmpegHelperException(a, "throwFfmpegHelperException message");
- to add a signer handler in the C code and call a java function from there.
(*a)->CallStaticVoidMethod(a, obj, nativeCrashed);
- to use the 'thread' approach by setting the uncaught exception handler of a java thread with:
t.setDefaultUncaughtExceptionHandler
However none of theses actually runs the code I put in java (ie. throws the java Exception I would like to see triggered).
Any idea what could be wrong here? Any alternative? For now, my workaround is to wait for some time the reply from the service and if timeout is reached, consider it means that the service crashed.
Service (java)
contains the call to the 'java class that will call native function' & thread with its setDefaultUncaughtExceptionHandler
public class FfmpegBoundService extends Service {
/**
* Command to the service to display a message
*/
static final int MSG_SAY_HELLO = 1;
/**
* Handler of incoming messages from clients.
*/
static class IncomingHandler extends Handler {
Bitmap tb;
Messenger clientMessenger;
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
clientMessenger = msg.replyTo;
Runnable doSwscale = () -> {
try {
tb = Smooth.rescale_with_crash((Bitmap) msg.obj, msg.arg1, msg.arg2, Smooth.AlgoParametrized1.LANCZOS, 3.0); // 3 is default width in ffmpeg.
} catch (Exception e) {
e.printStackTrace();
}
};
Thread t = new Thread(doSwscale);
// Set UncaughtExceptionHandler to catch crash
t.setDefaultUncaughtExceptionHandler(new Thread.
UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable e) {
try {
throw new FfmpegHelperException("ffmpeg Crash");
} catch (Exception e2) {
e2.printStackTrace();
}
}
});
t.start();
// Wait until thread is finished.
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// Reply to client and send the thumbnail
Message reply = Message.obtain(null, 10, tb);
try {
clientMessenger.send(reply);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
Messenger mMessenger;
/**
* When binding to the service, we return an interface to our messenger
* for sending messages to the service.
*/
@Override
public IBinder onBind(Intent intent) {
mMessenger = new Messenger(new IncomingHandler());
return mMessenger.getBinder();
}
}
java (call to native function)
public class Smooth implements Serializable {
public static Bitmap rescale_with_crash(Bitmap src, int dstWidth, int dstHeight,
AlgoParametrized1 algo, double p0) throws Exception {
Bitmap aa = null;
int a = native_rescale_with_crash(src, Bitmap.createBitmap(dstWidth, dstHeight, src.getConfig()),
algo.flag, p0, 0.0);
return aa;
}
static void nativeCrashed() throws Exception {
//new RuntimeException("crashed here (native trace should follow after the Java trace)").printStackTrace();
//Log.e("eta", "crashed here (native trace should follow after the Java trace)");
throw new FfmpegHelperException("native crashed here ");
}
private static native Bitmap native_rescale(Bitmap src, Bitmap dst, int algo, double p0, double p1);
private static native int native_rescale_with_crash(Bitmap src, Bitmap dst, int algo, double p0, double p1);
}
C code
jint throwFfmpegHelperException( JNIEnv *env, char *message );
JNIEnv *a;
static jclass obj;
static jmethodID nativeCrashed;
void Segfault_Handler(int signo)
{
//(*a)->CallStaticVoidMethod(a, obj, nativeCrashed);
throwFfmpegHelperException(a, "throwFfmpegHelperException message");
signal(signo, SIG_DFL);
raise(signo);
}
static int JNICALL native_rescale_impl_with_crash(JNIEnv *env, jclass clazz,
jobject srcBitmap, jobject dstBitmap, jint sws_algo, jdouble p0, jdouble p1) {
signal(SIGSEGV,Segfault_Handler);
a = env;
obj = clazz;
raise(SIGSEGV);
return 200;
}
static const char *g_rescaler_java_class = "com/schokoladenbrown/Smooth";
static const JNINativeMethod g_native_methods[] = {
{"native_rescale",
"(Landroid/graphics/Bitmap;Landroid/graphics/Bitmap;IDD)Landroid/graphics/Bitmap;",
native_rescale_impl},
{"native_rescale_with_crash",
"(Landroid/graphics/Bitmap;Landroid/graphics/Bitmap;IDD)I",
native_rescale_impl_with_crash},
};
jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
JNIEnv *env = NULL;
jclass cls;
(*jvm)->AttachCurrentThread(jvm, &env, NULL);
cls = (*env)->FindClass(env, g_rescaler_java_class);
(*env)->RegisterNatives(env, cls, g_native_methods, sizeof g_native_methods / sizeof g_native_methods[0]);
nativeCrashed = (*env)->GetStaticMethodID(env, cls, "nativeCrashed", "()V");
}
jint throwNoClassDefError_( JNIEnv *env, char *message )
{
jclass exClass;
char *className = "java/lang/NoClassDefFoundError";
exClass = (*env)->FindClass( env, className);
if (exClass == NULL) {
return throwNoClassDefError_( env, className );
}
return (*env)->ThrowNew( env, exClass, message );
}
//https://stackoverflow.com/questions/230689/best-way-to-throw-exceptions-in-jni-code
jint throwFfmpegHelperException( JNIEnv *env, char *message )
{
if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
return 99;
}
jclass exClass;
char *className = "com/exifthumbnailadder/app/exception/FfmpegHelperException" ;
exClass = (*env)->FindClass( env, className );
if ( exClass == NULL ) {
return throwNoClassDefError_( env, className );
}
return (*env)->ThrowNew( env, exClass, message );
}