diff --git a/src/alstream.cpp b/src/alstream.cpp
index da1e844..b0ca1b1 100644
--- a/src/alstream.cpp
+++ b/src/alstream.cpp
@@ -23,6 +23,7 @@
 
 #include "sharedstate.h"
 #include "sharedmidistate.h"
+#include "eventthread.h"
 #include "filesystem.h"
 #include "aldatasource.h"
 #include "fluid-fun.h"
@@ -361,6 +362,8 @@ void ALStream::streamData()
 	 * refill and queue them up again */
 	while (true)
 	{
+		shState->rtData().syncPoint.passSecondarySync();
+
 		ALint procBufs = AL::Source::getProcBufferCount(alSrc);
 
 		while (procBufs--)
diff --git a/src/audio.cpp b/src/audio.cpp
index aa16e7d..945aed0 100644
--- a/src/audio.cpp
+++ b/src/audio.cpp
@@ -25,6 +25,7 @@
 #include "soundemitter.h"
 #include "sharedstate.h"
 #include "sharedmidistate.h"
+#include "eventthread.h"
 #include "sdl-util.h"
 
 #include <string>
@@ -40,6 +41,8 @@ struct AudioPrivate
 
 	SoundEmitter se;
 
+	SyncPoint &syncPoint;
+
 	/* The 'MeWatch' is responsible for detecting
 	 * a playing ME, quickly fading out the BGM and
 	 * keeping it paused/stopped while the ME plays,
@@ -60,11 +63,12 @@ struct AudioPrivate
 		MeWatchState state;
 	} meWatch;
 
-	AudioPrivate(const Config &conf)
+	AudioPrivate(RGSSThreadData &rtData)
 	    : bgm(ALStream::Looped, "bgm"),
 	      bgs(ALStream::Looped, "bgs"),
 	      me(ALStream::NotLooped, "me"),
-	      se(conf)
+	      se(rtData.config),
+	      syncPoint(rtData.syncPoint)
 	{
 		meWatch.state = MeNotPlaying;
 		meWatch.thread = createSDLThread
@@ -84,6 +88,8 @@ struct AudioPrivate
 
 		while (true)
 		{
+			syncPoint.passSecondarySync();
+
 			if (meWatch.termReq)
 				return;
 
@@ -231,8 +237,8 @@ struct AudioPrivate
 	}
 };
 
-Audio::Audio(const Config &conf)
-	: p(new AudioPrivate(conf))
+Audio::Audio(RGSSThreadData &rtData)
+	: p(new AudioPrivate(rtData))
 {}
 
 
diff --git a/src/audio.h b/src/audio.h
index ea2a357..b0aec26 100644
--- a/src/audio.h
+++ b/src/audio.h
@@ -33,7 +33,7 @@
  *   quite make out their meaning yet) */
 
 struct AudioPrivate;
-struct Config;
+struct RGSSThreadData;
 
 class Audio
 {
@@ -70,7 +70,7 @@ public:
 	void reset();
 
 private:
-	Audio(const Config &conf);
+	Audio(RGSSThreadData &rtData);
 	~Audio();
 
 	friend struct SharedStatePrivate;
diff --git a/src/eventthread.cpp b/src/eventthread.cpp
index 64212b2..f87b5aa 100644
--- a/src/eventthread.cpp
+++ b/src/eventthread.cpp
@@ -76,6 +76,8 @@ void EventThread::process(RGSSThreadData &rtData)
 	SDL_Window *win = rtData.window;
 	UnidirMessage<Vec2i> &windowSizeMsg = rtData.windowSizeMsg;
 
+	SDL_SetEventFilter(eventFilter, &rtData);
+
 	fullscreen = rtData.config.fullscreen;
 	int toggleFSMod = rtData.config.anyAltToggleFS ? KMOD_ALT : KMOD_LALT;
 
@@ -402,12 +404,63 @@ void EventThread::process(RGSSThreadData &rtData)
 			break;
 	}
 
+	/* Just in case */
+	rtData.syncPoint.resumeThreads();
+
 	if (SDL_JoystickGetAttached(js))
 		SDL_JoystickClose(js);
 
 	delete sMenu;
 }
 
+int EventThread::eventFilter(void *data, SDL_Event *event)
+{
+	RGSSThreadData &rtData = *static_cast<RGSSThreadData*>(data);
+
+	switch (event->type)
+	{
+	case SDL_APP_WILLENTERBACKGROUND :
+		Debug() << "SDL_APP_WILLENTERBACKGROUND";
+
+		rtData.syncPoint.haltThreads();
+
+		return 0;
+
+	case SDL_APP_DIDENTERBACKGROUND :
+		Debug() << "SDL_APP_DIDENTERBACKGROUND";
+		return 0;
+
+	case SDL_APP_WILLENTERFOREGROUND :
+		Debug() << "SDL_APP_WILLENTERFOREGROUND";
+		return 0;
+
+	case SDL_APP_DIDENTERFOREGROUND :
+		Debug() << "SDL_APP_DIDENTERFOREGROUND";
+
+		rtData.syncPoint.resumeThreads();
+
+		return 0;
+
+	case SDL_APP_TERMINATING :
+		Debug() << "SDL_APP_TERMINATING";
+		return 0;
+
+	case SDL_APP_LOWMEMORY :
+		Debug() << "SDL_APP_LOWMEMORY";
+		return 0;
+
+	case SDL_RENDER_TARGETS_RESET :
+		Debug() << "****** SDL_RENDER_TARGETS_RESET";
+		return 0;
+
+	case SDL_RENDER_DEVICE_RESET :
+		Debug() << "****** SDL_RENDER_DEVICE_RESET";
+		return 0;
+	}
+
+	return 1;
+}
+
 void EventThread::cleanup()
 {
 	SDL_Event event;
@@ -539,3 +592,87 @@ void EventThread::notifyFrame()
 	event.user.type = usrIdStart + UPDATE_FPS;
 	SDL_PushEvent(&event);
 }
+
+void SyncPoint::haltThreads()
+{
+	if (mainSync.locked)
+		return;
+
+	/* Lock the reply sync first to avoid races */
+	reply.lock();
+
+	/* Lock main sync and sleep until RGSS thread
+	 * reports back */
+	mainSync.lock();
+	reply.waitForUnlock();
+
+	/* Now that the RGSS thread is asleep, we can
+	 * safely put the other threads to sleep as well
+	 * without causing deadlocks */
+	secondSync.lock();
+}
+
+void SyncPoint::resumeThreads()
+{
+	if (!mainSync.locked)
+		return;
+
+	mainSync.unlock(false);
+	secondSync.unlock(true);
+}
+
+bool SyncPoint::mainSyncLocked()
+{
+	return mainSync.locked;
+}
+
+void SyncPoint::waitMainSync()
+{
+	reply.unlock(false);
+	mainSync.waitForUnlock();
+}
+
+void SyncPoint::passSecondarySync()
+{
+	if (!secondSync.locked)
+		return;
+
+	secondSync.waitForUnlock();
+}
+
+SyncPoint::Util::Util()
+{
+	mut = SDL_CreateMutex();
+	cond = SDL_CreateCond();
+}
+
+SyncPoint::Util::~Util()
+{
+	SDL_DestroyCond(cond);
+	SDL_DestroyMutex(mut);
+}
+
+void SyncPoint::Util::lock()
+{
+	locked.set();
+}
+
+void SyncPoint::Util::unlock(bool multi)
+{
+	locked.clear();
+
+	if (multi)
+		SDL_CondBroadcast(cond);
+	else
+		SDL_CondSignal(cond);
+}
+
+void SyncPoint::Util::waitForUnlock()
+{
+	SDL_LockMutex(mut);
+
+	while (locked)
+		SDL_CondWait(cond, mut);
+
+	SDL_UnlockMutex(mut);
+}
diff --git a/src/eventthread.h b/src/eventthread.h
index 9dcc21f..46051f1 100644
--- a/src/eventthread.h
+++ b/src/eventthread.h
@@ -39,6 +39,7 @@
 struct RGSSThreadData;
 typedef struct ALCdevice_struct ALCdevice;
 struct SDL_Window;
+union SDL_Event;
 
 #define MAX_FINGERS 4
 
@@ -98,6 +99,8 @@ public:
 	void notifyFrame();
 
 private:
+	static int eventFilter(void *, SDL_Event*);
+
 	void resetInputStates();
 	void setFullscreen(SDL_Window *, bool mode);
 	void updateCursorState(bool inWindow);
@@ -174,6 +177,39 @@ private:
 	T current;
 };
 
+struct SyncPoint
+{
+	/* Used by eventFilter to control sleep/wakeup */
+	void haltThreads();
+	void resumeThreads();
+
+	/* Used by RGSS thread */
+	bool mainSyncLocked();
+	void waitMainSync();
+
+	/* Used by secondary (audio) threads */
+	void passSecondarySync();
+
+private:
+	struct Util
+	{
+		Util();
+		~Util();
+
+		void lock();
+		void unlock(bool multi);
+		void waitForUnlock();
+
+		AtomicFlag locked;
+		SDL_mutex *mut;
+		SDL_cond *cond;
+	};
+
+	Util mainSync;
+	Util reply;
+	Util secondSync;
+};
+
 struct RGSSThreadData
 {
 	/* Main thread sets this to request RGSS thread to terminate */
@@ -191,6 +227,7 @@ struct RGSSThreadData
 	EventThread *ethread;
 	UnidirMessage<Vec2i> windowSizeMsg;
 	UnidirMessage<BDescVec> bindingUpdateMsg;
+	SyncPoint syncPoint;
 
 	const char *argv0;
 
diff --git a/src/graphics.cpp b/src/graphics.cpp
index 2f31077..a27914e 100644
--- a/src/graphics.cpp
+++ b/src/graphics.cpp
@@ -466,6 +466,7 @@ struct GraphicsPrivate
 
 	ScreenScene screen;
 	RGSSThreadData *threadData;
+	SDL_GLContext glCtx;
 
 	int frameRate;
 	int frameCount;
@@ -489,6 +490,7 @@ struct GraphicsPrivate
 	      winSize(rtData->config.defScreenW, rtData->config.defScreenH),
 	      screen(scRes.x, scRes.y),
 	      threadData(rtData),
+	      glCtx(SDL_GL_GetCurrentContext()),
 	      frameRate(DEF_FRAMERATE),
 	      frameCount(0),
 	      brightness(255),
@@ -622,6 +624,21 @@ struct GraphicsPrivate
 
 		swapGLBuffer();
 	}
+
+	void checkSyncLock()
+	{
+		if (!threadData->syncPoint.mainSyncLocked())
+			return;
+
+		/* Releasing the GL context before sleeping and making it
+		 * current again on wakeup seems to avoid the context loss
+		 * when the app moves into the background on Android */
+		SDL_GL_MakeCurrent(threadData->window, 0);
+		threadData->syncPoint.waitMainSync();
+		SDL_GL_MakeCurrent(threadData->window, glCtx);
+
+		fpsLimiter.resetFrameAdjust();
+	}
 };
 
 Graphics::Graphics(RGSSThreadData *data)
@@ -651,6 +668,7 @@ Graphics::~Graphics()
 void Graphics::update()
 {
 	p->checkShutDownReset();
+	p->checkSyncLock();
 
 	if (p->frozen)
 		return;
@@ -692,6 +710,8 @@ void Graphics::transition(int duration,
                           const char *filename,
                           int vague)
 {
+	p->checkSyncLock();
+
 	if (!p->frozen)
 		return;
 
@@ -752,6 +772,8 @@ void Graphics::transition(int duration,
 			return;
 		}
 
+		p->checkSyncLock();
+
 		const float prog = i * (1.0 / duration);
 
 		if (transMap)
diff --git a/src/sharedstate.cpp b/src/sharedstate.cpp
index ff0989b..b7a2c27 100644
--- a/src/sharedstate.cpp
+++ b/src/sharedstate.cpp
@@ -108,7 +108,7 @@ struct SharedStatePrivate
 	      midiState(threadData->config),
 	      graphics(threadData),
 	      input(*threadData),
-	      audio(threadData->config),
+	      audio(*threadData),
 	      fontState(threadData->config),
 	      stampCounter(0)
 	{