Android: Access APK files using AssetFileDescriptor

This commit is contained in:
Gabriel Jacobo 2013-01-08 09:30:53 -03:00
parent b2b90c9f49
commit 678523ea7c
2 changed files with 217 additions and 117 deletions

View file

@ -94,8 +94,11 @@ typedef struct SDL_RWops
void *inputStreamRef; void *inputStreamRef;
void *readableByteChannelRef; void *readableByteChannelRef;
void *readMethod; void *readMethod;
void *assetFileDescriptorRef;
long position; long position;
int size; long size;
long offset;
int fd;
} androidio; } androidio;
#elif defined(__WIN32__) #elif defined(__WIN32__)
struct struct

View file

@ -37,6 +37,8 @@ extern "C" {
#include <android/log.h> #include <android/log.h>
#include <pthread.h> #include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#define LOG_TAG "SDL_android" #define LOG_TAG "SDL_android"
//#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) //#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
//#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) //#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
@ -559,6 +561,9 @@ static int Android_JNI_FileOpen(SDL_RWops* ctx)
jclass channels; jclass channels;
jobject readableByteChannel; jobject readableByteChannel;
jstring fileNameJString; jstring fileNameJString;
jobject fd;
jclass fdCls;
jfieldID descriptor;
JNIEnv *mEnv = Android_JNI_GetEnv(); JNIEnv *mEnv = Android_JNI_GetEnv();
if (!refs.init(mEnv)) { if (!refs.init(mEnv)) {
@ -566,21 +571,58 @@ static int Android_JNI_FileOpen(SDL_RWops* ctx)
} }
fileNameJString = (jstring)ctx->hidden.androidio.fileNameRef; fileNameJString = (jstring)ctx->hidden.androidio.fileNameRef;
ctx->hidden.androidio.position = 0;
// context = SDLActivity.getContext(); // context = SDLActivity.getContext();
mid = mEnv->GetStaticMethodID(mActivityClass, mid = mEnv->GetStaticMethodID(mActivityClass,
"getContext","()Landroid/content/Context;"); "getContext","()Landroid/content/Context;");
context = mEnv->CallStaticObjectMethod(mActivityClass, mid); context = mEnv->CallStaticObjectMethod(mActivityClass, mid);
// assetManager = context.getAssets(); // assetManager = context.getAssets();
mid = mEnv->GetMethodID(mEnv->GetObjectClass(context), mid = mEnv->GetMethodID(mEnv->GetObjectClass(context),
"getAssets", "()Landroid/content/res/AssetManager;"); "getAssets", "()Landroid/content/res/AssetManager;");
assetManager = mEnv->CallObjectMethod(context, mid); assetManager = mEnv->CallObjectMethod(context, mid);
/* First let's try opening the file to obtain an AssetFileDescriptor.
* This method reads the files directly from the APKs using standard *nix calls
*/
mid = mEnv->GetMethodID(mEnv->GetObjectClass(assetManager), "openFd", "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;");
inputStream = mEnv->CallObjectMethod(assetManager, mid, fileNameJString);
if (Android_JNI_ExceptionOccurred()) {
goto fallback;
}
ctx->hidden.androidio.assetFileDescriptorRef = mEnv->NewGlobalRef(inputStream);
mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "getStartOffset", "()J");
ctx->hidden.androidio.offset = mEnv->CallLongMethod(inputStream, mid);
if (Android_JNI_ExceptionOccurred()) {
goto fallback;
}
mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "getDeclaredLength", "()J");
ctx->hidden.androidio.size = mEnv->CallLongMethod(inputStream, mid);
if (Android_JNI_ExceptionOccurred()) {
goto fallback;
}
mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "getFileDescriptor", "()Ljava/io/FileDescriptor;");
fd = mEnv->CallObjectMethod(inputStream, mid);
fdCls = mEnv->GetObjectClass(fd);
descriptor = mEnv->GetFieldID(fdCls, "descriptor", "I");
ctx->hidden.androidio.fd = mEnv->GetIntField(fd, descriptor);
if (false) {
fallback:
__android_log_print(ANDROID_LOG_DEBUG, "SDL", "Falling back to legacy InputStream method for opening file");
/* Try the old method using InputStream */
ctx->hidden.androidio.assetFileDescriptorRef = NULL;
// inputStream = assetManager.open(<filename>); // inputStream = assetManager.open(<filename>);
mid = mEnv->GetMethodID(mEnv->GetObjectClass(assetManager), mid = mEnv->GetMethodID(mEnv->GetObjectClass(assetManager),
"open", "(Ljava/lang/String;)Ljava/io/InputStream;"); "open", "(Ljava/lang/String;I)Ljava/io/InputStream;");
inputStream = mEnv->CallObjectMethod(assetManager, mid, fileNameJString); inputStream = mEnv->CallObjectMethod(assetManager, mid, fileNameJString, 1 /*ACCESS_RANDOM*/);
if (Android_JNI_ExceptionOccurred()) { if (Android_JNI_ExceptionOccurred()) {
goto failure; goto failure;
} }
@ -596,7 +638,7 @@ static int Android_JNI_FileOpen(SDL_RWops* ctx)
// size = inputStream.available(); // size = inputStream.available();
mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream),
"available", "()I"); "available", "()I");
ctx->hidden.androidio.size = mEnv->CallIntMethod(inputStream, mid); ctx->hidden.androidio.size = (long)mEnv->CallIntMethod(inputStream, mid);
if (Android_JNI_ExceptionOccurred()) { if (Android_JNI_ExceptionOccurred()) {
goto failure; goto failure;
} }
@ -619,8 +661,7 @@ static int Android_JNI_FileOpen(SDL_RWops* ctx)
mid = mEnv->GetMethodID(mEnv->GetObjectClass(readableByteChannel), mid = mEnv->GetMethodID(mEnv->GetObjectClass(readableByteChannel),
"read", "(Ljava/nio/ByteBuffer;)I"); "read", "(Ljava/nio/ByteBuffer;)I");
ctx->hidden.androidio.readMethod = mid; ctx->hidden.androidio.readMethod = mid;
}
ctx->hidden.androidio.position = 0;
if (false) { if (false) {
failure: failure:
@ -636,6 +677,10 @@ failure:
mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef); mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef);
} }
if(ctx->hidden.androidio.assetFileDescriptorRef != NULL) {
mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.assetFileDescriptorRef);
}
} }
return result; return result;
@ -660,6 +705,7 @@ extern "C" int Android_JNI_FileOpen(SDL_RWops* ctx,
ctx->hidden.androidio.inputStreamRef = NULL; ctx->hidden.androidio.inputStreamRef = NULL;
ctx->hidden.androidio.readableByteChannelRef = NULL; ctx->hidden.androidio.readableByteChannelRef = NULL;
ctx->hidden.androidio.readMethod = NULL; ctx->hidden.androidio.readMethod = NULL;
ctx->hidden.androidio.assetFileDescriptorRef = NULL;
return Android_JNI_FileOpen(ctx); return Android_JNI_FileOpen(ctx);
} }
@ -668,6 +714,19 @@ extern "C" size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer,
size_t size, size_t maxnum) size_t size, size_t maxnum)
{ {
LocalReferenceHolder refs(__FUNCTION__); LocalReferenceHolder refs(__FUNCTION__);
if (ctx->hidden.androidio.assetFileDescriptorRef) {
size_t bytesMax = size * maxnum;
if (ctx->hidden.androidio.size != -1 /*UNKNOWN_LENGTH*/ && ctx->hidden.androidio.position + bytesMax > ctx->hidden.androidio.size) {
bytesMax = ctx->hidden.androidio.size - ctx->hidden.androidio.position;
}
size_t result = read(ctx->hidden.androidio.fd, buffer, bytesMax );
if (result > 0) {
ctx->hidden.androidio.position += result;
return result / size;
}
return 0;
} else {
jlong bytesRemaining = (jlong) (size * maxnum); jlong bytesRemaining = (jlong) (size * maxnum);
jlong bytesMax = (jlong) (ctx->hidden.androidio.size - ctx->hidden.androidio.position); jlong bytesMax = (jlong) (ctx->hidden.androidio.size - ctx->hidden.androidio.position);
int bytesRead = 0; int bytesRead = 0;
@ -700,9 +759,9 @@ extern "C" size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer,
bytesRead += result; bytesRead += result;
ctx->hidden.androidio.position += result; ctx->hidden.androidio.position += result;
} }
return bytesRead / size; return bytesRead / size;
} }
}
extern "C" size_t Android_JNI_FileWrite(SDL_RWops* ctx, const void* buffer, extern "C" size_t Android_JNI_FileWrite(SDL_RWops* ctx, const void* buffer,
size_t size, size_t num) size_t size, size_t num)
@ -727,6 +786,17 @@ static int Android_JNI_FileClose(SDL_RWops* ctx, bool release)
mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef); mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef);
} }
if (ctx->hidden.androidio.assetFileDescriptorRef) {
jobject inputStream = (jobject)ctx->hidden.androidio.assetFileDescriptorRef;
jmethodID mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream),
"close", "()V");
mEnv->CallVoidMethod(inputStream, mid);
mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.assetFileDescriptorRef);
if (Android_JNI_ExceptionOccurred()) {
result = -1;
}
}
else {
jobject inputStream = (jobject)ctx->hidden.androidio.inputStreamRef; jobject inputStream = (jobject)ctx->hidden.androidio.inputStreamRef;
// inputStream.close(); // inputStream.close();
@ -738,6 +808,7 @@ static int Android_JNI_FileClose(SDL_RWops* ctx, bool release)
if (Android_JNI_ExceptionOccurred()) { if (Android_JNI_ExceptionOccurred()) {
result = -1; result = -1;
} }
}
if (release) { if (release) {
SDL_FreeRW(ctx); SDL_FreeRW(ctx);
@ -755,6 +826,30 @@ extern "C" Sint64 Android_JNI_FileSize(SDL_RWops* ctx)
extern "C" Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence) extern "C" Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence)
{ {
if (ctx->hidden.androidio.assetFileDescriptorRef) {
switch (whence) {
case RW_SEEK_SET:
if (ctx->hidden.androidio.size != -1 /*UNKNOWN_LENGTH*/ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
offset += ctx->hidden.androidio.offset;
break;
case RW_SEEK_CUR:
offset += ctx->hidden.androidio.position;
if (ctx->hidden.androidio.size != -1 /*UNKNOWN_LENGTH*/ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
offset += ctx->hidden.androidio.offset;
break;
case RW_SEEK_END:
offset = ctx->hidden.androidio.offset + ctx->hidden.androidio.size + offset;
break;
default:
SDL_SetError("Unknown value for 'whence'");
return -1;
}
whence = SEEK_SET;
off_t ret = lseek(ctx->hidden.androidio.fd, (off_t)offset, SEEK_SET);
if (ret == -1) return -1;
ctx->hidden.androidio.position = ret - ctx->hidden.androidio.offset;
} else {
Sint64 newPosition; Sint64 newPosition;
switch (whence) { switch (whence) {
@ -807,8 +902,10 @@ extern "C" Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence
Android_JNI_FileOpen(ctx); Android_JNI_FileOpen(ctx);
Android_JNI_FileSeek(ctx, newPosition, RW_SEEK_SET); Android_JNI_FileSeek(ctx, newPosition, RW_SEEK_SET);
} }
}
return ctx->hidden.androidio.position; return ctx->hidden.androidio.position;
} }
extern "C" int Android_JNI_FileClose(SDL_RWops* ctx) extern "C" int Android_JNI_FileClose(SDL_RWops* ctx)