Android and JNI real time clock

2019-05-08 23:54发布

I got a problem with a mini Android application and the use of real time clock signals in a C (JNI) function.

It seems like Android UI doesn't like real time signals from timers instanced in a C function.

In the following PoC, a timer trigger a signal 5 times per second and if the signal is triggered while the UI is updating, the application crashes.

  • If i don't start the timer => no crash
  • If i don't put anything on UI => no crash

I wrote this little PoC to evidence the behaviour. Java part just call the JNI function and put a button on screen.

public class MainActivity extends AppCompatActivity {

    Button bt;

    static {
        System.loadLibrary("testtimer-jni");
    }

    /* JNI ingresso */
    public native void jniStartTimer();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        jniStartTimer();

        /* load button */
        bt = new Button(getBaseContext());

        setContentView(bt);
    }
}    

and this is the main.c file content. A timer is instanced and started. Every 200ms (5 times per sec) the cb() function is called.

#include <jni.h>
#include <android/log.h>
#include <signal.h>
#include <time.h>
#include <strings.h>

timer_t           timer_id = 0x12;
struct itimerspec timer;
struct sigevent   te;
struct sigaction  sa;

void cb(int sig, siginfo_t *si, void *uc)
{
    __android_log_write(ANDROID_LOG_ERROR, "Test", "Called callback");
}

void Java_it_dbtecno_testtimer_MainActivity_jniStartTimer(JNIEnv *env, jobject thiz)
{
    __android_log_write(ANDROID_LOG_ERROR, "Test", "Timer inited");

    /* prepare timer to emulate video refresh interrupts */
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = cb;
    sigemptyset(&sa.sa_mask);

    if (sigaction(SIGRTMIN + 7, &sa, NULL) == -1)
        return;
    bzero(&te, sizeof(struct sigevent));

    /* set and enable alarm */
    te.sigev_notify = SIGEV_SIGNAL;
    te.sigev_signo = SIGRTMIN + 7;
    te.sigev_value.sival_ptr = &timer_id;
    timer_create(CLOCK_REALTIME, &te, &timer_id);

    timer.it_value.tv_sec = 1;
    timer.it_value.tv_nsec = 1000;
    timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_nsec = 1000000000 / 5;

    /* start timer */
    timer_settime(timer_id, 0, &timer, NULL);
}

Sometimes it dies instantly, sometimes it dies after i press the button (i think it depends on timing of signal/UI update) and it output this on log

09-22 11:52:12.087 13587-13587/it.dbtecno.testtimer I/Test: Called callback
09-22 11:52:12.288 13587-13587/it.dbtecno.testtimer I/Test: Called callback
09-22 11:52:12.501 13587-13587/it.dbtecno.testtimer I/Test: Called callback
09-22 11:52:12.532 13587-13587/it.dbtecno.testtimer A/OpenGLRenderer: Task is already in the queue!
09-22 11:52:12.532 13587-13587/it.dbtecno.testtimer A/libc: Fatal signal 6 (SIGABRT), code -6 in tid 13587 (tecno.testtimer)
09-22 11:52:12.637 1187-1187/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
09-22 11:52:12.637 1187-1187/? A/DEBUG: Build fingerprint: 'Android/sdk_google_phone_x86/generic_x86:6.0/MASTER/3079352:userdebug/test-keys'
09-22 11:52:12.637 1187-1187/? A/DEBUG: Revision: '0'
09-22 11:52:12.637 1187-1187/? A/DEBUG: ABI: 'x86'
09-22 11:52:12.638 1187-1187/? A/DEBUG: pid: 13587, tid: 13587, name: tecno.testtimer  >>> it.dbtecno.testtimer <<<
09-22 11:52:12.638 1187-1187/? A/DEBUG: signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
09-22 11:52:12.642 1187-1187/? A/DEBUG: Abort message: 'Task is already in the queue!'
09-22 11:52:12.642 1187-1187/? A/DEBUG:     eax 00000000  ebx 00003513  ecx 00003513  edx 00000006
09-22 11:52:12.642 1187-1187/? A/DEBUG:     esi b77a5c50  edi 0000000b
09-22 11:52:12.642 1187-1187/? A/DEBUG:     xcs 00000073  xds 0000007b  xes 0000007b  xfs 00000007  xss 0000007b
09-22 11:52:12.642 1187-1187/? A/DEBUG:     eip b736f666  ebp 00003513  esp bfdc7540  flags 00200202
09-22 11:52:12.644 1187-1187/? A/DEBUG: backtrace:
09-22 11:52:12.645 1187-1187/? A/DEBUG:     #00 pc 00084666  /system/lib/libc.so (tgkill+22)
09-22 11:52:12.650 1187-1187/? A/DEBUG:     #01 pc 00081608  /system/lib/libc.so (pthread_kill+70)
09-22 11:52:12.651 1187-1187/? A/DEBUG:     #02 pc 00027205  /system/lib/libc.so (raise+36)
09-22 11:52:12.651 1187-1187/? A/DEBUG:     #03 pc 000209e4  /system/lib/libc.so (abort+80)
09-22 11:52:12.659 1187-1187/? A/DEBUG:     #04 pc 0000cbc3  /system/lib/libcutils.so (__android_log_assert+128)
09-22 11:52:12.660 1187-1187/? A/DEBUG:     #05 pc 00025e81  /system/lib/libhwui.so (android::uirenderer::renderthread::RenderThread::queue(android::uirenderer::renderthread::RenderTask*)+81)
09-22 11:52:12.660 1187-1187/? A/DEBUG:     #06 pc 00021b44  /system/lib/libhwui.so
09-22 11:52:12.660 1187-1187/? A/DEBUG:     #07 pc 000243e5  /system/lib/libhwui.so (android::uirenderer::renderthread::RenderProxy::syncAndDrawFrame()+29)
09-22 11:52:12.660 1187-1187/? A/DEBUG:     #08 pc 000ba75b  /system/lib/libandroid_runtime.so
09-22 11:52:12.660 1187-1187/? A/DEBUG:     #09 pc 72dfe20e  /data/dalvik-cache/x86/system@framework@boot.oat (offset 0x1eb2000)
09-22 11:52:12.713 1187-1187/? A/DEBUG: Tombstone written to: /data/tombstones/tombstone_08
09-22 11:52:12.713 1187-1187/? E/DEBUG: AM write failed: Broken pipe

I also tried to change the signal number (from SIGRTMIN to SIGRTMIN + 20) but no luck....

My question is.... Is it possible to use real time clock signals in a JNI function without breaking the RenderThread? (Yes, it's the latter that crashes)

Is it a bad habit to play with timers in JNI functions?

1条回答
祖国的老花朵
2楼-- · 2019-05-09 00:43

Probably timer signal is delivered to renderer thread or main thread. In such case it interrupts system call that is in progress in that thread (if any). And this situation may trigger some assert in runtime code. You may play with SIGEV_THREAD_ID to direct signal to dedicated thread. But be aware that this notification type is not intended for widespread usage. Also some of real-time signals may be silently used by bionic threading implementation or even ART. So it is very easy to break something.

P.S. if possible - you should prefer SIGEV_THREAD. It looks more reliable on Android since you shouldn't think about proper signal numbers etc.

查看更多
登录 后发表回答