Android lifecycle cleanups

- Differentiate between cpu_pause() when going to menu (and app is still foregrounded, and cpu_pauseBackground()
      when a system backgrounding event has occurred.
    - Destroy/free audio resources owned by CPU thread when background is requested, put CPU thread into waiting upon
      pthread condition to resume.
    - Mobile device now always signals pthread condition to force CPU thread resume from background
This commit is contained in:
Aaron Culliney 2015-07-27 22:36:39 -07:00
parent 16a743fd50
commit 25b9f3d3f3
6 changed files with 159 additions and 73 deletions

View File

@ -41,6 +41,7 @@ public class Apple2Activity extends Activity {
private final static int MAX_FINGERS = 32;// HACK ...
private String mDataDir = null;
private boolean mSetUncaughtExceptionHandler = false;
private Apple2View mView = null;
private ArrayList<Apple2MenuView> mMenuStack = new ArrayList<Apple2MenuView>();
@ -76,7 +77,7 @@ public class Apple2Activity extends Activity {
public native void nativeOnResume(boolean isSystemResume);
public native void nativeOnPause();
public native void nativeOnPause(boolean isSystemPause);
public native void nativeOnQuit();
@ -92,7 +93,7 @@ public class Apple2Activity extends Activity {
// HACK NOTE 2015/02/22 : Apparently native code cannot easily access stuff in the APK ... so copy various resources
// out of the APK and into the /data/data/... for ease of access. Because this is FOSS software we don't care about
// security or DRM for these assets =)
private String firstTimeInitialization() {
private String _firstTimeInitialization() {
String dataDir = null;
try {
@ -153,25 +154,12 @@ public class Apple2Activity extends Activity {
os.close();
}
@Override
public void onCreate(Bundle savedInstanceState) {
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectAll()
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
/*.detectLeakedClosableObjects()*/
.penaltyLog()
.penaltyDeath()
.build());
private void _setCustomExceptionHandler() {
if (mSetUncaughtExceptionHandler) {
return;
}
super.onCreate(savedInstanceState);
mSetUncaughtExceptionHandler = true;
// Immediately set up exception handler ...
final String homeDir = "/data/data/" + this.getPackageName();
final Thread.UncaughtExceptionHandler defaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@ -221,10 +209,30 @@ public class Apple2Activity extends Activity {
defaultExceptionHandler.uncaughtException(thread, t);
}
});
}
@Override
public void onCreate(Bundle savedInstanceState) {
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectAll()
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
/*.detectLeakedClosableObjects()*/
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate(savedInstanceState);
Log.e(TAG, "onCreate()");
mDataDir = firstTimeInitialization();
_setCustomExceptionHandler();
mDataDir = _firstTimeInitialization();
// get device audio parameters for native OpenSLES
mSampleRate = DevicePropertyCalculator.getRecommendedSampleRate(this);
@ -297,7 +305,7 @@ public class Apple2Activity extends Activity {
}
} while (apple2MenuView != null);
nativeOnPause();
nativeOnPause(true);
}
@Override
@ -372,17 +380,14 @@ public class Apple2Activity extends Activity {
@Override
public boolean onTouchEvent(MotionEvent event) {
do {
Apple2MainMenu mainMenu = mView.getMainMenu();
if (mainMenu == null) {
break;
}
Apple2SettingsMenu settingsMenu = mainMenu.getSettingsMenu();
Apple2DisksMenu disksMenu = mView.getDisksMenu();
if (settingsMenu != null && settingsMenu.isShowing()) {
break;
}
if (disksMenu != null && disksMenu.isShowing()) {
Apple2MenuView apple2MenuView = peekApple2View();
if (apple2MenuView != null) {
break;
}
@ -441,7 +446,7 @@ public class Apple2Activity extends Activity {
public synchronized void pushApple2View(Apple2MenuView apple2MenuView) {
mMenuStack.add(apple2MenuView);
View menuView = apple2MenuView.getView();
nativeOnPause();
nativeOnPause(false);
addContentView(menuView, new FrameLayout.LayoutParams(getWidth(), getHeight()));
}
@ -456,6 +461,15 @@ public class Apple2Activity extends Activity {
return apple2MenuView;
}
public synchronized Apple2MenuView peekApple2View() {
int lastIndex = mMenuStack.size() - 1;
if (lastIndex < 0) {
return null;
}
return mMenuStack.get(lastIndex);
}
public synchronized Apple2MenuView popApple2View(Apple2MenuView apple2MenuView) {
boolean wasRemoved = mMenuStack.remove(apple2MenuView);
_disposeApple2View(apple2MenuView);
@ -495,7 +509,7 @@ public class Apple2Activity extends Activity {
}
public void maybeQuitApp() {
nativeOnPause();
nativeOnPause(false);
if (mQuitDialog == null) {
mQuitDialog = new AlertDialog.Builder(this).setIcon(R.drawable.ic_launcher).setCancelable(true).setTitle(R.string.quit_really).setMessage(R.string.quit_warning).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
@ -527,7 +541,7 @@ public class Apple2Activity extends Activity {
}
public void maybeReboot() {
nativeOnPause();
nativeOnPause(false);
if (mRebootDialog == null) {
mRebootDialog = new AlertDialog.Builder(this).setIcon(R.drawable.ic_launcher).setCancelable(true).setTitle(R.string.reboot_really).setMessage(R.string.reboot_warning).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override

View File

@ -201,7 +201,7 @@ public class Apple2MainMenu {
return;
}
mActivity.nativeOnPause();
mActivity.nativeOnPause(false);
mMainMenuPopup.showAtLocation(mParentView, Gravity.CENTER, 0, 0);
}

View File

@ -30,7 +30,6 @@ enum {
ANDROID_ACTION_POINTER_UP = 0x6,
};
static bool nativePaused = false;
static bool nativeRequestsShowMainMenu = false;
#if TESTING
@ -161,29 +160,28 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeGraphicsInitialized(JNIEnv
}
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnResume(JNIEnv *env, jobject obj, jboolean isSystemResume) {
if (!nativePaused) {
#warning FIXME ... replace nativePaused check with cpu_isPaused()
if (!cpu_isPaused()) {
return;
}
LOG("%s", "native onResume...");
if (isSystemResume) {
if (video_backend->animation_showPaused) {
video_backend->animation_showPaused();
}
} else {
nativePaused = false;
if (!isSystemResume) {
cpu_resume();
}
}
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnPause(JNIEnv *env, jobject obj) {
if (nativePaused) {
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnPause(JNIEnv *env, jobject obj, jboolean isSystemPause) {
if (cpu_isPaused()) {
return;
}
nativePaused = true;
LOG("%s", "native onPause...");
cpu_pause();
if (isSystemPause) {
// going to background
cpu_pauseBackground();
} else {
// going to menu
cpu_pause();
}
}
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeRender(JNIEnv *env, jobject obj) {
@ -191,7 +189,7 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeRender(JNIEnv *env, jobject
return;
}
if (!nativePaused) {
if (!cpu_isPaused()) {
c_keys_handle_input(-1, 0, 0);
}
@ -289,9 +287,8 @@ jboolean Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnTouch(JNIEnv *env, jo
return true;
}
if (nativePaused) {
if (cpu_isPaused()) {
LOG("UNPAUSING NATIVE CPU THREAD");
nativePaused = false;
cpu_resume();
return true;
}

View File

@ -269,6 +269,16 @@ static const char *log_end = "\n";
_LOG(__VA_ARGS__); \
} while (0)
#define RELEASE_BREAK() \
do { \
/* BOOM */ \
char *ptr = (char *)0xABADF000; \
*ptr = '\0';\
/* or if that worked, just deref NULL */ \
ptr = (char *)0x0; \
*ptr = '\0'; \
} while (0);
#define FREE(x) \
do { \
free((x)); \

View File

@ -81,7 +81,10 @@ bool alt_speed_enabled = false;
// misc
volatile uint8_t emul_reinitialize = 1;
unsigned long emul_reinitialize_audio = 1UL;
bool emul_reinitialize_audio = 1UL;
#if MOBILE_DEVICE
static bool emul_reinitialize_background = true;
#endif
pthread_t cpu_thread_id = 0;
pthread_mutex_t interface_mutex = { 0 };
pthread_cond_t dbg_thread_cond = PTHREAD_COND_INITIALIZER;
@ -170,35 +173,48 @@ void timing_toggleCPUSpeed(void) {
void timing_reinitializeAudio(void) {
assert(cpu_isPaused() || (pthread_self() == cpu_thread_id));
__sync_fetch_and_or(&emul_reinitialize_audio, 1UL);
emul_reinitialize_audio = true;
}
void cpu_pause(void) {
assert(pthread_self() != cpu_thread_id);
_LOCK_CPU_THREAD();
#if MOBILE_DEVICE
if (emul_reinitialize_background) {
RELEASE_LOG("CPU thread already paused ...");
_UNLOCK_CPU_THREAD();
return;
}
#endif
#ifdef AUDIO_ENABLED
audio_pause();
#endif
is_paused = true;
}
static void _cpu_thread_really_start(void) {
int err = 0;
if ( (err = pthread_cond_signal(&cpu_thread_cond)) ) {
ERRLOG("pthread_cond_signal : %d", err);
}
#if MOBILE_DEVICE
void cpu_pauseBackground(void) {
assert(pthread_self() != cpu_thread_id);
_LOCK_CPU_THREAD();
emul_reinitialize_background = true;
_UNLOCK_CPU_THREAD();
}
#endif
void cpu_resume(void) {
assert(pthread_self() != cpu_thread_id);
assert(cpu_isPaused());
is_paused = false;
static pthread_once_t onceToken = PTHREAD_ONCE_INIT;
if (onceToken == PTHREAD_ONCE_INIT) {
int err = 0;
if ( (err = pthread_once(&onceToken, _cpu_thread_really_start)) ) {
ERRLOG("pthread_once : %d", err);
}
#if MOBILE_DEVICE
int err = pthread_cond_signal(&cpu_thread_cond);
if (err) {
RELEASE_ERRLOG("pthread_cond_signal : %d", err);
RELEASE_BREAK();
}
#endif
#ifdef AUDIO_ENABLED
audio_resume();
@ -243,20 +259,46 @@ void *cpu_thread(void *dummyptr) {
unsigned int dbg_cycles_executed = 0;
#endif
#if !TESTING
pthread_mutex_lock(&interface_mutex);
int err = 0;
LOG("cpu_thread : waiting for splash screen completion...");
pthread_cond_wait(&cpu_thread_cond, &interface_mutex);
pthread_mutex_unlock(&interface_mutex);
LOG("cpu_thread : starting...");
#endif
do
{
#if MOBILE_DEVICE && !TESTING
if (emul_reinitialize_background) {
speaker_destroy();
MB_Destroy();
audio_shutdown();
int err = TEMP_FAILURE_RETRY(pthread_mutex_lock(&interface_mutex));
if (err) {
RELEASE_LOG("Error locking CPU mutex : %d", err);
RELEASE_BREAK();
}
is_paused = true;
emul_reinitialize_background = false;
LOG("cpu_thread : waiting for splash screen completion...");
err = pthread_cond_wait(&cpu_thread_cond, &interface_mutex);
if (err) {
RELEASE_LOG("Error waiting for CPU condition : %d", err);
RELEASE_BREAK();
}
err = TEMP_FAILURE_RETRY(pthread_mutex_unlock(&interface_mutex));
if (err) {
RELEASE_LOG("Error unlocking CPU mutex : %d", err);
RELEASE_BREAK();
}
LOG("cpu_thread : starting...");
emul_reinitialize_audio = true;
}
#endif
#ifdef AUDIO_ENABLED
bool reinit_audio = __sync_fetch_and_and(&emul_reinitialize_audio, 0UL);
if (reinit_audio) {
if (emul_reinitialize_audio) {
emul_reinitialize_audio = false;
speaker_destroy();
MB_Destroy();
audio_shutdown();
@ -266,6 +308,7 @@ void *cpu_thread(void *dummyptr) {
MB_Initialize();
}
#endif
if (emul_reinitialize) {
reinitialize();
}
@ -457,6 +500,12 @@ void *cpu_thread(void *dummyptr) {
break;
}
#if MOBILE_DEVICE
if (UNLIKELY(emul_reinitialize_background)) {
break;
}
#endif
if (UNLIKELY(emulator_shutting_down)) {
break;
}

View File

@ -74,7 +74,7 @@ struct timespec timespec_diff(struct timespec start, struct timespec end, bool *
*/
void timing_toggleCPUSpeed(void);
#if !defined(MOBILE_DEVICE)
#if !MOBILE_DEVICE
/*
* check whether automatic adjusting of CPU speed is configured.
*/
@ -97,10 +97,26 @@ void timing_reinitializeAudio(void);
void *cpu_thread(void *ignored);
/*
* Pause timing/CPU thread
* Pause timing/CPU thread.
*
* This may block for a short amount of time to grab the appropriate mutex. CPU thread is blocked upon function return,
* until call to cpu_resume() is made.
*/
void cpu_pause(void);
#if MOBILE_DEVICE
/*
* Pause timing/CPU thread because of a system backgrounding event.
*
* This may block for a short amount of time to grab the appropriate mutex, toggle a dirty bit, and release the mutex.
* NOTE: CPU thread is not likely to actually be paused upon function return, (but will be shortly thereafter).
*
* This should also destroy/free any audio resources (speaker, mockingboard) managed by the CPU thread back to system.
* Audio resources will be automatically recreated upon call to cpu_resume()
*/
void cpu_pauseBackground(void);
#endif
/*
* Resume timing/CPU thread
*/