From 5a93760af9db8ae2340c62492c6499ab5720b778 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Tue, 3 Nov 2020 16:15:04 +1000 Subject: [PATCH] Android: Fix some crashes reported via Play Store --- .../app/src/cpp/android_progress_callback.cpp | 5 +++ .../src/cpp/android_settings_interface.cpp | 38 ++++++++++++++++++- .../duckstation/AndroidHostInterface.java | 4 ++ .../duckstation/EmulationActivity.java | 14 ++++++- .../github/stenzek/duckstation/FileUtil.java | 27 +++++++++---- .../TouchscreenControllerView.java | 6 +++ 6 files changed, 84 insertions(+), 10 deletions(-) diff --git a/android/app/src/cpp/android_progress_callback.cpp b/android/app/src/cpp/android_progress_callback.cpp index de03bd7db..aafff1e0f 100644 --- a/android/app/src/cpp/android_progress_callback.cpp +++ b/android/app/src/cpp/android_progress_callback.cpp @@ -35,6 +35,7 @@ void AndroidProgressCallback::SetCancellable(bool cancellable) void AndroidProgressCallback::SetTitle(const char* title) { + Assert(title); JNIEnv* env = AndroidHelpers::GetJNIEnv(); jstring text_jstr = env->NewStringUTF(title); env->CallVoidMethod(m_java_object, m_set_title_method, text_jstr); @@ -42,6 +43,7 @@ void AndroidProgressCallback::SetTitle(const char* title) void AndroidProgressCallback::SetStatusText(const char* text) { + Assert(text); JNIEnv* env = AndroidHelpers::GetJNIEnv(); jstring text_jstr = env->NewStringUTF(text); env->CallVoidMethod(m_java_object, m_set_status_text_method, text_jstr); @@ -85,6 +87,7 @@ void AndroidProgressCallback::DisplayDebugMessage(const char* message) void AndroidProgressCallback::ModalError(const char* message) { + Assert(message); JNIEnv* env = AndroidHelpers::GetJNIEnv(); jstring message_jstr = env->NewStringUTF(message); env->CallVoidMethod(m_java_object, m_modal_error_method, message_jstr); @@ -92,6 +95,7 @@ void AndroidProgressCallback::ModalError(const char* message) bool AndroidProgressCallback::ModalConfirmation(const char* message) { + Assert(message); JNIEnv* env = AndroidHelpers::GetJNIEnv(); jstring message_jstr = env->NewStringUTF(message); return env->CallBooleanMethod(m_java_object, m_modal_confirmation_method, message_jstr); @@ -99,6 +103,7 @@ bool AndroidProgressCallback::ModalConfirmation(const char* message) void AndroidProgressCallback::ModalInformation(const char* message) { + Assert(message); JNIEnv* env = AndroidHelpers::GetJNIEnv(); jstring message_jstr = env->NewStringUTF(message); env->CallVoidMethod(m_java_object, m_modal_information_method, message_jstr); diff --git a/android/app/src/cpp/android_settings_interface.cpp b/android/app/src/cpp/android_settings_interface.cpp index 7f1f44e6e..016d40f01 100644 --- a/android/app/src/cpp/android_settings_interface.cpp +++ b/android/app/src/cpp/android_settings_interface.cpp @@ -96,6 +96,13 @@ float AndroidSettingsInterface::GetFloatValue(const char* section, const char* k jstring string_object = reinterpret_cast( env->CallObjectMethod(m_java_shared_preferences, m_get_string, env->NewStringUTF(GetSettingKey(section, key)), env->NewStringUTF(TinyString::FromFormat("%f", default_value)))); + + if (env->ExceptionCheck()) + { + env->ExceptionClear(); + return default_value; + } + if (!string_object) return default_value; @@ -111,8 +118,15 @@ float AndroidSettingsInterface::GetFloatValue(const char* section, const char* k bool AndroidSettingsInterface::GetBoolValue(const char* section, const char* key, bool default_value /*= false*/) { JNIEnv* env = AndroidHelpers::GetJNIEnv(); - return static_cast(env->CallBooleanMethod(m_java_shared_preferences, m_get_boolean, + jboolean bool_value = static_cast(env->CallBooleanMethod(m_java_shared_preferences, m_get_boolean, env->NewStringUTF(GetSettingKey(section, key)), default_value)); + if (env->ExceptionCheck()) + { + env->ExceptionClear(); + return default_value; + } + + return bool_value; } std::string AndroidSettingsInterface::GetStringValue(const char* section, const char* key, @@ -122,6 +136,16 @@ std::string AndroidSettingsInterface::GetStringValue(const char* section, const jobject string_object = env->CallObjectMethod(m_java_shared_preferences, m_get_string, env->NewStringUTF(GetSettingKey(section, key)), env->NewStringUTF(default_value)); + + if (env->ExceptionCheck()) + { + env->ExceptionClear(); + return default_value; + } + + if (!string_object) + return default_value; + return AndroidHelpers::JStringToString(env, reinterpret_cast(string_object)); } @@ -155,10 +179,22 @@ std::vector AndroidSettingsInterface::GetStringList(const char* sec JNIEnv* env = AndroidHelpers::GetJNIEnv(); jobject values_set = env->CallObjectMethod(m_java_shared_preferences, m_get_string_set, env->NewStringUTF(GetSettingKey(section, key)), nullptr); + if (env->ExceptionCheck()) + { + env->ExceptionClear(); + return {}; + } + if (!values_set) return {}; jobjectArray values_array = reinterpret_cast(env->CallObjectMethod(values_set, m_set_to_array)); + if (env->ExceptionCheck()) + { + env->ExceptionClear(); + return {}; + } + if (!values_array) return {}; diff --git a/android/app/src/main/java/com/github/stenzek/duckstation/AndroidHostInterface.java b/android/app/src/main/java/com/github/stenzek/duckstation/AndroidHostInterface.java index 0d1faecfc..6b32db583 100644 --- a/android/app/src/main/java/com/github/stenzek/duckstation/AndroidHostInterface.java +++ b/android/app/src/main/java/com/github/stenzek/duckstation/AndroidHostInterface.java @@ -104,4 +104,8 @@ public class AndroidHostInterface { static public AndroidHostInterface getInstance() { return mInstance; } + + static public boolean hasInstanceAndEmulationThreadIsRunning() { + return hasInstance() && getInstance().isEmulationThreadRunning(); + } } diff --git a/android/app/src/main/java/com/github/stenzek/duckstation/EmulationActivity.java b/android/app/src/main/java/com/github/stenzek/duckstation/EmulationActivity.java index 6d89fa329..8a99fa2e7 100644 --- a/android/app/src/main/java/com/github/stenzek/duckstation/EmulationActivity.java +++ b/android/app/src/main/java/com/github/stenzek/duckstation/EmulationActivity.java @@ -116,6 +116,16 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde mApplySettingsOnSurfaceRestored = true; } + /// Ends the activity if it was restored without properly being created. + private boolean checkActivityIsValid() { + if (!AndroidHostInterface.hasInstance() || !AndroidHostInterface.getInstance().isEmulationThreadRunning()) { + finish(); + return false; + } + + return true; + } + @Override public void surfaceCreated(SurfaceHolder holder) { } @@ -227,7 +237,9 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde @Override public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); - updateOrientation(newConfig.orientation); + + if (checkActivityIsValid()) + updateOrientation(newConfig.orientation); } private void updateOrientation() { diff --git a/android/app/src/main/java/com/github/stenzek/duckstation/FileUtil.java b/android/app/src/main/java/com/github/stenzek/duckstation/FileUtil.java index 853939c19..429da3992 100644 --- a/android/app/src/main/java/com/github/stenzek/duckstation/FileUtil.java +++ b/android/app/src/main/java/com/github/stenzek/duckstation/FileUtil.java @@ -42,6 +42,9 @@ public final class FileUtil { @SuppressLint("ObsoleteSdkInt") private static String getVolumePath(final String volumeId, Context context) { + if (volumeId == null) + return null; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return null; try { StorageManager mStorageManager = @@ -113,18 +116,26 @@ public final class FileUtil { @TargetApi(Build.VERSION_CODES.LOLLIPOP) private static String getVolumeIdFromUri(final Uri treeUri) { - final String docId = DocumentsContract.getDocumentId(treeUri); - final String[] split = docId.split(":"); - if (split.length > 0) return split[0]; - else return null; + try { + final String docId = DocumentsContract.getDocumentId(treeUri); + final String[] split = docId.split(":"); + if (split.length > 0) return split[0]; + else return null; + } catch (Exception e) { + return null; + } } @TargetApi(Build.VERSION_CODES.LOLLIPOP) private static String getDocumentPathFromUri(final Uri treeUri) { - final String docId = DocumentsContract.getDocumentId(treeUri); - final String[] split = docId.split(":"); - if ((split.length >= 2) && (split[1] != null)) return split[1]; - else return File.separator; + try { + final String docId = DocumentsContract.getDocumentId(treeUri); + final String[] split = docId.split(":"); + if ((split.length >= 2) && (split[1] != null)) return split[1]; + else return File.separator; + } catch (Exception e) { + return null; + } } } \ No newline at end of file diff --git a/android/app/src/main/java/com/github/stenzek/duckstation/TouchscreenControllerView.java b/android/app/src/main/java/com/github/stenzek/duckstation/TouchscreenControllerView.java index 019860404..ff5f061d1 100644 --- a/android/app/src/main/java/com/github/stenzek/duckstation/TouchscreenControllerView.java +++ b/android/app/src/main/java/com/github/stenzek/duckstation/TouchscreenControllerView.java @@ -143,6 +143,9 @@ public class TouchscreenControllerView extends FrameLayout { private boolean handleTouchEvent(MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_UP: { + if (!AndroidHostInterface.hasInstanceAndEmulationThreadIsRunning()) + return false; + for (TouchscreenControllerButtonView buttonView : mButtonViews) { buttonView.setPressed(false); } @@ -158,6 +161,9 @@ public class TouchscreenControllerView extends FrameLayout { case MotionEvent.ACTION_POINTER_DOWN: case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_MOVE: { + if (!AndroidHostInterface.hasInstanceAndEmulationThreadIsRunning()) + return false; + Rect rect = new Rect(); final int pointerCount = event.getPointerCount(); final int liftedPointerIndex = (event.getActionMasked() == MotionEvent.ACTION_POINTER_UP) ? event.getActionIndex() : -1;