FAQ: NativeActivity Input Crashes and ANRs

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

The basic problem is that the original input handling for native_app_glue.c does not process input events at a high enough rate, especially when both touch and external game controllers are present. This causes the input pipeline to back up, which in turn leads Android to declare the application as "not responding," and gives the user the option to kill it. This particular bug was uncommon on older devices, but on systems with an external USB or Bluetooth controller, or a 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_input, and look for the line that calls AInputQueue_getEvent. If that call has the following form, then you are using the original (buggy) version:

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

The correct implementation of process_input is:

// 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 that the while loop is used to process more than one event in a call, and also note the use of continue instead of return. Swapping in the above version of process_input should avoid the ANR issues.

A future release of TADP will include an updated version of native_app_glue.

 

 

 


NVIDIA® GameWorks™ Documentation Rev. 1.0.200608 ©2014-2020. NVIDIA Corporation. All Rights Reserved.