Recently, as we have been testing applications and games for NVIDIA SHIELD, we have found that a seemingly minor issue in Google’s native_app_glue source code (and thus in NVIDIA’s TADP samples based on this code) is actually a serious issue on next-gen Android platforms. Specifically, the bug described in this posting makes it very common for pure-native games to hit an "ANR" (Application Not Responding) and crash in normal use. Fortunately, the fix is extremely simple.

The basic problem is that the original native_app_glue.c’s input handling does not process input events at a high enough rate, especially when both touch and external game controllers are present. This leads to the input pipeline backing up. This in turn leads Android to declare the application as “not responding”, and gives the user the option to kill it. The bug was uncommon on older devices but on systems with external USB or Bluetooth controllers or dedicated gaming controllers, such as SHIELD's in-built controller, the bug is easily reproducible.

The bug is easy to diagnose. First, find your application’s copy of native_app_glue.c. If you are using the NDK’s version directly, that is normally:

$NDKROOT/sources/android/native_app_glue/native_app_glue.c

If you are using the NVIDIA TADP samples, it is:

libs/jni/nv_and_util/nv_native_app_glue.c

In the C file, find the function process_inputand look for the line that calls AInputQueue_getEvent. If that call is of the form:

if (AInputQueue_getEvent(app->inputQueue, &event) >= 0) {

then you are using the original (buggy) version. The correct implementation of process_inputis:

 

// Fix per Google for bug https://code.google.com/p/android/issues/detail?id=41755
static void process_input(struct android_app* app, struct android_poll_source* source) {
    AInputEvent* event = NULL;
    int processed = 0;
    while (AInputQueue_getEvent(app->inputQueue, &event) >= 0) {
        LOGV("New input event: type=%d\n", AInputEvent_getType(event));
        if (AInputQueue_preDispatchEvent(app->inputQueue, event)) {
            continue;
        }
        int32_t handled = 0;
        if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event);
        AInputQueue_finishEvent(app->inputQueue, event, handled);
        processed = 1;
    }
    if (processed == 0) {
        LOGE("Failure reading next input event: %s\n", strerror(errno));
    }
}

 

Note the "while" loop to process more than one event in a call and the use of "continue" instead of "return". Swapping in the above version of process_inputshould avoid the ANR issues.

NVIDIA will shortly update the TADP version of native_app_glue. However, this issue is common enough and dangerous enough that we wanted to provide a convenient and quick fix sooner than TADP's next release.

Wishing all mobile (and yet-to-be-mobile) game developers the best at E3 next week!