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.
This commit is contained in:
Muhammad Rifqi Priyo Susanto 2022-11-30 22:50:06 +07:00 committed by GitHub
parent 055fc69c11
commit 281f9a9f88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 47 additions and 24 deletions

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

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

@ -29,10 +29,10 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build; import android.os.Build;
import android.os.Environment; import android.os.Environment;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import java.io.File; import java.io.File;
@ -77,9 +77,6 @@ public class UnzipService extends IntentService {
try { try {
setIsRunning(true); setIsRunning(true);
File userDataDirectory = Utils.getUserDataDirectory(this); 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 (InputStream in = this.getAssets().open(zipFile.getName())) {
try (OutputStream out = new FileOutputStream(zipFile)) { try (OutputStream out = new FileOutputStream(zipFile)) {
@ -98,7 +95,9 @@ public class UnzipService extends IntentService {
failureMessage = e.getLocalizedMessage(); failureMessage = e.getLocalizedMessage();
} finally { } finally {
setIsRunning(false); 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); Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_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, PendingIntent intent = PendingIntent.getActivity(this, 0,
notificationIntent, 0); notificationIntent, pendingIntentFlag);
builder.setContentTitle(getString(R.string.notification_title)) builder.setContentTitle(getString(R.string.notification_title))
.setSmallIcon(R.mipmap.ic_launcher) .setSmallIcon(R.mipmap.ic_launcher)
@ -210,7 +213,9 @@ public class UnzipService extends IntentService {
return; return;
publishProgress(notificationBuilder, R.string.migrating, 0); 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" }; String[] dirs = new String[] { "worlds", "games", "mods", "textures", "client" };
for (int i = 0; i < dirs.length; i++) { 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) { private void publishProgress(@Nullable Notification.Builder notificationBuilder, @StringRes int message, int progress) {

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

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