Browse Source

Fix crashes reported by the Google Play Console

Fredia Huya-Kouadio 1 year ago
parent
commit
c6a23a7a7d

+ 11 - 11
main/main.cpp

@@ -909,13 +909,11 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 
 
 	// Benchmark tracking must be done after `OS::get_singleton()->initialize()` as on some
 	// Benchmark tracking must be done after `OS::get_singleton()->initialize()` as on some
 	// platforms, it's used to set up the time utilities.
 	// platforms, it's used to set up the time utilities.
-	OS::get_singleton()->benchmark_begin_measure("Startup", "Total");
-	OS::get_singleton()->benchmark_begin_measure("Startup", "Setup");
+	OS::get_singleton()->benchmark_begin_measure("Startup", "Main::Setup");
 
 
 	engine = memnew(Engine);
 	engine = memnew(Engine);
 
 
 	MAIN_PRINT("Main: Initialize CORE");
 	MAIN_PRINT("Main: Initialize CORE");
-	OS::get_singleton()->benchmark_begin_measure("Startup", "Core");
 
 
 	register_core_types();
 	register_core_types();
 	register_core_driver_types();
 	register_core_driver_types();
@@ -2453,8 +2451,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 	Thread::release_main_thread(); // If setup2() is called from another thread, that one will become main thread, so preventively release this one.
 	Thread::release_main_thread(); // If setup2() is called from another thread, that one will become main thread, so preventively release this one.
 	set_current_thread_safe_for_nodes(false);
 	set_current_thread_safe_for_nodes(false);
 
 
-	OS::get_singleton()->benchmark_end_measure("Startup", "Core");
-
 #if defined(STEAMAPI_ENABLED)
 #if defined(STEAMAPI_ENABLED)
 	if (editor || project_manager) {
 	if (editor || project_manager) {
 		steam_tracker = memnew(SteamTracker);
 		steam_tracker = memnew(SteamTracker);
@@ -2465,7 +2461,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 		return setup2();
 		return setup2();
 	}
 	}
 
 
-	OS::get_singleton()->benchmark_end_measure("Startup", "Setup");
+	OS::get_singleton()->benchmark_end_measure("Startup", "Main::Setup");
 	return OK;
 	return OK;
 
 
 error:
 error:
@@ -2519,7 +2515,7 @@ error:
 	}
 	}
 
 
 	OS::get_singleton()->benchmark_end_measure("Startup", "Core");
 	OS::get_singleton()->benchmark_end_measure("Startup", "Core");
-	OS::get_singleton()->benchmark_end_measure("Startup", "Setup");
+	OS::get_singleton()->benchmark_end_measure("Startup", "Main::Setup");
 
 
 #if defined(STEAMAPI_ENABLED)
 #if defined(STEAMAPI_ENABLED)
 	if (steam_tracker) {
 	if (steam_tracker) {
@@ -2553,6 +2549,8 @@ Error _parse_resource_dummy(void *p_data, VariantParser::Stream *p_stream, Ref<R
 }
 }
 
 
 Error Main::setup2(bool p_show_boot_logo) {
 Error Main::setup2(bool p_show_boot_logo) {
+	OS::get_singleton()->benchmark_begin_measure("Startup", "Main::Setup2");
+
 	Thread::make_main_thread(); // Make whatever thread call this the main thread.
 	Thread::make_main_thread(); // Make whatever thread call this the main thread.
 	set_current_thread_safe_for_nodes(true);
 	set_current_thread_safe_for_nodes(true);
 
 
@@ -3149,7 +3147,7 @@ Error Main::setup2(bool p_show_boot_logo) {
 	print_verbose("EDITOR API HASH: " + uitos(ClassDB::get_api_hash(ClassDB::API_EDITOR)));
 	print_verbose("EDITOR API HASH: " + uitos(ClassDB::get_api_hash(ClassDB::API_EDITOR)));
 	MAIN_PRINT("Main: Done");
 	MAIN_PRINT("Main: Done");
 
 
-	OS::get_singleton()->benchmark_end_measure("Startup", "Setup");
+	OS::get_singleton()->benchmark_end_measure("Startup", "Main::Setup2");
 
 
 	return OK;
 	return OK;
 }
 }
@@ -3230,6 +3228,8 @@ static MainTimerSync main_timer_sync;
 // and should move on to `OS::run`, and EXIT_FAILURE otherwise for
 // and should move on to `OS::run`, and EXIT_FAILURE otherwise for
 // an early exit with that error code.
 // an early exit with that error code.
 int Main::start() {
 int Main::start() {
+	OS::get_singleton()->benchmark_begin_measure("Startup", "Main::Start");
+
 	ERR_FAIL_COND_V(!_start_success, false);
 	ERR_FAIL_COND_V(!_start_success, false);
 
 
 	bool has_icon = false;
 	bool has_icon = false;
@@ -3953,7 +3953,7 @@ int Main::start() {
 		}
 		}
 	}
 	}
 
 
-	OS::get_singleton()->benchmark_end_measure("Startup", "Total");
+	OS::get_singleton()->benchmark_end_measure("Startup", "Main::Start");
 	OS::get_singleton()->benchmark_dump();
 	OS::get_singleton()->benchmark_dump();
 
 
 	return EXIT_SUCCESS;
 	return EXIT_SUCCESS;
@@ -4221,7 +4221,7 @@ void Main::force_redraw() {
  * The order matters as some of those steps are linked with each other.
  * The order matters as some of those steps are linked with each other.
  */
  */
 void Main::cleanup(bool p_force) {
 void Main::cleanup(bool p_force) {
-	OS::get_singleton()->benchmark_begin_measure("Shutdown", "Total");
+	OS::get_singleton()->benchmark_begin_measure("Shutdown", "Main::Cleanup");
 	if (!p_force) {
 	if (!p_force) {
 		ERR_FAIL_COND(!_start_success);
 		ERR_FAIL_COND(!_start_success);
 	}
 	}
@@ -4379,7 +4379,7 @@ void Main::cleanup(bool p_force) {
 
 
 	unregister_core_types();
 	unregister_core_types();
 
 
-	OS::get_singleton()->benchmark_end_measure("Shutdown", "Total");
+	OS::get_singleton()->benchmark_end_measure("Shutdown", "Main::Cleanup");
 	OS::get_singleton()->benchmark_dump();
 	OS::get_singleton()->benchmark_dump();
 
 
 	OS::get_singleton()->finalize_core();
 	OS::get_singleton()->finalize_core();

+ 1 - 1
platform/android/java/app/config.gradle

@@ -7,7 +7,7 @@ ext.versions = [
     targetSdk          : 34,
     targetSdk          : 34,
     buildTools         : '34.0.0',
     buildTools         : '34.0.0',
     kotlinVersion      : '1.9.20',
     kotlinVersion      : '1.9.20',
-    fragmentVersion    : '1.6.2',
+    fragmentVersion    : '1.7.1',
     nexusPublishVersion: '1.3.0',
     nexusPublishVersion: '1.3.0',
     javaVersion        : JavaVersion.VERSION_17,
     javaVersion        : JavaVersion.VERSION_17,
     // Also update 'platform/android/detect.py#get_ndk_version()' when this is updated.
     // Also update 'platform/android/detect.py#get_ndk_version()' when this is updated.

+ 1 - 1
platform/android/java/editor/build.gradle

@@ -9,7 +9,7 @@ dependencies {
     implementation "androidx.fragment:fragment:$versions.fragmentVersion"
     implementation "androidx.fragment:fragment:$versions.fragmentVersion"
     implementation project(":lib")
     implementation project(":lib")
 
 
-    implementation "androidx.window:window:1.2.0"
+    implementation "androidx.window:window:1.3.0"
     implementation "androidx.core:core-splashscreen:$versions.splashscreenVersion"
     implementation "androidx.core:core-splashscreen:$versions.splashscreenVersion"
     implementation "androidx.constraintlayout:constraintlayout:2.1.4"
     implementation "androidx.constraintlayout:constraintlayout:2.1.4"
 }
 }

+ 38 - 36
platform/android/java/lib/src/org/godotengine/godot/Godot.kt

@@ -86,15 +86,14 @@ class Godot(private val context: Context) : SensorEventListener {
 		private val TAG = Godot::class.java.simpleName
 		private val TAG = Godot::class.java.simpleName
 	}
 	}
 
 
-	private val windowManager: WindowManager by lazy {
-		requireActivity().getSystemService(Context.WINDOW_SERVICE) as WindowManager
-	}
+	private val windowManager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+	private val mSensorManager: SensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
+	private val mClipboard: ClipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
+	private val vibratorService: Vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
+
 	private val pluginRegistry: GodotPluginRegistry by lazy {
 	private val pluginRegistry: GodotPluginRegistry by lazy {
 		GodotPluginRegistry.getPluginRegistry()
 		GodotPluginRegistry.getPluginRegistry()
 	}
 	}
-	private val mSensorManager: SensorManager by lazy {
-		requireActivity().getSystemService(Context.SENSOR_SERVICE) as SensorManager
-	}
 	private val mAccelerometer: Sensor? by lazy {
 	private val mAccelerometer: Sensor? by lazy {
 		mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
 		mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
 	}
 	}
@@ -107,9 +106,6 @@ class Godot(private val context: Context) : SensorEventListener {
 	private val mGyroscope: Sensor? by lazy {
 	private val mGyroscope: Sensor? by lazy {
 		mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
 		mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
 	}
 	}
-	private val mClipboard: ClipboardManager by lazy {
-		requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
-	}
 
 
 	private val uiChangeListener = View.OnSystemUiVisibilityChangeListener { visibility: Int ->
 	private val uiChangeListener = View.OnSystemUiVisibilityChangeListener { visibility: Int ->
 		if (visibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0) {
 		if (visibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0) {
@@ -323,13 +319,15 @@ class Godot(private val context: Context) : SensorEventListener {
 			return false
 			return false
 		}
 		}
 
 
-		if (expansionPackPath.isNotEmpty()) {
-			commandLine.add("--main-pack")
-			commandLine.add(expansionPackPath)
-		}
-		val activity = requireActivity()
-		if (!nativeLayerInitializeCompleted) {
-			nativeLayerInitializeCompleted = GodotLib.initialize(
+		beginBenchmarkMeasure("Startup", "Godot::onInitNativeLayer")
+		try {
+			if (expansionPackPath.isNotEmpty()) {
+				commandLine.add("--main-pack")
+				commandLine.add(expansionPackPath)
+			}
+			val activity = requireActivity()
+			if (!nativeLayerInitializeCompleted) {
+				nativeLayerInitializeCompleted = GodotLib.initialize(
 					activity,
 					activity,
 					this,
 					this,
 					activity.assets,
 					activity.assets,
@@ -338,15 +336,17 @@ class Godot(private val context: Context) : SensorEventListener {
 					directoryAccessHandler,
 					directoryAccessHandler,
 					fileAccessHandler,
 					fileAccessHandler,
 					useApkExpansion,
 					useApkExpansion,
-			)
-		}
+				)
+			}
 
 
-		if (nativeLayerInitializeCompleted && !nativeLayerSetupCompleted) {
-			nativeLayerSetupCompleted = GodotLib.setup(commandLine.toTypedArray(), tts)
-			if (!nativeLayerSetupCompleted) {
-				Log.e(TAG, "Unable to setup the Godot engine! Aborting...")
-				alert(R.string.error_engine_setup_message, R.string.text_error_title, this::forceQuit)
+			if (nativeLayerInitializeCompleted && !nativeLayerSetupCompleted) {
+				nativeLayerSetupCompleted = GodotLib.setup(commandLine.toTypedArray(), tts)
+				if (!nativeLayerSetupCompleted) {
+					throw IllegalStateException("Unable to setup the Godot engine! Aborting...")
+				}
 			}
 			}
+		} finally {
+			endBenchmarkMeasure("Startup", "Godot::onInitNativeLayer")
 		}
 		}
 		return isNativeInitialized()
 		return isNativeInitialized()
 	}
 	}
@@ -370,6 +370,7 @@ class Godot(private val context: Context) : SensorEventListener {
 			throw IllegalStateException("onInitNativeLayer() must be invoked successfully prior to initializing the render view")
 			throw IllegalStateException("onInitNativeLayer() must be invoked successfully prior to initializing the render view")
 		}
 		}
 
 
+		beginBenchmarkMeasure("Startup", "Godot::onInitRenderView")
 		try {
 		try {
 			val activity: Activity = host.activity
 			val activity: Activity = host.activity
 			containerLayout = providedContainerLayout
 			containerLayout = providedContainerLayout
@@ -392,8 +393,7 @@ class Godot(private val context: Context) : SensorEventListener {
 			containerLayout?.addView(editText)
 			containerLayout?.addView(editText)
 			renderView = if (usesVulkan()) {
 			renderView = if (usesVulkan()) {
 				if (!meetsVulkanRequirements(activity.packageManager)) {
 				if (!meetsVulkanRequirements(activity.packageManager)) {
-					alert(R.string.error_missing_vulkan_requirements_message, R.string.text_error_title, this::forceQuit)
-					return null
+					throw IllegalStateException(activity.getString(R.string.error_missing_vulkan_requirements_message))
 				}
 				}
 				GodotVulkanRenderView(host, this)
 				GodotVulkanRenderView(host, this)
 			} else {
 			} else {
@@ -482,6 +482,8 @@ class Godot(private val context: Context) : SensorEventListener {
 				containerLayout?.removeAllViews()
 				containerLayout?.removeAllViews()
 				containerLayout = null
 				containerLayout = null
 			}
 			}
+
+			endBenchmarkMeasure("Startup", "Godot::onInitRenderView")
 		}
 		}
 		return containerLayout
 		return containerLayout
 	}
 	}
@@ -609,13 +611,17 @@ class Godot(private val context: Context) : SensorEventListener {
 		// These properties are defined after Godot setup completion, so we retrieve them here.
 		// These properties are defined after Godot setup completion, so we retrieve them here.
 		val longPressEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_long_press_as_right_click"))
 		val longPressEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_long_press_as_right_click"))
 		val panScaleEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))
 		val panScaleEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))
-		val rotaryInputAxis = java.lang.Integer.parseInt(GodotLib.getGlobal("input_devices/pointing/android/rotary_input_scroll_axis"))
+		val rotaryInputAxisValue = GodotLib.getGlobal("input_devices/pointing/android/rotary_input_scroll_axis")
 
 
 		runOnUiThread {
 		runOnUiThread {
 			renderView?.inputHandler?.apply {
 			renderView?.inputHandler?.apply {
 				enableLongPress(longPressEnabled)
 				enableLongPress(longPressEnabled)
 				enablePanningAndScalingGestures(panScaleEnabled)
 				enablePanningAndScalingGestures(panScaleEnabled)
-				setRotaryInputAxis(rotaryInputAxis)
+				try {
+					setRotaryInputAxis(Integer.parseInt(rotaryInputAxisValue))
+				} catch (e: NumberFormatException) {
+					Log.w(TAG, e)
+				}
 			}
 			}
 		}
 		}
 
 
@@ -646,12 +652,7 @@ class Godot(private val context: Context) : SensorEventListener {
 		decorView.setOnSystemUiVisibilityChangeListener(uiChangeListener)
 		decorView.setOnSystemUiVisibilityChangeListener(uiChangeListener)
 	}
 	}
 
 
-	@Keep
-	private fun alert(message: String, title: String) {
-		alert(message, title, null)
-	}
-
-	private fun alert(
+	fun alert(
 		@StringRes messageResId: Int,
 		@StringRes messageResId: Int,
 		@StringRes titleResId: Int,
 		@StringRes titleResId: Int,
 		okCallback: Runnable?
 		okCallback: Runnable?
@@ -660,7 +661,9 @@ class Godot(private val context: Context) : SensorEventListener {
 		alert(res.getString(messageResId), res.getString(titleResId), okCallback)
 		alert(res.getString(messageResId), res.getString(titleResId), okCallback)
 	}
 	}
 
 
-	private fun alert(message: String, title: String, okCallback: Runnable?) {
+	@JvmOverloads
+	@Keep
+	fun alert(message: String, title: String, okCallback: Runnable? = null) {
 		val activity: Activity = getActivity() ?: return
 		val activity: Activity = getActivity() ?: return
 		runOnUiThread {
 		runOnUiThread {
 			val builder = AlertDialog.Builder(activity)
 			val builder = AlertDialog.Builder(activity)
@@ -770,7 +773,7 @@ class Godot(private val context: Context) : SensorEventListener {
 		mClipboard.setPrimaryClip(clip)
 		mClipboard.setPrimaryClip(clip)
 	}
 	}
 
 
-	private fun forceQuit() {
+	fun forceQuit() {
 		forceQuit(0)
 		forceQuit(0)
 	}
 	}
 
 
@@ -881,7 +884,6 @@ class Godot(private val context: Context) : SensorEventListener {
 	@Keep
 	@Keep
 	private fun vibrate(durationMs: Int, amplitude: Int) {
 	private fun vibrate(durationMs: Int, amplitude: Int) {
 		if (durationMs > 0 && requestPermission("VIBRATE")) {
 		if (durationMs > 0 && requestPermission("VIBRATE")) {
-			val vibratorService = getActivity()?.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator? ?: return
 			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
 			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
 				if (amplitude <= -1) {
 				if (amplitude <= -1) {
 					vibratorService.vibrate(
 					vibratorService.vibrate(

+ 7 - 0
platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java

@@ -42,6 +42,7 @@ import android.content.res.Configuration;
 import android.os.Build;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Bundle;
 import android.os.Messenger;
 import android.os.Messenger;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View;
@@ -203,6 +204,12 @@ public class GodotFragment extends Fragment implements IDownloaderClient, GodotH
 			if (godotContainerLayout == null) {
 			if (godotContainerLayout == null) {
 				throw new IllegalStateException("Unable to initialize engine render view");
 				throw new IllegalStateException("Unable to initialize engine render view");
 			}
 			}
+		} catch (IllegalStateException e) {
+			Log.e(TAG, "Engine initialization failed", e);
+			final String errorMessage = TextUtils.isEmpty(e.getMessage())
+					? getString(R.string.error_engine_setup_message)
+					: e.getMessage();
+			godot.alert(errorMessage, getString(R.string.text_error_title), godot::forceQuit);
 		} catch (IllegalArgumentException ignored) {
 		} catch (IllegalArgumentException ignored) {
 			final Activity activity = getActivity();
 			final Activity activity = getActivity();
 			Intent notifierIntent = new Intent(activity, activity.getClass());
 			Intent notifierIntent = new Intent(activity, activity.getClass());

+ 1 - 1
platform/android/java/lib/src/org/godotengine/godot/GodotIO.java

@@ -121,7 +121,7 @@ public class GodotIO {
 
 
 			activity.startActivity(intent);
 			activity.startActivity(intent);
 			return 0;
 			return 0;
-		} catch (ActivityNotFoundException e) {
+		} catch (Exception e) {
 			Log.e(TAG, "Unable to open uri " + uriString, e);
 			Log.e(TAG, "Unable to open uri " + uriString, e);
 			return 1;
 			return 1;
 		}
 		}

+ 8 - 3
platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt

@@ -81,7 +81,8 @@ fun beginBenchmarkMeasure(scope: String, label: String) {
  *
  *
  * * Note: Only enabled on 'editorDev' build variant.
  * * Note: Only enabled on 'editorDev' build variant.
  */
  */
-fun endBenchmarkMeasure(scope: String, label: String) {
+@JvmOverloads
+fun endBenchmarkMeasure(scope: String, label: String, dumpBenchmark: Boolean = false) {
 	if (BuildConfig.FLAVOR != "editor" || BuildConfig.BUILD_TYPE != "dev") {
 	if (BuildConfig.FLAVOR != "editor" || BuildConfig.BUILD_TYPE != "dev") {
 		return
 		return
 	}
 	}
@@ -93,6 +94,10 @@ fun endBenchmarkMeasure(scope: String, label: String) {
 	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
 	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
 		Trace.endAsyncSection("[$scope] $label", 0)
 		Trace.endAsyncSection("[$scope] $label", 0)
 	}
 	}
+
+	if (dumpBenchmark) {
+		dumpBenchmark()
+	}
 }
 }
 
 
 /**
 /**
@@ -102,11 +107,11 @@ fun endBenchmarkMeasure(scope: String, label: String) {
  * * Note: Only enabled on 'editorDev' build variant.
  * * Note: Only enabled on 'editorDev' build variant.
  */
  */
 @JvmOverloads
 @JvmOverloads
-fun dumpBenchmark(fileAccessHandler: FileAccessHandler?, filepath: String? = benchmarkFile) {
+fun dumpBenchmark(fileAccessHandler: FileAccessHandler? = null, filepath: String? = benchmarkFile) {
 	if (BuildConfig.FLAVOR != "editor" || BuildConfig.BUILD_TYPE != "dev") {
 	if (BuildConfig.FLAVOR != "editor" || BuildConfig.BUILD_TYPE != "dev") {
 		return
 		return
 	}
 	}
-	if (!useBenchmark) {
+	if (!useBenchmark || benchmarkTracker.isEmpty()) {
 		return
 		return
 	}
 	}