mirror of
https://github.com/mauiaaron/apple2.git
synced 2025-02-16 02:31:26 +00:00
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:
parent
16a743fd50
commit
25b9f3d3f3
@ -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
|
||||
|
@ -201,7 +201,7 @@ public class Apple2MainMenu {
|
||||
return;
|
||||
}
|
||||
|
||||
mActivity.nativeOnPause();
|
||||
mActivity.nativeOnPause(false);
|
||||
|
||||
mMainMenuPopup.showAtLocation(mParentView, Gravity.CENTER, 0, 0);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
10
src/common.h
10
src/common.h
@ -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)); \
|
||||
|
97
src/timing.c
97
src/timing.c
@ -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;
|
||||
}
|
||||
|
20
src/timing.h
20
src/timing.h
@ -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
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user