Android: Access APK files using AssetFileDescriptor
This commit is contained in:
parent
b2b90c9f49
commit
678523ea7c
2 changed files with 217 additions and 117 deletions
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue