Browse Source

Android: Segmentation fault fix, PendingIntent flag, and other fixes (#12960)

* Android: Segmentation fault fix, PendingIntent flag, and other fixes

- Information about the crosshair is sent after camera initialization.
- Since API 31, PendingIntent requires mutability flag.
- super (class) is called in onRequestPermissionsResult().
- GameActivity suppresses "unused" warning since most of its methods are called from native code.
- Non-null safety is added for nullable function calls.
- Warning/error logging is added for various function calls' return value.

* Move utility functions into Utils.java

- Some nullable functions are changed to be non-null functions.
- Some null checking outside it is removed.
- More annotations are added to functions and parameters.
Muhammad Rifqi Priyo Susanto 1 year ago
parent
commit
281f9a9f88

+ 1 - 0
android/app/src/main/java/net/minetest/minetest/GameActivity.java

@@ -45,6 +45,7 @@ import java.util.Objects;
 // Native code finds these methods by name (see porting_android.cpp).
 // This annotation prevents the minifier/Proguard from mangling them.
 @Keep
+@SuppressWarnings("unused")
 public class GameActivity extends NativeActivity {
 	static {
 		System.loadLibrary("c++_shared");

+ 6 - 2
android/app/src/main/java/net/minetest/minetest/MainActivity.java

@@ -127,8 +127,12 @@ public class MainActivity extends AppCompatActivity {
 	}
 
 	@Override
-	public void onRequestPermissionsResult(int requestCode,
-									@NonNull String[] permissions, @NonNull int[] grantResults) {
+	public void onRequestPermissionsResult(
+		int requestCode,
+		@NonNull String[] permissions,
+		@NonNull int[] grantResults
+	) {
+		super.onRequestPermissionsResult(requestCode, permissions, grantResults);
 		if (requestCode == PERMISSIONS) {
 			for (int grantResult : grantResults) {
 				if (grantResult != PackageManager.PERMISSION_GRANTED) {

+ 15 - 8
android/app/src/main/java/net/minetest/minetest/UnzipService.java

@@ -29,10 +29,10 @@ import android.content.Context;
 import android.content.Intent;
 import android.os.Build;
 import android.os.Environment;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
 import androidx.annotation.StringRes;
 
 import java.io.File;
@@ -77,9 +77,6 @@ public class UnzipService extends IntentService {
 		try {
 			setIsRunning(true);
 			File userDataDirectory = Utils.getUserDataDirectory(this);
-			if (userDataDirectory == null) {
-				throw new IOException("Unable to find user data directory");
-			}
 
 			try (InputStream in = this.getAssets().open(zipFile.getName())) {
 				try (OutputStream out = new FileOutputStream(zipFile)) {
@@ -98,7 +95,9 @@ public class UnzipService extends IntentService {
 			failureMessage = e.getLocalizedMessage();
 		} finally {
 			setIsRunning(false);
-			zipFile.delete();
+			if (!zipFile.delete()) {
+				Log.w("UnzipService", "Minetest installation ZIP cannot be deleted");
+			}
 		}
 	}
 
@@ -131,8 +130,12 @@ public class UnzipService extends IntentService {
 		Intent notificationIntent = new Intent(this, MainActivity.class);
 		notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
 			| Intent.FLAG_ACTIVITY_SINGLE_TOP);
+		int pendingIntentFlag = 0;
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+			pendingIntentFlag = PendingIntent.FLAG_MUTABLE;
+		}
 		PendingIntent intent = PendingIntent.getActivity(this, 0,
-			notificationIntent, 0);
+			notificationIntent, pendingIntentFlag);
 
 		builder.setContentTitle(getString(R.string.notification_title))
 				.setSmallIcon(R.mipmap.ic_launcher)
@@ -210,7 +213,9 @@ public class UnzipService extends IntentService {
 			return;
 
 		publishProgress(notificationBuilder, R.string.migrating, 0);
-		newLocation.mkdir();
+		if (!newLocation.mkdir()) {
+			Log.e("UnzipService", "New installation folder cannot be made");
+		}
 
 		String[] dirs = new String[] { "worlds", "games", "mods", "textures", "client" };
 		for (int i = 0; i < dirs.length; i++) {
@@ -228,7 +233,9 @@ public class UnzipService extends IntentService {
 			}
 		}
 
-		recursivelyDeleteDirectory(oldLocation);
+		if (!recursivelyDeleteDirectory(oldLocation)) {
+			Log.w("UnzipService", "Old installation files cannot be deleted successfully");
+		}
 	}
 
 	private void publishProgress(@Nullable  Notification.Builder notificationBuilder, @StringRes int message, int progress) {

+ 20 - 13
android/app/src/main/java/net/minetest/minetest/Utils.java

@@ -1,36 +1,43 @@
 package net.minetest.minetest;
 
 import android.content.Context;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import java.io.File;
+import java.util.Objects;
 
 public class Utils {
-	public static @NonNull File createDirs(File root, String dir) {
+	@NonNull
+	public static File createDirs(@NonNull File root, @NonNull String dir) {
 		File f = new File(root, dir);
 		if (!f.isDirectory())
-			f.mkdirs();
+			if (!f.mkdirs())
+				Log.e("Utils", "Directory " + dir + " cannot be created");
 
 		return f;
 	}
 
-	public static @Nullable File getUserDataDirectory(Context context) {
-		File extDir = context.getExternalFilesDir(null);
-		if (extDir == null) {
-			return null;
-		}
-
+	@NonNull
+	public static File getUserDataDirectory(@NonNull Context context) {
+		File extDir = Objects.requireNonNull(
+			context.getExternalFilesDir(null),
+			"Cannot get external file directory"
+		);
 		return createDirs(extDir, "Minetest");
 	}
 
-	public static @Nullable File getCacheDirectory(Context context) {
-		return context.getCacheDir();
+	@NonNull
+	public static File getCacheDirectory(@NonNull Context context) {
+		return Objects.requireNonNull(
+			context.getCacheDir(),
+			"Cannot get cache directory"
+		);
 	}
 
-	public static boolean isInstallValid(Context context) {
+	public static boolean isInstallValid(@NonNull Context context) {
 		File userDataDirectory = getUserDataDirectory(context);
-		return userDataDirectory != null && userDataDirectory.isDirectory() &&
+		return userDataDirectory.isDirectory() &&
 			new File(userDataDirectory, "games").isDirectory() &&
 			new File(userDataDirectory, "builtin").isDirectory() &&
 			new File(userDataDirectory, "client").isDirectory() &&

+ 5 - 1
src/client/game.cpp

@@ -1421,7 +1421,6 @@ bool Game::createClient(const GameStartData &start_data)
 	if (g_touchscreengui) {
 		g_touchscreengui->init(texture_src);
 		g_touchscreengui->hide();
-		g_touchscreengui->setUseCrosshair(!isNoCrosshairAllowed());
 	}
 #endif
 	if (!connectToServer(start_data, &could_connect, &connect_aborted))
@@ -1458,6 +1457,11 @@ bool Game::createClient(const GameStartData &start_data)
 	if (client->modsLoaded())
 		client->getScript()->on_camera_ready(camera);
 	client->setCamera(camera);
+#ifdef HAVE_TOUCHSCREENGUI
+	if (g_touchscreengui) {
+		g_touchscreengui->setUseCrosshair(!isNoCrosshairAllowed());
+	}
+#endif
 
 	/* Clouds
 	 */