Skip to content
Permalink
Browse files
Merge pull request #9229 from JosJuice/android-emulationactivity-finish
Android: Handle failed boots correctly
  • Loading branch information
JosJuice committed Nov 9, 2020
2 parents a9ef7e0 + 8181a7b commit 72997c1
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 84 deletions.
@@ -12,6 +12,8 @@
import android.view.Surface;
import android.widget.Toast;

import androidx.fragment.app.FragmentManager;

import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.dialogs.AlertMessage;
import org.dolphinemu.dolphinemu.utils.CompressCallback;
@@ -403,15 +405,13 @@ public static native void SetMotionSensorsEnabled(boolean accelerometerEnabled,
*/
public static native void StopEmulation();

public static native boolean IsBooting();

public static native void WaitUntilDoneBooting();

/**
* Returns true if emulation is running (or is paused).
*/
public static native boolean IsRunning();

public static native boolean IsRunningAndStarted();

/**
* Enables or disables CPU block profiling
*
@@ -487,7 +487,7 @@ private static void CheckGameMetadataValid()
private static native String GetCurrentTitleDescriptionUnchecked();

public static boolean displayAlertMsg(final String caption, final String text,
final boolean yesNo, final boolean isWarning)
final boolean yesNo, final boolean isWarning, final boolean nonBlocking)
{
Log.error("[NativeLibrary] Alert: " + text);
final EmulationActivity emulationActivity = sEmulationActivity.get();
@@ -498,10 +498,9 @@ public static boolean displayAlertMsg(final String caption, final String text,
}
else
{
// AlertMessages while the core is booting will deadlock if WaitUntilDoneBooting is called.
// We also can't use AlertMessages unless we have a non-null activity reference.
// As a fallback, we use toasts instead.
if (emulationActivity == null || IsBooting())
// We can't use AlertMessages unless we have a non-null activity reference
// and are allowed to block. As a fallback, we can use toasts.
if (emulationActivity == null || nonBlocking)
{
new Handler(Looper.getMainLooper()).post(
() -> Toast.makeText(DolphinApplication.getAppContext(), text, Toast.LENGTH_LONG)
@@ -511,9 +510,22 @@ public static boolean displayAlertMsg(final String caption, final String text,
{
sIsShowingAlertMessage = true;

emulationActivity.runOnUiThread(
() -> AlertMessage.newInstance(caption, text, yesNo, isWarning)
.show(emulationActivity.getSupportFragmentManager(), "AlertMessage"));
emulationActivity.runOnUiThread(() ->
{
FragmentManager fragmentManager = emulationActivity.getSupportFragmentManager();
if (fragmentManager.isStateSaved())
{
// The activity is being destroyed, so we can't use it to display an AlertMessage.
// Fall back to a toast.
Toast.makeText(emulationActivity, text, Toast.LENGTH_LONG).show();
NotifyAlertMessageLock();
}
else
{
AlertMessage.newInstance(caption, text, yesNo, isWarning)
.show(fragmentManager, "AlertMessage");
}
});

// Wait for the lock to notify that it is complete.
synchronized (sAlertMessageLock)
@@ -563,6 +575,20 @@ public static void clearEmulationActivity()
sEmulationActivity.clear();
}

public static void finishEmulationActivity()
{
final EmulationActivity emulationActivity = sEmulationActivity.get();
if (emulationActivity == null)
{
Log.warning("[NativeLibrary] EmulationActivity is null.");
}
else
{
Log.verbose("[NativeLibrary] Finishing EmulationActivity.");
emulationActivity.runOnUiThread(emulationActivity::finish);
}
}

public static void updateTouchPointer()
{
final EmulationActivity emulationActivity = sEmulationActivity.get();
@@ -81,10 +81,12 @@ public final class EmulationActivity extends AppCompatActivity
private String[] mPaths;
private boolean mIgnoreWarnings;
private static boolean sUserPausedEmulation;
private boolean mMenuToastShown;

public static final String EXTRA_SELECTED_GAMES = "SelectedGames";
public static final String EXTRA_IGNORE_WARNINGS = "IgnoreWarnings";
public static final String EXTRA_USER_PAUSED_EMULATION = "sUserPausedEmulation";
public static final String EXTRA_MENU_TOAST_SHOWN = "MenuToastShown";

@Retention(SOURCE)
@IntDef({MENU_ACTION_EDIT_CONTROLS_PLACEMENT, MENU_ACTION_TOGGLE_CONTROLS, MENU_ACTION_ADJUST_SCALE,
@@ -212,8 +214,8 @@ protected void onCreate(Bundle savedInstanceState)
mPaths = gameToEmulate.getStringArrayExtra(EXTRA_SELECTED_GAMES);
mIgnoreWarnings = gameToEmulate.getBooleanExtra(EXTRA_IGNORE_WARNINGS, false);
sUserPausedEmulation = gameToEmulate.getBooleanExtra(EXTRA_USER_PAUSED_EMULATION, false);
mMenuToastShown = false;
activityRecreated = false;
Toast.makeText(this, R.string.emulation_menu_help, Toast.LENGTH_LONG).show();
}
else
{
@@ -260,8 +262,9 @@ protected void onSaveInstanceState(@NonNull Bundle outState)
mEmulationFragment.saveTemporaryState();
}
outState.putStringArray(EXTRA_SELECTED_GAMES, mPaths);
outState.putBoolean(EXTRA_USER_PAUSED_EMULATION, mIgnoreWarnings);
outState.putBoolean(EXTRA_IGNORE_WARNINGS, mIgnoreWarnings);
outState.putBoolean(EXTRA_USER_PAUSED_EMULATION, sUserPausedEmulation);
outState.putBoolean(EXTRA_MENU_TOAST_SHOWN, mMenuToastShown);
super.onSaveInstanceState(outState);
}

@@ -270,6 +273,7 @@ protected void restoreState(Bundle savedInstanceState)
mPaths = savedInstanceState.getStringArray(EXTRA_SELECTED_GAMES);
mIgnoreWarnings = savedInstanceState.getBoolean(EXTRA_IGNORE_WARNINGS);
sUserPausedEmulation = savedInstanceState.getBoolean(EXTRA_USER_PAUSED_EMULATION);
mMenuToastShown = savedInstanceState.getBoolean(EXTRA_MENU_TOAST_SHOWN);
}

@Override
@@ -306,6 +310,13 @@ protected void onStop()

public void onTitleChanged()
{
if (!mMenuToastShown)
{
// The reason why this doesn't run earlier is because we want to be sure the boot succeeded.
Toast.makeText(this, R.string.emulation_menu_help, Toast.LENGTH_LONG).show();
mMenuToastShown = true;
}

setTitle(NativeLibrary.GetCurrentTitleDescription());
updateMotionListener();

@@ -342,7 +353,6 @@ public boolean onKeyLongPress(int keyCode, @NonNull KeyEvent event)
if (keyCode == KeyEvent.KEYCODE_BACK)
{
mEmulationFragment.stopEmulation();
finish();
return true;
}
return super.onKeyLongPress(keyCode, event);
@@ -617,7 +627,6 @@ public void handleMenuAction(@MenuAction int menuAction)

case MENU_ACTION_EXIT:
mEmulationFragment.stopEmulation();
finish();
break;
}
}
@@ -330,16 +330,6 @@ public synchronized void clearSurface()
mSurface = null;
Log.debug("[EmulationFragment] Surface destroyed.");

if (state != State.STOPPED && !NativeLibrary.IsShowingAlertMessage())
{
// In order to avoid dereferencing nullptr, we must not destroy the surface while booting
// the core, so wait here if necessary. An easy (but not 100% consistent) way to reach
// this method while the core is booting is by having landscape orientation lock enabled
// and starting emulation while the phone is in portrait mode, leading to the activity
// being recreated very soon after NativeLibrary.Run has been called.
NativeLibrary.WaitUntilDoneBooting();
}

NativeLibrary.SurfaceDestroyed();
}
}
@@ -148,7 +148,7 @@ public void setSurfacePosition(Rect rect)
public void initTouchPointer()
{
// Check if we have all the data we need yet
boolean aspectRatioAvailable = NativeLibrary.IsRunning() && !NativeLibrary.IsBooting();
boolean aspectRatioAvailable = NativeLibrary.IsRunningAndStarted();
if (!aspectRatioAvailable || mSurfacePosition == null)
return;

@@ -15,6 +15,7 @@ static jmethodID s_display_alert_msg;
static jmethodID s_do_rumble;
static jmethodID s_update_touch_pointer;
static jmethodID s_on_title_changed;
static jmethodID s_finish_emulation_activity;

static jclass s_game_file_class;
static jfieldID s_game_file_pointer;
@@ -94,6 +95,11 @@ jmethodID GetOnTitleChanged()
return s_on_title_changed;
}

jmethodID GetFinishEmulationActivity()
{
return s_finish_emulation_activity;
}

jclass GetAnalyticsClass()
{
return s_analytics_class;
@@ -216,11 +222,13 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved)
const jclass native_library_class = env->FindClass("org/dolphinemu/dolphinemu/NativeLibrary");
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
s_display_alert_msg = env->GetStaticMethodID(s_native_library_class, "displayAlertMsg",
"(Ljava/lang/String;Ljava/lang/String;ZZ)Z");
"(Ljava/lang/String;Ljava/lang/String;ZZZ)Z");
s_do_rumble = env->GetStaticMethodID(s_native_library_class, "rumble", "(ID)V");
s_update_touch_pointer =
env->GetStaticMethodID(s_native_library_class, "updateTouchPointer", "()V");
s_on_title_changed = env->GetStaticMethodID(s_native_library_class, "onTitleChanged", "()V");
s_finish_emulation_activity =
env->GetStaticMethodID(s_native_library_class, "finishEmulationActivity", "()V");
env->DeleteLocalRef(native_library_class);

const jclass game_file_class = env->FindClass("org/dolphinemu/dolphinemu/model/GameFile");
@@ -15,6 +15,7 @@ jmethodID GetDisplayAlertMsg();
jmethodID GetDoRumble();
jmethodID GetUpdateTouchPointer();
jmethodID GetOnTitleChanged();
jmethodID GetFinishEmulationActivity();

jclass GetAnalyticsClass();
jmethodID GetSendAnalyticsReport();

0 comments on commit 72997c1

Please sign in to comment.