Friday, 3 August 2012

Android development, SDL2 and crashes

Two more learning experiences with Android & SDL 2.0.

Crashes in native code

When your native code crashes, you get a PS3-style stacktrace with mystifying anonymous addresses representing each level of the call stack.  This is important because Eclipse cannot debug native code, it just exits debugging.  Much the same as on the PS3, you can run some tool on this stacktrace and get a readable form with file names and line numbers.  But you have to do it line by line manually, feeding addresses and static library paths into the tool.

This is a raw crash from the logcat output (with some duplicate lines trimmed):

08-01 19:36:56.899: I/DEBUG(31): Build fingerprint: 'generic/sdk/generic:2.3.3/GRI34/101070:eng/test-keys'
08-01 19:36:56.899: I/DEBUG(31): pid: 495, tid: 503  >>> org.disinterest.broguex <<<
08-01 19:36:56.899: I/DEBUG(31): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000020
08-01 19:36:56.899: I/DEBUG(31):  r0 00000000  r1 4419ac10  r2 ac411085  r3 00000001
08-01 19:36:56.899: I/DEBUG(31):  r4 0029e528  r5 002bdf60  r6 00000001  r7 43f37f3c
08-01 19:36:56.899: I/DEBUG(31):  r8 4419ac70  r9 43f37f34  10 00000000  fp 419507e8
08-01 19:36:56.899: I/DEBUG(31):  ip 441baf00  sp 4419ac10  lr ac411c93  pc ac411094  cpsr 00000030
08-01 19:36:57.139: I/DEBUG(31):          #00  pc 00011094  /system/lib/egl/libGLES_android.so
08-01 19:36:57.139: I/DEBUG(31):          #01  pc 00011c90  /system/lib/egl/libGLES_android.so
08-01 19:36:57.150: I/DEBUG(31):          #02  pc 00003844  /system/lib/libEGL.so
08-01 19:36:57.150: I/DEBUG(31):          #03  pc 00033004  /system/lib/libandroid_runtime.so
08-01 19:36:57.159: I/DEBUG(31):          #04  pc 00017d74  /system/lib/libdvm.so
08-01 19:36:57.159: I/DEBUG(31):          #05  pc 00048f54  /system/lib/libdvm.so
...
08-01 19:36:57.199: I/DEBUG(31):          #19  pc 000a3518  /data/data/org.disinterest.broguex/lib/libSDL2.so
08-01 19:36:57.199: I/DEBUG(31):          #20  pc 000a348e  /data/data/org.disinterest.broguex/lib/libSDL2.so
08-01 19:36:57.199: I/DEBUG(31):          #21  pc 0003142a  /data/data/org.disinterest.broguex/lib/libtcod.so
08-01 19:36:57.199: I/DEBUG(31):          #22  pc 00032e8e  /data/data/org.disinterest.broguex/lib/libtcod.so
08-01 19:36:57.209: I/DEBUG(31):          #23  pc 000112be  /data/data/org.disinterest.broguex/lib/libtcod.so
08-01 19:36:57.209: I/DEBUG(31):          #24  pc 0008e066  /data/data/org.disinterest.broguex/lib/libmain.so
...
08-01 19:36:57.219: I/DEBUG(31):          #31  pc 00029582  /data/data/org.disinterest.broguex/lib/libmain.so
08-01 19:36:57.219: I/DEBUG(31): code around pc:

Feeding each line into the tool is somewhat tedious and error prone.  Fortunately Ben van Daele has written a Python script to parse this raw data into a proper call stack.  With a few modifications, it even works on Windows and gives the output similar to the following:
d:/Code/android-ndk-r8/platforms/android-14/arch-arm/usr/include/jni.h:793:_JNIEnv::CallStaticVoidMethod(_jclass*, _jmet
hodID*, ...)
D:\Code\games\Brogue-v1.6.4-Android/jni/sdl2/src/core/android/SDL_android.cpp:273:Android_JNI_SwapWindow
D:\Code\games\Brogue-v1.6.4-Android/jni/sdl2/src/video/android/SDL_androidgl.c:108:Android_GL_SwapWindow
D:\Code\games\Brogue-v1.6.4-Android/jni/sdl2/src/video/SDL_video.c:2592:SDL_GL_SwapWindow
D:\Code\games\Brogue-v1.6.4-Android/jni/sdl2/src/render/opengles/SDL_render_gles.c:1062:GLES_RenderPresent
...
D:\Code\games\Brogue-v1.6.4-Android/jni/brogue/BrogueCode/RogueMain.c:38:executeEvent
D:\Code\games\Brogue-v1.6.4-Android/jni/brogue/BrogueCode/IO.c:637:mainInputLoop

My DOS window compatible version of the script, is available here.

Android rendering crashes

There's a page on the SDL wiki which says (or said, being unable to find it now) that as the SDL_APPACTIVE app state no longer exists, you should instead check the SDL_WINDOW_SHOWN window flag.  Unfortunately, on Android at least this is set even when the SDL_WINDOW_MINIMIZED flag is set.  Which might be when you hit the home button.  And if you try and render while your window is minimised, crashes ensue due to an invalid surface.

Something like this will precede one of those anonymous stacktraces:
09-20 01:59:06.702: ERROR/Surface(20116): surface (identity=...) is invalid, err=-19 (No such device)
This is what I use to check that it is okay to engage in normal rendering-related logic:
#if SDL_VERSION_ATLEAST(2,0,0) && defined(__ANDROID__)
 /* Abort if the window no longer has focus.  Otherwise a later draw operation will crash the app. */
 if ((SDL_GetWindowFlags((SDL_Window*)TCOD_sys_get_sdl_window()) & (SDL_WINDOW_SHOWN | SDL_WINDOW_MINIMIZED)) != SDL_WINDOW_SHOWN)
  return true;
#endif
I still need to properly handle pausing, and of course the special kind of pausing where the application gets killed and needs to persist state. But it's enough for now not to crash if the application is paused and resumed.

Supporting all Android architectures

Charilaos Kalogirou's post about porting your game from iOS to Android, recommends not just building code for the default ARMv5TE architecture, but all supported architectures.  To do this, in your "jni/" directory add the file "Application.mk" if it doesn't already exist.  And within this add the line "APP_ABI := all".  This will build all architectures, which according to the NDK documentation is ARMv5TE, ARMv7-A, x86 and MIPS.

You don't need to do anything else.  The presence of the binaries for the different architectures just works, and the Play store will now offer the app to those whose device was previously unsupported.  It did double the APK size of 1.1M to 2.2M, with MIPS architecture excluded for SDL-specific reasons.


The Play store does allow uploading multiple APK files, and filtering which of these is offered to different potential recipients, based on their differences.  But none of the filters allows you to upload a different APK per-architecture.

Getting more familiar with SDL 2.0

I'm a little confused by SDL 2.0.  On one hand, it has made getting libtcod working on Android very easy.  On the other hand, there is a range of functionality that was present in 1.2, which is.. vaguely alluded to as no longer being considered part of SDL and up to any end user to reimplement themselves.

One of these is key repeat rates.  SDL 1.2 had an API that let you customise this.  SDL 2.0 has a mailing list post suggesting that any end user wanting to modify it, should use the OS facilities.  Android does not have any OS facility to modify this.

Another of these is mapping key events to the character on the key the user actually pressed.  What I get as a developer when I press "~", is the keycode for "`" with a modifier indicating a shift key was being pressed at the time.  I get this on Windows, and I get this on Android.  SDL 1.2 had the unicode field, which was populated with the correct character through platform-specific logic that queried the keyboard mapping or code page, or whatever.  SDL 2.0 has the unicode field, but it never gets a value set to it, so remains 0.  It however has a comment indicating it is obsolete and that the text input API should be used.  Now, the text input API is a completely different non-key press based approach.  If you're writing a text adventure, then it's probably what you're looking for.  And if you are writing an arcade game, then the current SDL key press support is probably okay.  But if you want to have more than the limited SDL key press support, you need to (on Android at least) completely bypass the SDL event system and it's KeyEvent.  This is easy to do, but it feels like SDL is incomplete and that you're violating proper procedure.

Friday, 27 July 2012

BrogueX released

I've published my port of Brogue as an Android paid app.  It's basically playable with a little difficulty, and requires a little work to fix bugs, and a lot of work to enhance the Brogue libtcod-based UI for tablets and mobile devices.

All the details are on the link above.  Buying it supports the work required to make this a first class Android experience, and you get to direct the efforts involved should you choose.

Tuesday, 24 July 2012

Android development, Eclipse and SDL2

I've started playing with developing an Android application.  Here are my notes for future reference.

Debugging emulated C code in Eclipse

If I was developing in Java, Eclipse would be an easy choice.  When a breakpoint is hit or an exception happens within the Android emulator, execution will pause at the given point in whatever code is executing.  But only for Java.  If I step into C code, the debugger simply proceeds to run the program.  If the C code crashes, the debugger exits.  The only help is the LogCat window, and printf driven debugging.


No amount of web searching has located anyone else having this problem.

Eclipse's pre-compilation code checker gets in the way

Eclipse has this feature called Code Analysis.  It appears to be an editor level tool that shows you errors in your code before you actually compile.  The only problem is that it is unable to properly parse C code.  This means that not only do you get the wrong parts of preprocessor directive #if/#ifdef clauses being highlighted, you get officially lodged errors you have to fix before you are allowed to do things like debugging your code - even though it was just compiled without error!

Web searching reveals people who suggest clearing the analysis results seemingly every time you make a code change.  Others suggest rerunning the indexer.  Then there are suggestions to manually enter the mis-indexed symbols in a settings tab which does not exist for me.  And finally, some people hack the Eclipse project to specify the symbol, to get it to work.

I ended up disabling this feature via the Window->Preferences menu, within the C/C++->Code Analysis pane.  It sounds like it just isn't reliable enough to spend the time required to get it to work.


Android file access and SDL

An Android application is packaged in a zip archive, suffixed with ".apk".  One of the your directories, named "assets", is placed in this archive and can be accessed in a read-only fashion.  To access this, you need to make calls into the Java VM and your application object running there.  You grab an AssetManager, and from there can make calls on it to list whatever files and subdirectories are present within this directory, and of course can open and read from them.

Luckily, I am using SDL2 and can avoid this by just using the SDL_rwops abstraction layer.  This is not very well documented like much of the still in-development SDL2, but only provides access to the asset manager, and the assets directory within the apk file.  File system access I do not have much experience with, and still need to research.  But it looks like the standard C file-related functions are used.

Android asset access and libpng

I have a png image within my apk file, but libpng internally uses standard C file IO functions.  Working around this was a simple matter of taking advantage of the ability to specify read and write callbacks to libpng, which are used in place of the standard functions.
static void png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
 SDL_RWops *rwops = png_get_io_ptr(png_ptr);
 rwops->read(rwops,data,length,1);
}
static void png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
 SDL_RWops *rwops = png_get_io_ptr(png_ptr);
 rwops->write(rwops,data,length,1);
}
static void png_flush_data(png_structp png_ptr) {}

/* ... */

 SDL_RWops *rwops;
 if ((rwops = SDL_RWFromFile(filename, "rb")) == NULL)

 png_set_read_fn(png_ptr, (voidp)rwops, (png_rw_ptr)png_read_data);

There are some macros I should be using, which allow me to get rid of the rwops->opfunc(rwops pattern.  And what happens if I want to read or write png files from external storage, is another complication yet to be addressed.

Emulator logging, printf and Eclipse's LogCat tab

If I write to standard output from your application, it effectively goes nowhere by the looks of it.  So as advised by web searching, I use Android logging functions like __android_log_print whose output appears in the LogCat window in Eclipse  Better yet, I just bung these defines in a global include file -- which every project seems to have.

#ifdef __ANDROID__
#include <android/log.h>
#ifdef printf
#undef printf
#endif
#ifdef vprintf
#undef vprintf
#endif
#define printf(args...) __android_log_print(ANDROID_LOG_INFO, "libtcod", ## args)
#define vprintf(args...) __android_log_vprint(ANDROID_LOG_INFO, "libtcod", ## args)

#ifdef assert
#undef assert
#endif
#define assert(cond) if(!(cond)) __android_log_assert(#cond, "libtcod", "assertion failed: %s", #cond)
#endif
And I can just proceed to observe the existing printf calls in the LogCat tab in Eclipse.  I don't know why the Android assert function takes in the condition as a string for the first argument, as it doesn't appear in the Eclipse LogCat tab.

SDL2 also has logging functions which on Android, end up going through the Android logging API.  But as my application already has pre-existing printf calls, and I don't need the complication of an extended logging system, whatever.

Touch events and screen coordinates

Touch events have x and y coordinates.  But these are relative to the resolution of the touch device, and are not screen coordinates as events normally give.  It is an easy matter to fetch the device resolution and to scale it to screen coordinates.
 SDL_TouchFingerEvent *tfe=&ev->tfinger;
 SDL_Touch *touch=SDL_GetTouch(tfe->touchId);
 dx += (tfe->dx * windowWidth)/touch->xres;
 dy += (tfe->dy * windowHeight)/touch->yres;
 x = (tfe->x * windowWidth)/touch->xres;
 y = (tfe->y * windowHeight)/touch->yres;

Android "offline" SDK documentation

Being on Windows, I downloaded the SDK documentation through the SDK Manager program which comes as part of the SDK.  However, it has hard-coded references to Google javascript and font related web sites.  This means I get is a 10-20 sec loading delay every time I bring it up in a browser, or click a link within it to go to another page.

Android application's initial cwd

Porting an existing program to an Android application, being based on SDL it generally just works.  But what does cause crashes is the presumption that the current working directory is somewhere files can be stored.  What is the initial cwd for an Android application?

#include <errno.h> /* errno */
#include <unistd.h> /* getcwd */
#include <dirent.h> /* opendir / readdir */

/* ... */

 char cwdpath[100];
 DIR* pd;
 struct dirent *cur;
 if (getcwd(cwdpath, 99) == NULL) {
  printf("getcwd() failed and gave: %d\n", errno);
 } else {
  printf("getcwd() gave: %s\n", cwdpath);
  pd = opendir(cwdpath);
  if (pd == NULL) {
   printf("opendir() failed and gave: %d\n", errno);
  } else {
   while (cur = readdir(pd)) {
    printf("\"%s\"\n", cur->d_name);
   }
   closedir(pd);
  }
 }
Results in LogCat rows containing:
07-21 14:30:50.227: I/Brogue(923): getcwd() gave: /
07-21 14:30:50.227: I/Brogue(923): "."
07-21 14:30:50.227: I/Brogue(923): ".."
07-21 14:30:50.227: I/Brogue(923): "config"
This makes sense.  The install location of the apk file is not really relevant.  So the developer needs to query where to store these files, which may be either on internal or external storage.

SDL2 hard-coded namespace

Java code is referred to by namespace, and so JNI calls into C are made on the naming of that namespace.  This is a problem with SDL2 because the implementation of SDL2 relies on the use of the namespace "org.libsdl.app".  Anyone publishing their own app would want to do so in their own namespace, and if you change all the namespace references (whether in code, directory structure or configuration files), the JNI calls try and find the SDL C methods using your namespace.  This means that the internal SDL functions don't get called, and your app doesn't load due to the first UnresolvedLinkError.

This forum thread discusses it, and this blog post provides a file you can link against (with changes), in order to work around this problem. The changes required are replacing the use of this namespace with your own, and also verifying that all the SDL methods are wrapped.  I had to add several more.