mirror of https://github.com/encounter/SDL.git
Fixed bug 4297 - Android StrictMode policy. Remove APK expansion support
"In the second half of 2021, new apps will be required to publish with the Android App Bundle on Google Play" (see https://developer.android.com/guide/app-bundle) And "Android App Bundles don't support APK expansion (*.obb) files".
This commit is contained in:
parent
92cb9192e3
commit
965b466ee8
|
@ -1221,76 +1221,6 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// APK expansion files support
|
|
||||||
|
|
||||||
/** com.android.vending.expansion.zipfile.ZipResourceFile object or null. */
|
|
||||||
private static Object expansionFile;
|
|
||||||
|
|
||||||
/** com.android.vending.expansion.zipfile.ZipResourceFile's getInputStream() or null. */
|
|
||||||
private static Method expansionFileMethod;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called by SDL using JNI.
|
|
||||||
* @return an InputStream on success or null if no expansion file was used.
|
|
||||||
* @throws IOException on errors. Message is set for the SDL error message.
|
|
||||||
*/
|
|
||||||
public static InputStream openAPKExpansionInputStream(String fileName) throws IOException {
|
|
||||||
// Get a ZipResourceFile representing a merger of both the main and patch files
|
|
||||||
if (expansionFile == null) {
|
|
||||||
String mainHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION");
|
|
||||||
if (mainHint == null) {
|
|
||||||
return null; // no expansion use if no main version was set
|
|
||||||
}
|
|
||||||
String patchHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION");
|
|
||||||
if (patchHint == null) {
|
|
||||||
return null; // no expansion use if no patch version was set
|
|
||||||
}
|
|
||||||
|
|
||||||
Integer mainVersion;
|
|
||||||
Integer patchVersion;
|
|
||||||
try {
|
|
||||||
mainVersion = Integer.valueOf(mainHint);
|
|
||||||
patchVersion = Integer.valueOf(patchHint);
|
|
||||||
} catch (NumberFormatException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
throw new IOException("No valid file versions set for APK expansion files", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// To avoid direct dependency on Google APK expansion library that is
|
|
||||||
// not a part of Android SDK we access it using reflection
|
|
||||||
expansionFile = Class.forName("com.android.vending.expansion.zipfile.APKExpansionSupport")
|
|
||||||
.getMethod("getAPKExpansionZipFile", Context.class, int.class, int.class)
|
|
||||||
.invoke(null, SDL.getContext(), mainVersion, patchVersion);
|
|
||||||
|
|
||||||
expansionFileMethod = expansionFile.getClass()
|
|
||||||
.getMethod("getInputStream", String.class);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
expansionFile = null;
|
|
||||||
expansionFileMethod = null;
|
|
||||||
throw new IOException("Could not access APK expansion support library", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get an input stream for a known file inside the expansion file ZIPs
|
|
||||||
InputStream fileStream;
|
|
||||||
try {
|
|
||||||
fileStream = (InputStream)expansionFileMethod.invoke(expansionFile, fileName);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
// calling "getInputStream" failed
|
|
||||||
ex.printStackTrace();
|
|
||||||
throw new IOException("Could not open stream from APK expansion file", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileStream == null) {
|
|
||||||
// calling "getInputStream" was successful but null was returned
|
|
||||||
throw new IOException("Could not find path in APK expansion file");
|
|
||||||
}
|
|
||||||
|
|
||||||
return fileStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Messagebox
|
// Messagebox
|
||||||
|
|
||||||
/** Result of current messagebox. Also used for blocking the calling thread. */
|
/** Result of current messagebox. Also used for blocking the calling thread. */
|
||||||
|
|
|
@ -96,15 +96,7 @@ typedef struct SDL_RWops
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
void *fileNameRef;
|
void *asset;
|
||||||
void *inputStreamRef;
|
|
||||||
void *readableByteChannelRef;
|
|
||||||
void *readMethod;
|
|
||||||
void *assetFileDescriptorRef;
|
|
||||||
long position;
|
|
||||||
long size;
|
|
||||||
long offset;
|
|
||||||
int fd;
|
|
||||||
} androidio;
|
} androidio;
|
||||||
#elif defined(__WIN32__)
|
#elif defined(__WIN32__)
|
||||||
struct
|
struct
|
||||||
|
|
|
@ -311,7 +311,6 @@ static jmethodID midIsScreenKeyboardShown;
|
||||||
static jmethodID midIsTablet;
|
static jmethodID midIsTablet;
|
||||||
static jmethodID midManualBackButton;
|
static jmethodID midManualBackButton;
|
||||||
static jmethodID midMinimizeWindow;
|
static jmethodID midMinimizeWindow;
|
||||||
static jmethodID midOpenAPKExpansionInputStream;
|
|
||||||
static jmethodID midRequestPermission;
|
static jmethodID midRequestPermission;
|
||||||
static jmethodID midSendMessage;
|
static jmethodID midSendMessage;
|
||||||
static jmethodID midSetActivityTitle;
|
static jmethodID midSetActivityTitle;
|
||||||
|
@ -590,7 +589,6 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cl
|
||||||
midIsTablet = (*env)->GetStaticMethodID(env, mActivityClass, "isTablet", "()Z");
|
midIsTablet = (*env)->GetStaticMethodID(env, mActivityClass, "isTablet", "()Z");
|
||||||
midManualBackButton = (*env)->GetStaticMethodID(env, mActivityClass, "manualBackButton", "()V");
|
midManualBackButton = (*env)->GetStaticMethodID(env, mActivityClass, "manualBackButton", "()V");
|
||||||
midMinimizeWindow = (*env)->GetStaticMethodID(env, mActivityClass, "minimizeWindow","()V");
|
midMinimizeWindow = (*env)->GetStaticMethodID(env, mActivityClass, "minimizeWindow","()V");
|
||||||
midOpenAPKExpansionInputStream = (*env)->GetStaticMethodID(env, mActivityClass, "openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;");
|
|
||||||
midRequestPermission = (*env)->GetStaticMethodID(env, mActivityClass, "requestPermission", "(Ljava/lang/String;I)V");
|
midRequestPermission = (*env)->GetStaticMethodID(env, mActivityClass, "requestPermission", "(Ljava/lang/String;I)V");
|
||||||
midSendMessage = (*env)->GetStaticMethodID(env, mActivityClass, "sendMessage", "(II)Z");
|
midSendMessage = (*env)->GetStaticMethodID(env, mActivityClass, "sendMessage", "(II)Z");
|
||||||
midSetActivityTitle = (*env)->GetStaticMethodID(env, mActivityClass, "setActivityTitle","(Ljava/lang/String;)Z");
|
midSetActivityTitle = (*env)->GetStaticMethodID(env, mActivityClass, "setActivityTitle","(Ljava/lang/String;)Z");
|
||||||
|
@ -620,7 +618,6 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cl
|
||||||
!midIsTablet ||
|
!midIsTablet ||
|
||||||
!midManualBackButton ||
|
!midManualBackButton ||
|
||||||
!midMinimizeWindow ||
|
!midMinimizeWindow ||
|
||||||
!midOpenAPKExpansionInputStream ||
|
|
||||||
!midRequestPermission ||
|
!midRequestPermission ||
|
||||||
!midSendMessage ||
|
!midSendMessage ||
|
||||||
!midSetActivityTitle ||
|
!midSetActivityTitle ||
|
||||||
|
@ -1867,245 +1864,43 @@ static void Internal_Android_Destroy_AssetManager() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Internal_Android_JNI_FileOpen(SDL_RWops *ctx)
|
|
||||||
{
|
|
||||||
struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
|
|
||||||
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
jmethodID mid;
|
|
||||||
jobject context;
|
|
||||||
jobject assetManager;
|
|
||||||
jobject inputStream;
|
|
||||||
jclass channels;
|
|
||||||
jobject readableByteChannel;
|
|
||||||
jstring fileNameJString;
|
|
||||||
jobject fd;
|
|
||||||
jclass fdCls;
|
|
||||||
jfieldID descriptor;
|
|
||||||
|
|
||||||
JNIEnv *env = Android_JNI_GetEnv();
|
|
||||||
if (!LocalReferenceHolder_Init(&refs, env)) {
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
fileNameJString = (jstring)ctx->hidden.androidio.fileNameRef;
|
|
||||||
ctx->hidden.androidio.position = 0;
|
|
||||||
|
|
||||||
/* context = SDLActivity.getContext(); */
|
|
||||||
context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
|
|
||||||
|
|
||||||
/* assetManager = context.getAssets(); */
|
|
||||||
mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
|
|
||||||
"getAssets", "()Landroid/content/res/AssetManager;");
|
|
||||||
assetManager = (*env)->CallObjectMethod(env, 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 = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, assetManager), "openFd", "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;");
|
|
||||||
inputStream = (*env)->CallObjectMethod(env, assetManager, mid, fileNameJString);
|
|
||||||
if (Android_JNI_ExceptionOccurred(SDL_TRUE)) {
|
|
||||||
goto fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, inputStream), "getStartOffset", "()J");
|
|
||||||
ctx->hidden.androidio.offset = (long)(*env)->CallLongMethod(env, inputStream, mid);
|
|
||||||
if (Android_JNI_ExceptionOccurred(SDL_TRUE)) {
|
|
||||||
goto fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, inputStream), "getDeclaredLength", "()J");
|
|
||||||
ctx->hidden.androidio.size = (long)(*env)->CallLongMethod(env, inputStream, mid);
|
|
||||||
if (Android_JNI_ExceptionOccurred(SDL_TRUE)) {
|
|
||||||
goto fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, inputStream), "getFileDescriptor", "()Ljava/io/FileDescriptor;");
|
|
||||||
fd = (*env)->CallObjectMethod(env, inputStream, mid);
|
|
||||||
fdCls = (*env)->GetObjectClass(env, fd);
|
|
||||||
descriptor = (*env)->GetFieldID(env, fdCls, "descriptor", "I");
|
|
||||||
ctx->hidden.androidio.fd = (*env)->GetIntField(env, fd, descriptor);
|
|
||||||
ctx->hidden.androidio.assetFileDescriptorRef = (*env)->NewGlobalRef(env, inputStream);
|
|
||||||
|
|
||||||
/* Seek to the correct offset in the file. */
|
|
||||||
lseek(ctx->hidden.androidio.fd, (off_t)ctx->hidden.androidio.offset, SEEK_SET);
|
|
||||||
|
|
||||||
if (0) {
|
|
||||||
fallback:
|
|
||||||
/* Disabled log message because of spam on the Nexus 7 */
|
|
||||||
/* __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>); */
|
|
||||||
mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, assetManager),
|
|
||||||
"open", "(Ljava/lang/String;I)Ljava/io/InputStream;");
|
|
||||||
inputStream = (*env)->CallObjectMethod(env, assetManager, mid, fileNameJString, 1 /* ACCESS_RANDOM */);
|
|
||||||
if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
|
|
||||||
/* Try fallback to APK expansion files */
|
|
||||||
inputStream = (*env)->CallStaticObjectMethod(env, mActivityClass, midOpenAPKExpansionInputStream, fileNameJString);
|
|
||||||
|
|
||||||
/* Exception is checked first because it always needs to be cleared.
|
|
||||||
* If no exception occurred then the last SDL error message is kept.
|
|
||||||
*/
|
|
||||||
if (Android_JNI_ExceptionOccurred(SDL_FALSE) || !inputStream) {
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->hidden.androidio.inputStreamRef = (*env)->NewGlobalRef(env, inputStream);
|
|
||||||
|
|
||||||
/* Despite all the visible documentation on [Asset]InputStream claiming
|
|
||||||
* that the .available() method is not guaranteed to return the entire file
|
|
||||||
* size, comments in <sdk>/samples/<ver>/ApiDemos/src/com/example/ ...
|
|
||||||
* android/apis/content/ReadAsset.java imply that Android's
|
|
||||||
* AssetInputStream.available() /will/ always return the total file size
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* size = inputStream.available(); */
|
|
||||||
mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, inputStream),
|
|
||||||
"available", "()I");
|
|
||||||
ctx->hidden.androidio.size = (long)(*env)->CallIntMethod(env, inputStream, mid);
|
|
||||||
if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* readableByteChannel = Channels.newChannel(inputStream); */
|
|
||||||
channels = (*env)->FindClass(env, "java/nio/channels/Channels");
|
|
||||||
mid = (*env)->GetStaticMethodID(env, channels,
|
|
||||||
"newChannel",
|
|
||||||
"(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;");
|
|
||||||
readableByteChannel = (*env)->CallStaticObjectMethod(
|
|
||||||
env, channels, mid, inputStream);
|
|
||||||
if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->hidden.androidio.readableByteChannelRef =
|
|
||||||
(*env)->NewGlobalRef(env, readableByteChannel);
|
|
||||||
|
|
||||||
/* Store .read id for reading purposes */
|
|
||||||
mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, readableByteChannel),
|
|
||||||
"read", "(Ljava/nio/ByteBuffer;)I");
|
|
||||||
ctx->hidden.androidio.readMethod = mid;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (0) {
|
|
||||||
failure:
|
|
||||||
result = -1;
|
|
||||||
|
|
||||||
(*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.fileNameRef);
|
|
||||||
|
|
||||||
if(ctx->hidden.androidio.inputStreamRef != NULL) {
|
|
||||||
(*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.inputStreamRef);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ctx->hidden.androidio.readableByteChannelRef != NULL) {
|
|
||||||
(*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.readableByteChannelRef);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ctx->hidden.androidio.assetFileDescriptorRef != NULL) {
|
|
||||||
(*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.assetFileDescriptorRef);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalReferenceHolder_Cleanup(&refs);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Android_JNI_FileOpen(SDL_RWops *ctx,
|
int Android_JNI_FileOpen(SDL_RWops *ctx,
|
||||||
const char *fileName, const char *mode)
|
const char *fileName, const char *mode)
|
||||||
{
|
{
|
||||||
struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
|
AAsset *asset = NULL;
|
||||||
JNIEnv *env = Android_JNI_GetEnv();
|
ctx->hidden.androidio.asset = NULL;
|
||||||
int retval;
|
|
||||||
jstring fileNameJString;
|
|
||||||
|
|
||||||
if (!LocalReferenceHolder_Init(&refs, env)) {
|
if (asset_manager == NULL) {
|
||||||
LocalReferenceHolder_Cleanup(&refs);
|
Internal_Android_Create_AssetManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asset_manager == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ctx) {
|
asset = AAssetManager_open(asset_manager, fileName, AASSET_MODE_UNKNOWN);
|
||||||
LocalReferenceHolder_Cleanup(&refs);
|
if (asset == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fileNameJString = (*env)->NewStringUTF(env, fileName);
|
|
||||||
ctx->hidden.androidio.fileNameRef = (*env)->NewGlobalRef(env, fileNameJString);
|
|
||||||
ctx->hidden.androidio.inputStreamRef = NULL;
|
|
||||||
ctx->hidden.androidio.readableByteChannelRef = NULL;
|
|
||||||
ctx->hidden.androidio.readMethod = NULL;
|
|
||||||
ctx->hidden.androidio.assetFileDescriptorRef = NULL;
|
|
||||||
|
|
||||||
retval = Internal_Android_JNI_FileOpen(ctx);
|
ctx->hidden.androidio.asset = (void*) asset;
|
||||||
LocalReferenceHolder_Cleanup(&refs);
|
return 0;
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer,
|
size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer,
|
||||||
size_t size, size_t maxnum)
|
size_t size, size_t maxnum)
|
||||||
{
|
{
|
||||||
struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
|
|
||||||
|
|
||||||
if (ctx->hidden.androidio.assetFileDescriptorRef) {
|
|
||||||
size_t bytesMax = size * maxnum;
|
|
||||||
size_t result;
|
size_t result;
|
||||||
if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && ctx->hidden.androidio.position + bytesMax > ctx->hidden.androidio.size) {
|
AAsset *asset = (AAsset*) ctx->hidden.androidio.asset;
|
||||||
bytesMax = ctx->hidden.androidio.size - ctx->hidden.androidio.position;
|
result = AAsset_read(asset, buffer, size * maxnum);
|
||||||
}
|
|
||||||
result = read(ctx->hidden.androidio.fd, buffer, bytesMax );
|
|
||||||
if (result > 0) {
|
if (result > 0) {
|
||||||
ctx->hidden.androidio.position += result;
|
/* Number of chuncks */
|
||||||
LocalReferenceHolder_Cleanup(&refs);
|
return (result / size);
|
||||||
return result / size;
|
|
||||||
}
|
|
||||||
LocalReferenceHolder_Cleanup(&refs);
|
|
||||||
return 0;
|
|
||||||
} else {
|
} else {
|
||||||
jlong bytesRemaining = (jlong) (size * maxnum);
|
/* Error or EOF */
|
||||||
jlong bytesMax = (jlong) (ctx->hidden.androidio.size - ctx->hidden.androidio.position);
|
return result;
|
||||||
int bytesRead = 0;
|
|
||||||
JNIEnv *env;
|
|
||||||
jobject readableByteChannel;
|
|
||||||
jmethodID readMethod;
|
|
||||||
jobject byteBuffer;
|
|
||||||
|
|
||||||
/* Don't read more bytes than those that remain in the file, otherwise we get an exception */
|
|
||||||
if (bytesRemaining > bytesMax) bytesRemaining = bytesMax;
|
|
||||||
|
|
||||||
env = Android_JNI_GetEnv();
|
|
||||||
if (!LocalReferenceHolder_Init(&refs, env)) {
|
|
||||||
LocalReferenceHolder_Cleanup(&refs);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef;
|
|
||||||
readMethod = (jmethodID)ctx->hidden.androidio.readMethod;
|
|
||||||
byteBuffer = (*env)->NewDirectByteBuffer(env, buffer, bytesRemaining);
|
|
||||||
|
|
||||||
while (bytesRemaining > 0) {
|
|
||||||
/* result = readableByteChannel.read(...); */
|
|
||||||
int result = (*env)->CallIntMethod(env, readableByteChannel, readMethod, byteBuffer);
|
|
||||||
|
|
||||||
if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
|
|
||||||
LocalReferenceHolder_Cleanup(&refs);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result < 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytesRemaining -= result;
|
|
||||||
bytesRead += result;
|
|
||||||
ctx->hidden.androidio.position += result;
|
|
||||||
}
|
|
||||||
LocalReferenceHolder_Cleanup(&refs);
|
|
||||||
return bytesRead / size;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2116,148 +1911,27 @@ size_t Android_JNI_FileWrite(SDL_RWops *ctx, const void *buffer,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Internal_Android_JNI_FileClose(SDL_RWops *ctx, SDL_bool release)
|
|
||||||
{
|
|
||||||
struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
|
|
||||||
|
|
||||||
int result = 0;
|
|
||||||
JNIEnv *env = Android_JNI_GetEnv();
|
|
||||||
|
|
||||||
if (!LocalReferenceHolder_Init(&refs, env)) {
|
|
||||||
LocalReferenceHolder_Cleanup(&refs);
|
|
||||||
return SDL_SetError("Failed to allocate enough JVM local references");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx) {
|
|
||||||
if (release) {
|
|
||||||
(*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.fileNameRef);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->hidden.androidio.assetFileDescriptorRef) {
|
|
||||||
jobject inputStream = (jobject)ctx->hidden.androidio.assetFileDescriptorRef;
|
|
||||||
jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, inputStream),
|
|
||||||
"close", "()V");
|
|
||||||
(*env)->CallVoidMethod(env, inputStream, mid);
|
|
||||||
(*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.assetFileDescriptorRef);
|
|
||||||
if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
|
|
||||||
result = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
jobject inputStream = (jobject)ctx->hidden.androidio.inputStreamRef;
|
|
||||||
|
|
||||||
/* inputStream.close(); */
|
|
||||||
jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, inputStream),
|
|
||||||
"close", "()V");
|
|
||||||
(*env)->CallVoidMethod(env, inputStream, mid);
|
|
||||||
(*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.inputStreamRef);
|
|
||||||
(*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.readableByteChannelRef);
|
|
||||||
if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
|
|
||||||
result = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (release) {
|
|
||||||
SDL_FreeRW(ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalReferenceHolder_Cleanup(&refs);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Sint64 Android_JNI_FileSize(SDL_RWops *ctx)
|
Sint64 Android_JNI_FileSize(SDL_RWops *ctx)
|
||||||
{
|
{
|
||||||
return ctx->hidden.androidio.size;
|
off64_t result;
|
||||||
|
AAsset *asset = (AAsset*) ctx->hidden.androidio.asset;
|
||||||
|
result = AAsset_getLength64(asset);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence)
|
Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence)
|
||||||
{
|
{
|
||||||
if (ctx->hidden.androidio.assetFileDescriptorRef) {
|
off64_t result;
|
||||||
off_t ret;
|
AAsset *asset = (AAsset*) ctx->hidden.androidio.asset;
|
||||||
switch (whence) {
|
result = AAsset_seek64(asset, offset, whence);
|
||||||
case RW_SEEK_SET:
|
return result;
|
||||||
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:
|
|
||||||
return SDL_SetError("Unknown value for 'whence'");
|
|
||||||
}
|
|
||||||
|
|
||||||
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 movement;
|
|
||||||
|
|
||||||
switch (whence) {
|
|
||||||
case RW_SEEK_SET:
|
|
||||||
newPosition = offset;
|
|
||||||
break;
|
|
||||||
case RW_SEEK_CUR:
|
|
||||||
newPosition = ctx->hidden.androidio.position + offset;
|
|
||||||
break;
|
|
||||||
case RW_SEEK_END:
|
|
||||||
newPosition = ctx->hidden.androidio.size + offset;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return SDL_SetError("Unknown value for 'whence'");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Validate the new position */
|
|
||||||
if (newPosition < 0) {
|
|
||||||
return SDL_Error(SDL_EFSEEK);
|
|
||||||
}
|
|
||||||
if (newPosition > ctx->hidden.androidio.size) {
|
|
||||||
newPosition = ctx->hidden.androidio.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
movement = newPosition - ctx->hidden.androidio.position;
|
|
||||||
if (movement > 0) {
|
|
||||||
unsigned char buffer[4096];
|
|
||||||
|
|
||||||
/* The easy case where we're seeking forwards */
|
|
||||||
while (movement > 0) {
|
|
||||||
Sint64 amount = sizeof (buffer);
|
|
||||||
size_t result;
|
|
||||||
if (amount > movement) {
|
|
||||||
amount = movement;
|
|
||||||
}
|
|
||||||
result = Android_JNI_FileRead(ctx, buffer, 1, (size_t)amount);
|
|
||||||
if (result <= 0) {
|
|
||||||
/* Failed to read/skip the required amount, so fail */
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
movement -= result;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (movement < 0) {
|
|
||||||
/* We can't seek backwards so we have to reopen the file and seek */
|
|
||||||
/* forwards which obviously isn't very efficient */
|
|
||||||
Internal_Android_JNI_FileClose(ctx, SDL_FALSE);
|
|
||||||
Internal_Android_JNI_FileOpen(ctx);
|
|
||||||
Android_JNI_FileSeek(ctx, newPosition, RW_SEEK_SET);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx->hidden.androidio.position;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Android_JNI_FileClose(SDL_RWops *ctx)
|
int Android_JNI_FileClose(SDL_RWops *ctx)
|
||||||
{
|
{
|
||||||
return Internal_Android_JNI_FileClose(ctx, SDL_TRUE);
|
AAsset *asset = (AAsset*) ctx->hidden.androidio.asset;
|
||||||
|
AAsset_close(asset);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Android_JNI_SetClipboardText(const char *text)
|
int Android_JNI_SetClipboardText(const char *text)
|
||||||
|
|
Loading…
Reference in New Issue