src/core/android/SDL_android.cpp
changeset 5650 640c67302f8e
parent 5582 1281a3f1f0a6
child 5860 b89f7f3bc9be
     1.1 --- a/src/core/android/SDL_android.cpp	Fri Aug 26 03:38:46 2011 -0400
     1.2 +++ b/src/core/android/SDL_android.cpp	Fri Aug 26 13:11:53 2011 +0100
     1.3 @@ -259,35 +259,92 @@
     1.4      }
     1.5  }
     1.6  
     1.7 +// Test for an exception and call SDL_SetError with its detail if one occurs
     1.8 +static bool Android_JNI_ExceptionOccurred()
     1.9 +{
    1.10 +    jthrowable exception = mEnv->ExceptionOccurred();
    1.11 +    if (exception != NULL) {
    1.12 +        jmethodID mid;
    1.13 +
    1.14 +        // Until this happens most JNI operations have undefined behaviour
    1.15 +        mEnv->ExceptionClear();
    1.16 +
    1.17 +        jclass exceptionClass = mEnv->GetObjectClass(exception);
    1.18 +        jclass classClass = mEnv->FindClass("java/lang/Class");
    1.19 +
    1.20 +        mid = mEnv->GetMethodID(classClass, "getName", "()Ljava/lang/String;");
    1.21 +        jstring exceptionName = (jstring)mEnv->CallObjectMethod(exceptionClass, mid);
    1.22 +        const char* exceptionNameUTF8 = mEnv->GetStringUTFChars(exceptionName, 0);
    1.23 +
    1.24 +        mid = mEnv->GetMethodID(exceptionClass, "getMessage", "()Ljava/lang/String;");
    1.25 +        jstring exceptionMessage = (jstring)mEnv->CallObjectMethod(exceptionClass, mid);
    1.26 +
    1.27 +        if (exceptionMessage != NULL) {
    1.28 +            const char* exceptionMessageUTF8 = mEnv->GetStringUTFChars(
    1.29 +                    exceptionMessage, 0);
    1.30 +            SDL_SetError("%s: %s", exceptionNameUTF8, exceptionMessageUTF8);
    1.31 +            mEnv->ReleaseStringUTFChars(exceptionMessage, exceptionMessageUTF8);
    1.32 +            mEnv->DeleteLocalRef(exceptionMessage);
    1.33 +        } else {
    1.34 +            SDL_SetError("%s", exceptionNameUTF8);
    1.35 +        }
    1.36 +
    1.37 +        mEnv->ReleaseStringUTFChars(exceptionName, exceptionNameUTF8);
    1.38 +        mEnv->DeleteLocalRef(exceptionName);
    1.39 +        mEnv->DeleteLocalRef(classClass);
    1.40 +        mEnv->DeleteLocalRef(exceptionClass);
    1.41 +        mEnv->DeleteLocalRef(exception);
    1.42 +
    1.43 +        return true;
    1.44 +    }
    1.45 +
    1.46 +    return false;
    1.47 +}
    1.48 +
    1.49  static int Android_JNI_FileOpen(SDL_RWops* ctx)
    1.50  {
    1.51 -    jstring fileNameJString = (jstring)ctx->hidden.androidio.fileName;
    1.52 +    int result = 0;
    1.53 +
    1.54 +    jmethodID mid;
    1.55 +    jobject context;
    1.56 +    jobject assetManager;
    1.57 +    jobject inputStream;
    1.58 +    jclass channels;
    1.59 +    jobject readableByteChannel;
    1.60 +    jstring fileNameJString;
    1.61 +
    1.62 +    bool allocatedLocalFrame = false;
    1.63 +
    1.64 +    if (mEnv->PushLocalFrame(16) < 0) {
    1.65 +        SDL_SetError("Failed to allocate enough JVM local references");
    1.66 +        goto failure;
    1.67 +    } else {
    1.68 +        allocatedLocalFrame = true;
    1.69 +    }
    1.70 +
    1.71 +    fileNameJString = (jstring)ctx->hidden.androidio.fileName;
    1.72  
    1.73      // context = SDLActivity.getContext();
    1.74 -    jmethodID mid = mEnv->GetStaticMethodID(mActivityClass,
    1.75 +    mid = mEnv->GetStaticMethodID(mActivityClass,
    1.76              "getContext","()Landroid/content/Context;");
    1.77 -    jobject context = mEnv->CallStaticObjectMethod(mActivityClass, mid);
    1.78 +    context = mEnv->CallStaticObjectMethod(mActivityClass, mid);
    1.79  
    1.80      // assetManager = context.getAssets();
    1.81      mid = mEnv->GetMethodID(mEnv->GetObjectClass(context),
    1.82 -            "getAssets","()Landroid/content/res/AssetManager;");
    1.83 -    jobject assetManager = mEnv->CallObjectMethod(context, mid);
    1.84 +            "getAssets", "()Landroid/content/res/AssetManager;");
    1.85 +    assetManager = mEnv->CallObjectMethod(context, mid);
    1.86  
    1.87      // inputStream = assetManager.open(<filename>);
    1.88 -    mEnv->ExceptionClear();
    1.89      mid = mEnv->GetMethodID(mEnv->GetObjectClass(assetManager),
    1.90              "open", "(Ljava/lang/String;)Ljava/io/InputStream;");
    1.91 -    jobject inputStream = mEnv->CallObjectMethod(assetManager, mid, fileNameJString);
    1.92 -    if (mEnv->ExceptionOccurred()) {
    1.93 -        mEnv->ExceptionDescribe();
    1.94 -        mEnv->ExceptionClear();
    1.95 -        mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef);
    1.96 -        return -1;
    1.97 -    } else {
    1.98 -        ctx->hidden.androidio.inputStream = inputStream;
    1.99 -        ctx->hidden.androidio.inputStreamRef = mEnv->NewGlobalRef(inputStream);
   1.100 +    inputStream = mEnv->CallObjectMethod(assetManager, mid, fileNameJString);
   1.101 +    if (Android_JNI_ExceptionOccurred()) {
   1.102 +        goto failure;
   1.103      }
   1.104  
   1.105 +    ctx->hidden.androidio.inputStream = inputStream;
   1.106 +    ctx->hidden.androidio.inputStreamRef = mEnv->NewGlobalRef(inputStream);
   1.107 +
   1.108      // Store .skip id for seeking purposes
   1.109      mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream),
   1.110              "skip", "(J)J");
   1.111 @@ -300,38 +357,28 @@
   1.112      // AssetInputStream.available() /will/ always return the total file size
   1.113  
   1.114      // size = inputStream.available();
   1.115 -    mEnv->ExceptionClear();
   1.116      mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream),
   1.117              "available", "()I");
   1.118      ctx->hidden.androidio.size = mEnv->CallIntMethod(inputStream, mid);
   1.119 -    if (mEnv->ExceptionOccurred()) {
   1.120 -        mEnv->ExceptionDescribe();
   1.121 -        mEnv->ExceptionClear();
   1.122 -        mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef);
   1.123 -        mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef);
   1.124 -        return -1;
   1.125 +    if (Android_JNI_ExceptionOccurred()) {
   1.126 +        goto failure;
   1.127      }
   1.128  
   1.129      // readableByteChannel = Channels.newChannel(inputStream);
   1.130 -    mEnv->ExceptionClear();
   1.131 -    jclass channels = mEnv->FindClass("java/nio/channels/Channels");
   1.132 +    channels = mEnv->FindClass("java/nio/channels/Channels");
   1.133      mid = mEnv->GetStaticMethodID(channels,
   1.134              "newChannel",
   1.135              "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;");
   1.136 -    jobject readableByteChannel = mEnv->CallStaticObjectMethod(
   1.137 +    readableByteChannel = mEnv->CallStaticObjectMethod(
   1.138              channels, mid, inputStream);
   1.139 -    if (mEnv->ExceptionOccurred()) {
   1.140 -        mEnv->ExceptionDescribe();
   1.141 -        mEnv->ExceptionClear();
   1.142 -        mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef);
   1.143 -        mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef);
   1.144 -        return -1;
   1.145 -    } else {
   1.146 -        ctx->hidden.androidio.readableByteChannel = readableByteChannel;
   1.147 -        ctx->hidden.androidio.readableByteChannelRef =
   1.148 -            mEnv->NewGlobalRef(readableByteChannel);
   1.149 +    if (Android_JNI_ExceptionOccurred()) {
   1.150 +        goto failure;
   1.151      }
   1.152  
   1.153 +    ctx->hidden.androidio.readableByteChannel = readableByteChannel;
   1.154 +    ctx->hidden.androidio.readableByteChannelRef =
   1.155 +        mEnv->NewGlobalRef(readableByteChannel);
   1.156 +
   1.157      // Store .read id for reading purposes
   1.158      mid = mEnv->GetMethodID(mEnv->GetObjectClass(readableByteChannel),
   1.159              "read", "(Ljava/nio/ByteBuffer;)I");
   1.160 @@ -339,7 +386,22 @@
   1.161  
   1.162      ctx->hidden.androidio.position = 0;
   1.163  
   1.164 -    return 0;
   1.165 +    if (false) {
   1.166 +failure:
   1.167 +        result = -1;
   1.168 +
   1.169 +        mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef);
   1.170 +
   1.171 +        if(ctx->hidden.androidio.inputStreamRef != NULL) {
   1.172 +            mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef);
   1.173 +        }
   1.174 +    }
   1.175 +
   1.176 +    if (allocatedLocalFrame) {
   1.177 +        mEnv->PopLocalFrame(NULL);
   1.178 +    }
   1.179 +
   1.180 +    return result;
   1.181  }
   1.182  
   1.183  extern "C" int Android_JNI_FileOpen(SDL_RWops* ctx,
   1.184 @@ -352,6 +414,8 @@
   1.185      jstring fileNameJString = mEnv->NewStringUTF(fileName);
   1.186      ctx->hidden.androidio.fileName = fileNameJString;
   1.187      ctx->hidden.androidio.fileNameRef = mEnv->NewGlobalRef(fileNameJString);
   1.188 +    ctx->hidden.androidio.inputStreamRef = NULL;
   1.189 +    mEnv->DeleteLocalRef(fileNameJString);
   1.190  
   1.191      return Android_JNI_FileOpen(ctx);
   1.192  }
   1.193 @@ -366,14 +430,12 @@
   1.194      jmethodID readMethod = (jmethodID)ctx->hidden.androidio.readMethod;
   1.195      jobject byteBuffer = mEnv->NewDirectByteBuffer(buffer, bytesRemaining);
   1.196  
   1.197 -    mEnv->ExceptionClear();
   1.198      while (bytesRemaining > 0) {
   1.199          // result = readableByteChannel.read(...);
   1.200          int result = mEnv->CallIntMethod(readableByteChannel, readMethod, byteBuffer);
   1.201  
   1.202 -        if (mEnv->ExceptionOccurred()) {
   1.203 -            mEnv->ExceptionDescribe();
   1.204 -            mEnv->ExceptionClear();
   1.205 +        if (Android_JNI_ExceptionOccurred()) {
   1.206 +            mEnv->DeleteLocalRef(byteBuffer);
   1.207              return 0;
   1.208          }
   1.209  
   1.210 @@ -386,6 +448,8 @@
   1.211          ctx->hidden.androidio.position += result;
   1.212      }
   1.213  
   1.214 +    mEnv->DeleteLocalRef(byteBuffer);
   1.215 +
   1.216      return bytesRead / size;
   1.217  }
   1.218  
   1.219 @@ -408,16 +472,13 @@
   1.220          jobject inputStream = (jobject)ctx->hidden.androidio.inputStream;
   1.221  
   1.222          // inputStream.close();
   1.223 -        mEnv->ExceptionClear();
   1.224          jmethodID mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream),
   1.225                  "close", "()V");
   1.226          mEnv->CallVoidMethod(inputStream, mid);
   1.227          mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef);
   1.228          mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef);
   1.229 -        if (mEnv->ExceptionOccurred()) {
   1.230 +        if (Android_JNI_ExceptionOccurred()) {
   1.231              result = -1;
   1.232 -            mEnv->ExceptionDescribe();
   1.233 -            mEnv->ExceptionClear();
   1.234          }
   1.235  
   1.236          if (release) {
   1.237 @@ -460,14 +521,10 @@
   1.238  
   1.239      if (movement > 0) {
   1.240          // The easy case where we're seeking forwards
   1.241 -        mEnv->ExceptionClear();
   1.242          while (movement > 0) {
   1.243              // inputStream.skip(...);
   1.244              movement -= mEnv->CallLongMethod(inputStream, skipMethod, movement);
   1.245 -            if (mEnv->ExceptionOccurred()) {
   1.246 -                mEnv->ExceptionDescribe();
   1.247 -                mEnv->ExceptionClear();
   1.248 -                SDL_SetError("Exception while seeking");
   1.249 +            if (Android_JNI_ExceptionOccurred()) {
   1.250                  return -1;
   1.251              }
   1.252          }