Android, SDL, and the Ouya
Jun 30, 2013
16 minute read

When you decide to port your native application to a Java-based platform like Android, you know you’re in for some trouble. Thankfully, JNI makes it possible for native and managed code to interoperate, at the cost of many jumps through hoops. We’ll study how SDL, one of the most versatile base library for game development, exposes Android functionality through its C interface.

Before we continue, please bear in mind that I am not an expert on Android or JNI, and that I will gladly take feedback or factual corrections at @fasterthanlime on Twitter.

The manifest

Let’s start with the basics. We hear so much about Android applications, but what are they exactly? Well, for starters, an application has a manifest. The manifest is an XML file that defines the application’s name, its icon, its theme, whether it is debuggable. The application is uniquely defined by its package. If you install an application that is defined with the same package, it will replace it.

The manifest doesn’t only define an application, it also defines one or several activities. Let’s take the case of a simple Android/ooc application I have been toying with.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.nevargames.swoon"
      android:versionCode="1"
      android:versionName="1.0"
      android:installLocation="auto">

    <application android:label="@string/app_name"
                 android:icon="@drawable/icon"
                 android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
                 android:debuggable="true">
        <activity android:name="SwoonGame"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
		<category android:name="tv.ouya.intent.category.GAME"/>
            </intent-filter>
        </activity>
    </application>

    <!-- Ouya sdk version -->
    <uses-sdk android:minSdkVersion="16" /> 
</manifest> 

As you can see, the manifest also specifies things like the minimum SDK version, and on a regular Android device, you would specify the permissions that the App require. That’s what will trigger the permissions dialog when installing or upgrading the application.

However, on Ouya, there is a default set of permissions for all applications. Another ouya-specific line is the category tv.ouya.intent.category.GAME - that will make the app appear in the ‘Play’ menu rather than the ‘Make’ menu.

Some values in the manifest are references, for example @string/app_name lives in res/values/strings.xml. Drawables are also in res/, but they are actual image files, for example res/drawable-hdpi/icon.png (along with variants for various resolutions).

The game’s activity

Since we specified a package and a class name in the manifest, Android knows it should launch the main activity, in our case, com.nevargames.swoon.SwoonGame. Our activity lives in a Java file in src/com/nevargames/swoon/SwoonGame.java, as per Java conventions. Let’s take a look at it.

package com.nevargames.swoon;

import org.libsdl.app.SDLActivity;

public class SwoonGame extends SDLActivity {

    // Load the .so
    static {
        System.loadLibrary("SDL2");
        System.loadLibrary("sdk");
        System.loadLibrary("deadlogger");
        System.loadLibrary("sdl2");
        System.loadLibrary("freetype2");
        System.loadLibrary("stb-image");
        System.loadLibrary("dye");
        System.loadLibrary("yaml");
        System.loadLibrary("chipmunk");
        System.loadLibrary("gnaar");
        System.loadLibrary("swoon");
        System.loadLibrary("main");
    }

}

Not much to see here, except that we define a class extending org.libsdl.app.SDLActivity, and that we are using a static initializer to load a bunch of native libraries. The initializer will be ran when the class is loaded.

The order in which libraries are loaded is important. While I think the Android runtime will attempt to load dependencies itself, we’re cutting it some slack by loading them in the right order. For example, deadlogger depends on the sdk, so we’re loading the sdk first. And so on.

Those libraries are Linux dynamic libraries, in effect .so files, compiled for our target architecture. For the Ouya and the vast majority of Android devices, that’s ARM. So, in our libs/armeabi/ folder, after calling ndk-build, we find libSDL2.so, libsdk.so, libdeadlogger.so, etc.

ndk-build and native code

ndk-build is the make of the Android ecosystem. Instead of Makefiles, it uses Android.mk files. As I mentioned in my first ooc and android article, since version 0.9.5, rock can generate Android.mk files itself.

The process goes something like that:

  • rock takes arguments from the command-line or, preferably, a .use file
  • .ooc files are parsed, ooc dependencies are resolved and parsed as well
  • .c and .h files are generated
  • one Android.mk per ooc ‘compilation unit’ is generated
  • ndk-build reads Android.mk files, compiles to .o files, links to .so files

And then all that’s left is calling ant. Ant is Java’s make-like tool. It reads from build.xml and handles everything in relation to crunching assets, compiling Java source files, packing everything in an apk archive (the actual form of distribution for an Android application), and even signing it.

The SDL activity

Alright, so there is an APK file, containing an XML manifest, which specifies the fully qualified name of a Java class, which in turns asks the Java runtime (in this case, Dalvik) to load a bunch of native libraries - that’s all good and fancy, but where does the magic happen?

Well, remember how the game activity extends a certain SDLActivity class? It is provided, along with a basic manifest, ant build file, and other things as a template android project in the default SDL 2 source distribution.

I won’t post the full listing of SDLActivity, but I will present and study chosen extracts. The main, public class is essentially this:

/**
    SDL Activity
*/
public class SDLActivity extends Activity {
  
  // a few protected members: reference to the EGL (OpenGL) context, surface,
  // display, config. Reference to the main thread, the audio thread, etc.
  
  // Setup
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    //Log.v("SDL", "onCreate()");
    super.onCreate(savedInstanceState);
    
    // So we can call stuff from static callbacks
    mSingleton = this;

    // Set up the surface
    mSurface = new SDLSurface(getApplication());

    mLayout = new AbsoluteLayout(this);
    mLayout.addView(mSurface);

    setContentView(mLayout);
  }

  // other overrides: onPause, onResume, onLowMemory, onDestroy

}

So, what’s happening here? The SDLActivity class extends android.app.Activity. By overriding certain methods, we can respond to events that are outside of our control: the app being started, paused, resumed, memory running low, etc.

But so far we haven’t seen yet where native code is called from the SDLActivity. Well, the magic happens when the SDLSurface is created. It inherits from SurfaceView, an Android component which provides… somewhere to draw in your application. And somewhere that can be associated with an OpenGL context.

/**
    SDLSurface. This is what we draw on, so we need to know when it's created
    in order to do anything useful. 

    Because of this, that's where we set up the SDL thread
*/
class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, 
    View.OnKeyListener, View.OnTouchListener, SensorEventListener  {

    // constructor, and a few callbacks like surfaceCreated, surfaceDestroyed,
    // surfaceChanged, onKey, onTouch, etc.

}

When the app is starting up, the Java code in SDLActivity creates Android UI widgets so that the application may display something, and immediately, a resize event is fired, letting it know how many real estate we have available (the resolution, if you will). This triggers a routine to choose the pixel format, with a lovely switch and plenty of hardcoded values.

// Called when the surface is resized
@Override
public void surfaceChanged(SurfaceHolder holder,
                            int format, int width, int height) {
    Log.v("SDL", "surfaceChanged()");

    int sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 by default
    switch (format) {
    case PixelFormat.RGBA_4444:
        Log.v("SDL", "pixel format RGBA_4444");
        sdlFormat = 0x15421002; // SDL_PIXELFORMAT_RGBA4444
        break;
    case PixelFormat.RGBA_8888:
        Log.v("SDL", "pixel format RGBA_8888");
        sdlFormat = 0x16462004; // SDL_PIXELFORMAT_RGBA8888
        break;
    // etc.
    default:
        Log.v("SDL", "pixel format unknown " + format);
        break;
    }

    mWidth = width;
    mHeight = height;
    SDLActivity.onNativeResize(width, height, sdlFormat);
    Log.v("SDL", "Window size:" + width + "x"+height);

    SDLActivity.startApp();
}

That routine calls startApp, which creates the actual SDL thread, which runs the run method of the SDLMain class, which extends Runnable (a Java interface used to specify what is supposed to run in a thread). That method calls nativeInit - and that is our first native call right there.

/**
    Simple nativeInit() runnable
*/
class SDLMain implements Runnable {
    @Override
    public void run() {
        // Runs SDL_main()
        SDLActivity.nativeInit();

        //Log.v("SDL", "SDL thread terminated");
    }
}

nativeInit is declared in SDLActivity.java as a native method, along with a few other entry points. Fortunately these are pretty simple, as they only take primitive types like ints or floats.

// C functions we call
public static native void nativeInit();
public static native void nativeLowMemory();
public static native void nativeQuit();
public static native void nativePause();
public static native void nativeResume();
public static native void onNativeResize(int x, int y, int format);
public static native void onNativeKeyDown(int keycode);
public static native void onNativeKeyUp(int keycode);
public static native void onNativeTouch(int touchDevId, int pointerFingerId,
                                        int action, float x, 
                                        float y, float p);
public static native void onNativeAccel(float x, float y, float z);
public static native void nativeRunAudioThread();

Very well, so the native keyword in Java means those methods are not actually defined there, but in a native library somewhere. Where are they declared? In libSDL2.so of course! Let’s take a look at the source for nativeInit, in SDL’s source directory, precisely in src/main/android/SDL_android_main.cpp

#include <jni.h>

// Called before SDL_main() to initialize JNI bindings in SDL library
extern "C" void SDL_Android_Init(JNIEnv* env, jclass cls);

// Start up the SDL app
extern "C" void Java_org_libsdl_app_SDLActivity_nativeInit( \
  JNIEnv* env, jclass cls, jobject obj) {
    /* This interface could expand with ABI negotiation, calbacks, etc. */
    SDL_Android_Init(env, cls);

    SDL_SetMainReady();

    /* Run the application code! */
    int status;
    char *argv[2];
    argv[0] = strdup("SDL_app");
    argv[1] = NULL;
    status = SDL_main(1, argv);

    /* Do not issue an exit or the whole application will terminate instead of just the SDL thread */
    //exit(status);
}

After some more initialization, the actual SDL_main is called, with fake command-line arguments, to act as if it was launched from the shell, just as it would have been on a Desktop operating system.

You’ll notice that the name of the C symbol is Java_org_libsdl_app_SDLActivity_nativeInit, ie. it is fully qualified with the package name and the class name of the corresponding Java construct. That’s what it takes to call native code from Java. But what if we want to do it the other way around?

Calling Java from C

If we take another look at SDLActivity.java, we find methods that are meant to be called from C:

// Java functions called from C

public static boolean createGLContext(int majorVersion, int minorVersion, int[] attribs) {
    return initEGL(majorVersion, minorVersion, attribs);
}

public static void flipBuffers() {
    flipEGL();
}

public static boolean setActivityTitle(String title) {
    // Called from SDLMain() thread and can't directly affect the view
    return mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title);
}

public static boolean sendMessage(int command, int param) {
    return mSingleton.sendCommand(command, Integer.valueOf(param));
}

// etc.

How does one call those from C exactly? Let’s find out by studying the simplest example, calling into SDLActivity.flipBuffers(). We find such a call in the C++ method Android_JNI_SwapWindow, defined in src/core/android/SDL_android.cpp:

extern "C" void Android_JNI_SwapWindow() {
    JNIEnv *mEnv = Android_JNI_GetEnv();
    mEnv->CallStaticVoidMethod(mActivityClass, midFlipBuffers);
}

There are familiar things - we’ve seen the JNIEnv earlier, it is automatically passed when calling C from Java. So it makes sense that we need to use it when calling Java from C. There is also something new: what is midFlipBuffers? And mActivityClass?

As it turns out, those are global references to Java symbols, and they are looked up once when initializing the SDL JNI layer, in SDL_Android_Init:

// Called before SDL_main() to initialize JNI bindings
extern "C" void SDL_Android_Init(JNIEnv* mEnv, jclass cls) {
    __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init()");

    Android_JNI_SetupThread();

    mActivityClass = (jclass)mEnv->NewGlobalRef(cls);

    midCreateGLContext = mEnv->GetStaticMethodID(mActivityClass,
                                "createGLContext","(II[I)Z");
    midFlipBuffers = mEnv->GetStaticMethodID(mActivityClass,
                                "flipBuffers","()V");
    midAudioInit = mEnv->GetStaticMethodID(mActivityClass,
                                "audioInit", "(IZZI)V");
    midAudioWriteShortBuffer = mEnv->GetStaticMethodID(mActivityClass,
                                "audioWriteShortBuffer", "([S)V");
    midAudioWriteByteBuffer = mEnv->GetStaticMethodID(mActivityClass,
                                "audioWriteByteBuffer", "([B)V");
    midAudioQuit = mEnv->GetStaticMethodID(mActivityClass,
                                "audioQuit", "()V");

    bHasNewData = false;

    if(!midCreateGLContext || !midFlipBuffers || !midAudioInit ||
       !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioQuit) {
        __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL: Couldn't locate Java callbacks, check that they're named and typed correctly");
    }
    __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init() finished!");
}

What we have here is called ‘reflection’. It’s akin to meta-programming: we are looking up the addresses (or rather, references) of the SDLActivity class, along with a few static methods that we are going to want to call.

The GetStaticMethodID method of JNIEnv is particularly interesting: the first argument is a reference to the class to which the method we’re looking for belongs. The second argument is the name of the method. However that’s not enough: Java supports overloading method, which means there are methods with the same name but different signatures.

The signature is the list of argument types and the return type. It is encoded in a compact format, using letters to refer to primitive types. If we have a method taking an int and returning a float, it’ll be encoded as "(I)F". Here’s a cheat sheet for JNI argument strings:

  • B=byte
  • C=char
  • D=double
  • F=float
  • I=int
  • J=long
  • S=short
  • V=void
  • Z=boolean
  • Lfully-qualified-class=fully qualified class
  • [type=array of type>
  • (argument types)return type=method type. If no arguments, use empty argument types: (). If return type is void (or constructor) use (argument types)V.

If we look at the signature for createGLContext, which is "(II[I)Z", it’s coherent with what we have seen in the Java method declaration in SDLActivity:

public static boolean createGLContext(int majorVersion, int minorVersion, int[] attribs)

Global and local references

Let’s take the case of a C function that calls Java methods and is non-trivial. Here’s the C function that is used by SDL_RWOps, a structure useful to do input/output in a platform-independant way, when you use SDL.

extern "C" int Android_JNI_FileOpen(SDL_RWops* ctx,
        const char* fileName, const char*) {
    LocalReferenceHolder refs(__FUNCTION__);
    JNIEnv *mEnv = Android_JNI_GetEnv();

    if (!refs.init(mEnv)) {
        return -1;
    }

    if (!ctx) {
        return -1;
    }

    jstring fileNameJString = mEnv->NewStringUTF(fileName);
    ctx->hidden.androidio.fileNameRef = mEnv->NewGlobalRef(fileNameJString);
    ctx->hidden.androidio.inputStreamRef = NULL;
    ctx->hidden.androidio.readableByteChannelRef = NULL;
    ctx->hidden.androidio.readMethod = NULL;
    ctx->hidden.androidio.assetFileDescriptorRef = NULL;

    return Android_JNI_FileOpen(ctx);
}

This is just the icing, by the way. The actual cake is defined in a function called… also Android_JNI_FileOpen with a different signature, because this particular file is C++, which also supports function overloading. I’m leaving out the cake though, because the last thing I wanted to cover is at the beginning of the icing.

It’s these lines:

LocalReferenceHolder refs(__FUNCTION__);

if (!refs.init(mEnv)) {
    return -1;
}

Now where is that LocalReferenceHolder from? If we were in Java code it could come from the Android SDK, but we’re not - we’re in a C++ file buried deep down in the SDL code, where nobody looks and apparently nobody cares to talk about (as a quick Google search will reveal).

Let’s look at its definition (that’s C++ for you):

class LocalReferenceHolder
{
private:
    static int s_active;

public:
    static bool IsActive() {
        return s_active > 0;
    }

public:
    LocalReferenceHolder(const char *func) : m_env(NULL), m_func(func) {
#ifdef DEBUG_JNI
        SDL_Log("Entering function %s", m_func);
#endif
    }
    ~LocalReferenceHolder() {
#ifdef DEBUG_JNI
        SDL_Log("Leaving function %s", m_func);
#endif
        if (m_env) {
            m_env->PopLocalFrame(NULL);
            --s_active;
        }
    }

    bool init(JNIEnv *env, jint capacity = 16) {
        if (env->PushLocalFrame(capacity) < 0) {
            SDL_SetError("Failed to allocate enough JVM local references");
            return false;
        }
        ++s_active;
        m_env = env;
        return true;
    }

protected:
    JNIEnv *m_env;
    const char *m_func;
};
int LocalReferenceHolder::s_active;

Now, don’t ask me why the last line is there. As far as I know, s_active is already defined in the private section of the class. Maybe it’s a subtle way to make it readable from outside the class? In any case it doesn’t seem to be used in that file.

We can see that in the init function, the PushLocalFrame method of JNIEnv is called. If we look at the doc, here’s what we see:

Creates a new local reference frame, in which at least a given number of local references can be created. Returns 0 on success, a negative number and a pending OutOfMemoryError on failure.

Note that local references already created in previous local frames are still valid in the current local frame.

Now obviously a lot of that is jargon, but here’s the idea. When doing reflection, aka trying to meddle with the primal forces of Java structures living in the JVM from a language compiled to native code such as C or C++, you don’t get nice stuff like function pointers - you get references.

References are valid for a given context. That’s because the JVM, like any virtual machine (hence the VM) is allowed to do some crazy tricks, like inline functions, restructure stuff on the fly, and who knows what else in the holy name of performance. Point is, references are not valid forever - in this case, they are valid in the current local frame - something we can push when we need it, and that we can pop when we are done with it.

The init method of LocalReferenceHolder has a default capacity of 16, which is apparently enough for any SDL method implemented there, because none of these (as far as I can tell) specify a larger value - even though they could if they needed to.

Now that’s all nice and dandy but why is a class needed in the first place? Those PushLocalFrame and PopLocalFrame calls could simply be made within the functions that need them, and what’s more, with exactly the size they need instead of the default 16. So why?

You’ll notice PopLocalFrame is done in the destructor. And that’s because the destructor is guaranteed to be called whenever the object is freed. You’ll also notice, in the Android_JNI_FileOpen method, that the LocalReferenceHolder object is created on the stack. Which means as soon as the object goes out of scope (e.g. we exit the method for any reason), it will be freed, the destructor will be called, and PopLocalFrame will be called.

Of course, it could be done manually, inserting PopLocalFrame before every return. But returns are not the only thing that can interrupt a function. An exception being thrown could very well be an exit poing for our function, and the local frame would still need to be popped. So, that’s a neat little trick that the stuff we allocate is always freed.

Here’s what a naive version would look like:

extern "C" int Android_JNI_DoSomething() {
    JNIEnv *mEnv = Android_JNI_GetEnv();

    // Allocate a new local frame.
    if (env->PushLocalFrame(capacity) < 0) {
        SDL_SetError("Failed to allocate enough JVM local references");
        return -1;
    }

    // DANGEROUS SECTION
    // (some code here)

    // Free the local frame. If an exception is thrown in the dangerous section,
    // this call will never happen.
    env->PopLocalFrame(NULL);

    return 0;
}

Not only is there error checking code that would have to be repeated anywhere, but the PopLocalFrame would no longer be guaranteed. That’s why the stack-allocated object makes sense in this context.

There’s one final trick the LocalReferenceHolder does: you might have noticed the usage of __FUNCTION__ in the file open routine, when constructing the LocalReferenceHolder. In C, this is a predefined macro that evaluates to a null-terminated string which contains the name of.. the current function. As you can see in between the DEBUG_JNI ifdef guards, this is very useful to know what is actually going on - I am ready to bet that those are there not by overengineering, but simply by practicality when debugging the darn thing in the first place.

Conclusion

This article is actually quite long - I just wanted to show a neat few tricks that I have discovered when trying to find out how SDL worked on Android in general, and on the Ouya in particular.

There seems to be very few posts about this on the internet, so hopefully, every little bit helps. Again, I am just reverse engineering stuff that interests me here, if I got some of my facts arguably wrong, please [tell me so][ndd].

What I’d like to do next: create a layer that allows one to access the Ouya SDK via ooc - to get access to controllers, to the purchases API, and their storage facility. Hopefully then I’ll be able to make Bioboy work on there. I am really looking forward to see how it plays with a console controller on the big screen!

Now get an Ouya! It’s a very neat, hackable, developer-friendly device, and I swear on my language’s grammar, I am not even paid to say that.