From 9235ac4efdbd175d0c217ca0fd1bc824ccb968a0 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Tue, 28 Apr 2026 10:45:34 -0400
Subject: [PATCH] android: Parsing the APK's central dir is separate from the
AAssetManager.
Only parse when necessary, which it isn't when opening a file, so this is now
separated from creating the cached AAssetManager object.
---
src/core/android/SDL_android.c | 91 +++++++++++++++++-----------------
1 file changed, 46 insertions(+), 45 deletions(-)
diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c
index 8919b87b582a7..3b642d2a8d9fd 100644
--- a/src/core/android/SDL_android.c
+++ b/src/core/android/SDL_android.c
@@ -2236,37 +2236,58 @@ static bool CreateAPKNodes(const char *path)
{
SDL_Log("ANDROID: Parsing APK file '%s' ...", path);
- SDL_PathInfo apkinfo;
- SDL_assert(path[0] == '/'); // So SDL_GetPathInfo goes through the `stat` path and doesn't try to dig into the APK.
- if (!SDL_GetPathInfo(path, &apkinfo)) {
- SDL_zero(apkinfo); // we just want the file times here, so oh well.
+ SDL_IOStream *io = SDL_IOFromFile(path, "rb");
+ if (!io) {
+ SDL_Log("ANDROID: Can't open APK '%s' for reading (%s). Filesystem enumeration will fail.", path, SDL_GetError());
+ } else {
+ ProcessZip(io, APKRootNode);
+ SDL_CloseIO(io);
}
+ return true;
+}
- if (!APKRootNode) {
+static bool PrepareAPK(void)
+{
+ // the assetmanager isn't useful for enumerating directories, so parse the APK directly for that info upfront.
+ bool retval = (APKRootNode != NULL);
+ if (!retval) {
+ // allocate this upfront, so if there's a failure, we'll not try again and just have an empty file tree.
APKRootNode = (APKNode *) SDL_calloc(1, sizeof (*APKRootNode));
if (!APKRootNode) {
- SDL_Log("ANDROID: Can't open APK (out of memory). Filesystem enumeration will fail.");
- return false;
+ return false; // oh well.
}
APKRootNode->info.type = SDL_PATHTYPE_DIRECTORY;
- APKRootNode->info.create_time = apkinfo.create_time;
- APKRootNode->info.modify_time = apkinfo.modify_time;
- APKRootNode->info.access_time = apkinfo.access_time;
- }
- SDL_IOStream *io = SDL_IOFromFile(path, "rb");
- if (!io) {
- SDL_Log("ANDROID: Can't open APK '%s' for reading (%s). Filesystem enumeration will fail.", path, SDL_GetError());
- } else {
- ProcessZip(io, APKRootNode);
- SDL_CloseIO(io);
+ struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(SDL_FUNCTION);
+ JNIEnv *env = Android_JNI_GetEnv();
+ if (LocalReferenceHolder_Init(&refs, env)) {
+ jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
+ jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context), "getPackageResourcePath", "()Ljava/lang/String;");
+ jstring jstr = (jstring)(*env)->CallObjectMethod(env, context, mid);
+ jthrowable jexception = (*env)->ExceptionOccurred(env);
+ if (jexception != NULL) {
+ (*env)->ExceptionClear(env); // oh well
+ } else {
+ const char *apkpath = (*env)->GetStringUTFChars(env, jstr, NULL);
+ SDL_PathInfo apkinfo;
+ SDL_assert(apkpath[0] == '/'); // So SDL_GetPathInfo goes through the `stat` path and doesn't try to dig into the APK.
+ if (SDL_GetPathInfo(apkpath, &apkinfo)) { // we just want the file times here, so oh well if it fails.
+ APKRootNode->info.create_time = apkinfo.create_time;
+ APKRootNode->info.modify_time = apkinfo.modify_time;
+ APKRootNode->info.access_time = apkinfo.access_time;
+ }
+ CreateAPKNodes(apkpath);
+ (*env)->ReleaseStringUTFChars(env, jstr, apkpath);
+ retval = true;
+ }
+ }
+ LocalReferenceHolder_Cleanup(&refs);
}
- return true; // even on failure, leave an empty root node so we have zero files and don't try to load the .zip again.
+ return retval; // even on failure, leave an empty root node so we have zero files and don't try to load the .zip again.
}
static void Internal_Android_Create_AssetManager(void)
{
-
struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(SDL_FUNCTION);
JNIEnv *env = Android_JNI_GetEnv();
jmethodID mid;
@@ -2300,21 +2321,6 @@ static void Internal_Android_Create_AssetManager(void)
Android_JNI_ExceptionOccurred(true);
}
- // the assetmanager isn't useful for enumerating directories, so parse the APK directly for that info upfront.
- jthrowable jexception = 0;
- jstring jstr = 0;
-
- mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context), "getPackageResourcePath", "()Ljava/lang/String;");
- jstr = (jstring)(*env)->CallObjectMethod(env, context, mid);
- jexception = (*env)->ExceptionOccurred(env);
- if (jexception != NULL) {
- (*env)->ExceptionClear(env); // oh well
- } else {
- const char *apkpath = (*env)->GetStringUTFChars(env, jstr, NULL);
- CreateAPKNodes(apkpath);
- (*env)->ReleaseStringUTFChars(env, jstr, apkpath);
- }
-
LocalReferenceHolder_Cleanup(&refs);
}
@@ -2409,17 +2415,13 @@ bool Android_JNI_EnumerateAssetDirectory(const char *path, SDL_EnumerateDirector
{
SDL_assert(path != NULL);
- if (!asset_manager) {
- Internal_Android_Create_AssetManager();
- if (!asset_manager) {
- return SDL_SetError("Couldn't create asset manager");
- }
+ if (!PrepareAPK()) {
+ return false;
}
SDL_EnumerationResult result = SDL_ENUM_CONTINUE;
const char *asset_path = GetAssetPath(path);
- // check our tree we built from the APK first.
const APKNode *apknode = FindAPKNode(asset_path);
if (!apknode) {
return SDL_SetError("No such directory");
@@ -2436,11 +2438,10 @@ bool Android_JNI_EnumerateAssetDirectory(const char *path, SDL_EnumerateDirector
bool Android_JNI_GetAssetPathInfo(const char *path, SDL_PathInfo *info)
{
- if (!asset_manager) {
- Internal_Android_Create_AssetManager();
- if (!asset_manager) {
- return SDL_SetError("Couldn't create asset manager");
- }
+ SDL_assert(path != NULL);
+
+ if (!PrepareAPK()) {
+ return false;
}
path = GetAssetPath(path);