Showing the changes to src/core/util_platforms.cpp (1 of 3 files). Show all
Unified · Split
| @@ -39,9 +39,9 @@ · 38 unmodified lines | ||
| 39 | 39 | #ifdef __ANDROID__ |
| 40 | 40 | //----------------------------------------------------------------------------- |
| 41 | 41 | #include <jni.h> |
| 42 | −#include <cassert> | |
| 43 | −#include <memory> | |
| 44 | −#include <functional> | |
| 42 | +#include <fcntl.h> | |
| 43 | +#include <unistd.h> | |
| 44 | +#include <string> | |
| 45 | 45 | |
| 46 | 46 | static JavaVM * jvm_instance = NULL; |
| 47 | 47 | |
| @@ -57,90 +57,405 @@ JavaVM * ChuckAndroid::getJVM() · 9 unmodified lines | ||
| 57 | 57 | return jvm_instance; |
| 58 | 58 | } |
| 59 | 59 | |
| 60 | −FILE * ChuckAndroid::getTemporaryFile() | |
| 60 | +//----------------------------------------------------------------------------- | |
| 61 | +// name: class JNIEnvWrapper | |
| 62 | +// desc: Wrapper around JNIEnv. Serves three purposes: | |
| 63 | +// - releases JNIEnv and Java objects in RAII fashion | |
| 64 | +// - removes the need to get method IDs | |
| 65 | +// - throws JNIEnvWrapperException exception on Java exceptions | |
| 66 | +//----------------------------------------------------------------------------- | |
| 67 | +class JNIEnvWrapper | |
| 61 | 68 | { |
| 62 | − JavaVM * jvm = getJVM(); | |
| 63 | − if( !jvm ) | |
| 64 | − { | |
| 65 | − return NULL; | |
| 66 | − } | |
| 67 | − JNIEnv * env = NULL; | |
| 68 | − bool needs_detaching = false; | |
| 69 | − int reason = jvm->GetEnv((void **)&env, JNI_VERSION_1_6); | |
| 69 | +public: | |
| 70 | + JNIEnvWrapper(JavaVM * jvm); | |
| 71 | + ~JNIEnvWrapper(); | |
| 72 | + | |
| 73 | + bool IsValid() const; | |
| 74 | + | |
| 75 | + jclass FindClass(const char * class_signature); | |
| 76 | + jobject ConstructNewObject(jclass constructed_class, | |
| 77 | + const char * constructor_signature, jobject param_1); | |
| 78 | + jobject CallObjectMethod(jobject callee, jclass callee_class, | |
| 79 | + const char * method_name, const char * method_signature); | |
| 80 | + jobject CallObjectMethod(jobject callee, jclass callee_class, | |
| 81 | + const char * method_name, const char * method_signature, | |
| 82 | + jobject param_1); | |
| 83 | + jobject CallStaticObjectMethod(jclass callee_class, | |
| 84 | + const char * method_name, const char * method_signature); | |
| 85 | + jobject CallStaticObjectMethod(jclass callee_class, | |
| 86 | + const char * method_name, const char * method_signature, | |
| 87 | + jobject param_1); | |
| 88 | + jobject CallStaticObjectMethod(jclass callee_class, | |
| 89 | + const char * method_name, const char * method_signature, | |
| 90 | + jobject param_1, jobject param_2, jobject param_3); | |
| 91 | + jint CallIntMethod(jobject callee, jclass callee_class, | |
| 92 | + const char * method_name, const char * method_signature, | |
| 93 | + jobject param_1); | |
| 94 | + void CallVoidMethod(jobject callee, jclass callee_class, | |
| 95 | + const char * method_name, const char * method_signature); | |
| 96 | + | |
| 97 | + jstring NewStringUTF(const char * utf8_string); | |
| 98 | + const char * GetStringUTFChars(jstring string); | |
| 99 | + void ReleaseStringUTFChars(jstring string, const char * utf8_chars); | |
| 100 | + jbyteArray NewByteArray(size_t size); | |
| 101 | + jbyte * GetByteArrayElements(jbyteArray java_array); | |
| 102 | + void ReleaseByteArrayElements(jbyteArray java_array, jbyte * native_array); | |
| 103 | + | |
| 104 | +private: | |
| 105 | + void ThrowOnJavaException(); | |
| 106 | + JavaVM * m_jvm = NULL; | |
| 107 | + JNIEnv * m_env = NULL; | |
| 108 | + bool m_needs_detaching = false; | |
| 109 | +}; | |
| 110 | + | |
| 111 | +class JNIEnvWrapperException {}; | |
| 112 | + | |
| 113 | +JNIEnvWrapper::JNIEnvWrapper(JavaVM * jvm) | |
| 114 | + : m_jvm(jvm) | |
| 115 | +{ | |
| 116 | + int reason = jvm->GetEnv((void **)&m_env, JNI_VERSION_1_6); | |
| 70 | 117 | if( reason != JNI_OK && reason != JNI_EDETACHED ) |
| 71 | 118 | { |
| 72 | − return NULL; | |
| 119 | + m_env = NULL; | |
| 120 | + return; | |
| 73 | 121 | } |
| 74 | 122 | if( reason == JNI_EDETACHED ) |
| 75 | 123 | { |
| 76 | − if( jvm->AttachCurrentThread(&env, NULL) != 0 ) | |
| 124 | + if( jvm->AttachCurrentThread(&m_env, NULL) != 0 ) | |
| 77 | 125 | { |
| 78 | − return NULL; | |
| 126 | + m_env = NULL; | |
| 127 | + return; | |
| 79 | 128 | } |
| 80 | − needs_detaching = true; | |
| 129 | + m_needs_detaching = true; | |
| 81 | 130 | } |
| 82 | − | |
| 83 | − std::unique_ptr<JavaVM, std::function<void(JavaVM *)>> guard(jvm, | |
| 84 | − [needs_detaching](JavaVM *jvm) | |
| 131 | + if( m_env->PushLocalFrame(32) != 0 ) | |
| 132 | + { | |
| 133 | + if( m_needs_detaching ) | |
| 85 | 134 | { |
| 86 | − if (needs_detaching) | |
| 87 | − { | |
| 88 | − jvm->DetachCurrentThread(); | |
| 89 | − } | |
| 90 | − }); | |
| 91 | − | |
| 92 | − assert( env != NULL ); | |
| 93 | − #define EXCEPTION_CHECK \ | |
| 94 | − if (env->ExceptionCheck()) \ | |
| 95 | − { \ | |
| 96 | − env->ExceptionDescribe(); \ | |
| 97 | − env->ExceptionClear(); \ | |
| 98 | − return NULL; \ | |
| 135 | + m_jvm->DetachCurrentThread(); | |
| 99 | 136 | } |
| 137 | + m_env = NULL; | |
| 138 | + } | |
| 139 | +} | |
| 140 | + | |
| 141 | +JNIEnvWrapper::~JNIEnvWrapper() | |
| 142 | +{ | |
| 143 | + if( m_env == NULL ) | |
| 144 | + { | |
| 145 | + return; | |
| 146 | + } | |
| 147 | + m_env->PopLocalFrame(NULL); | |
| 148 | + if( m_needs_detaching ) | |
| 149 | + { | |
| 150 | + m_jvm->DetachCurrentThread(); | |
| 151 | + } | |
| 152 | +} | |
| 100 | 153 | |
| 154 | +bool JNIEnvWrapper::IsValid() const | |
| 155 | +{ | |
| 156 | + return m_env != NULL; | |
| 157 | +} | |
| 158 | + | |
| 159 | +void JNIEnvWrapper::ThrowOnJavaException() | |
| 160 | +{ | |
| 161 | + if( m_env->ExceptionCheck() ) | |
| 162 | + { | |
| 163 | + m_env->ExceptionDescribe(); | |
| 164 | + m_env->ExceptionClear(); | |
| 165 | + throw JNIEnvWrapperException(); | |
| 166 | + } | |
| 167 | +} | |
| 168 | + | |
| 169 | +jclass JNIEnvWrapper::FindClass(const char * class_signature) | |
| 170 | +{ | |
| 171 | + jclass ret = m_env->FindClass(class_signature); | |
| 172 | + ThrowOnJavaException(); | |
| 173 | + return ret; | |
| 174 | +} | |
| 175 | + | |
| 176 | +jobject JNIEnvWrapper::ConstructNewObject(jclass constructed_class, | |
| 177 | + const char * constructor_signature, jobject param_1) | |
| 178 | +{ | |
| 179 | + jmethodID method_id = m_env->GetMethodID(constructed_class, | |
| 180 | + "<init>", constructor_signature); | |
| 181 | + ThrowOnJavaException(); | |
| 182 | + jobject ret = m_env->NewObject(constructed_class, method_id, | |
| 183 | + param_1); | |
| 184 | + ThrowOnJavaException(); | |
| 185 | + return ret; | |
| 186 | +} | |
| 187 | + | |
| 188 | +jobject JNIEnvWrapper::CallObjectMethod(jobject callee, jclass callee_class, | |
| 189 | + const char * method_name, const char * method_signature) | |
| 190 | +{ | |
| 191 | + jmethodID method_id = m_env->GetMethodID(callee_class, | |
| 192 | + method_name, method_signature); | |
| 193 | + ThrowOnJavaException(); | |
| 194 | + jobject ret = m_env->CallObjectMethod(callee, method_id); | |
| 195 | + ThrowOnJavaException(); | |
| 196 | + return ret; | |
| 197 | +} | |
| 198 | + | |
| 199 | +jobject JNIEnvWrapper::CallObjectMethod(jobject callee, jclass callee_class, | |
| 200 | + const char * method_name, const char * method_signature, | |
| 201 | + jobject param_1) | |
| 202 | +{ | |
| 203 | + jmethodID method_id = m_env->GetMethodID(callee_class, | |
| 204 | + method_name, method_signature); | |
| 205 | + ThrowOnJavaException(); | |
| 206 | + jobject ret = m_env->CallObjectMethod(callee, method_id, param_1); | |
| 207 | + ThrowOnJavaException(); | |
| 208 | + return ret; | |
| 209 | +} | |
| 210 | + | |
| 211 | +jobject JNIEnvWrapper::CallStaticObjectMethod(jclass callee_class, | |
| 212 | + const char * method_name, const char * method_signature) | |
| 213 | +{ | |
| 214 | + jmethodID method_id = m_env->GetStaticMethodID(callee_class, | |
| 215 | + method_name, method_signature); | |
| 216 | + ThrowOnJavaException(); | |
| 217 | + jobject ret = m_env->CallStaticObjectMethod(callee_class, method_id); | |
| 218 | + ThrowOnJavaException(); | |
| 219 | + | |
| 220 | + return ret; | |
| 221 | +} | |
| 222 | + | |
| 223 | +jobject JNIEnvWrapper::CallStaticObjectMethod(jclass callee_class, | |
| 224 | + const char * method_name, const char * method_signature, jobject param_1) | |
| 225 | +{ | |
| 226 | + jmethodID method_id = m_env->GetStaticMethodID(callee_class, | |
| 227 | + method_name, method_signature); | |
| 228 | + ThrowOnJavaException(); | |
| 229 | + jobject ret = m_env->CallStaticObjectMethod(callee_class, | |
| 230 | + method_id, param_1); | |
| 231 | + ThrowOnJavaException(); | |
| 232 | + | |
| 233 | + return ret; | |
| 234 | +} | |
| 235 | + | |
| 236 | +jobject JNIEnvWrapper::CallStaticObjectMethod(jclass callee_class, | |
| 237 | + const char * method_name, const char * method_signature, jobject param_1, | |
| 238 | + jobject param_2, jobject param_3) | |
| 239 | +{ | |
| 240 | + jmethodID method_id = m_env->GetStaticMethodID(callee_class, | |
| 241 | + method_name, method_signature); | |
| 242 | + ThrowOnJavaException(); | |
| 243 | + jobject ret = m_env->CallStaticObjectMethod(callee_class, method_id, | |
| 244 | + param_1, param_2, param_3); | |
| 245 | + ThrowOnJavaException(); | |
| 246 | + | |
| 247 | + return ret; | |
| 248 | +} | |
| 249 | + | |
| 250 | +jint JNIEnvWrapper::CallIntMethod(jobject callee, jclass callee_class, | |
| 251 | + const char * method_name, const char * method_signature, jobject param_1) | |
| 252 | +{ | |
| 253 | + jmethodID method_id = m_env->GetMethodID(callee_class, | |
| 254 | + method_name, method_signature); | |
| 255 | + ThrowOnJavaException(); | |
| 256 | + jint ret = m_env->CallIntMethod(callee, method_id, param_1); | |
| 257 | + ThrowOnJavaException(); | |
| 258 | + return ret; | |
| 259 | +} | |
| 260 | + | |
| 261 | +void JNIEnvWrapper::CallVoidMethod(jobject callee, jclass callee_class, | |
| 262 | + const char * method_name, const char * method_signature) | |
| 263 | +{ | |
| 264 | + jmethodID method_id = m_env->GetMethodID(callee_class, | |
| 265 | + method_name, method_signature); | |
| 266 | + ThrowOnJavaException(); | |
| 267 | + m_env->CallVoidMethod(callee, method_id); | |
| 268 | + ThrowOnJavaException(); | |
| 269 | +} | |
| 270 | + | |
| 271 | +jstring JNIEnvWrapper::NewStringUTF(const char * utf8_string) | |
| 272 | +{ | |
| 273 | + jstring ret = m_env->NewStringUTF(utf8_string); | |
| 274 | + ThrowOnJavaException(); | |
| 275 | + | |
| 276 | + return ret; | |
| 277 | +} | |
| 278 | + | |
| 279 | +const char * JNIEnvWrapper::GetStringUTFChars(jstring string) | |
| 280 | +{ | |
| 281 | + return m_env->GetStringUTFChars(string, NULL); | |
| 282 | +} | |
| 283 | + | |
| 284 | +void JNIEnvWrapper::ReleaseStringUTFChars(jstring string, const char * utf8_chars) | |
| 285 | +{ | |
| 286 | + m_env->ReleaseStringUTFChars(string, utf8_chars); | |
| 287 | +} | |
| 288 | + | |
| 289 | +jbyteArray JNIEnvWrapper::NewByteArray(size_t size) | |
| 290 | +{ | |
| 291 | + jbyteArray ret = m_env->NewByteArray(size); | |
| 292 | + ThrowOnJavaException(); | |
| 293 | + | |
| 294 | + return ret; | |
| 295 | +} | |
| 296 | + | |
| 297 | +jbyte * JNIEnvWrapper::GetByteArrayElements(jbyteArray java_array) | |
| 298 | +{ | |
| 299 | + return m_env->GetByteArrayElements(java_array, NULL); | |
| 300 | +} | |
| 301 | + | |
| 302 | +void JNIEnvWrapper::ReleaseByteArrayElements(jbyteArray java_array, | |
| 303 | + jbyte * native_array) | |
| 304 | +{ | |
| 305 | + m_env->ReleaseByteArrayElements(java_array, native_array, 0); | |
| 306 | +} | |
| 307 | + | |
| 308 | +static std::string GetTemporaryFilePath(JNIEnvWrapper& jni) | |
| 309 | +{ | |
| 101 | 310 | // context = android.app.ActivityThread.currentActivityThread().getApplication() |
| 102 | − jclass thread_class = env->FindClass("android/app/ActivityThread"); | |
| 103 | − EXCEPTION_CHECK; | |
| 104 | − jmethodID get_thread = env->GetStaticMethodID( | |
| 105 | − thread_class, "currentActivityThread", "()Landroid/app/ActivityThread;"); | |
| 106 | − EXCEPTION_CHECK; | |
| 107 | − jobject current_thread = env->CallStaticObjectMethod(thread_class, get_thread); | |
| 108 | − EXCEPTION_CHECK; | |
| 109 | − jmethodID get_application = env->GetMethodID( | |
| 110 | − thread_class, "getApplication", "()Landroid/app/Application;"); | |
| 111 | − EXCEPTION_CHECK; | |
| 112 | − jobject context = env->CallObjectMethod(current_thread, get_application); | |
| 113 | − EXCEPTION_CHECK; | |
| 311 | + jclass thread_class = jni.FindClass("android/app/ActivityThread"); | |
| 312 | + jobject current_thread = jni.CallStaticObjectMethod(thread_class, | |
| 313 | + "currentActivityThread", "()Landroid/app/ActivityThread;"); | |
| 314 | + jobject context = jni.CallObjectMethod(current_thread, thread_class, | |
| 315 | + "getApplication", "()Landroid/app/Application;"); | |
| 114 | 316 | |
| 115 | 317 | // cache_dir = context.getCacheDir() |
| 116 | − jclass context_class = env->FindClass("android/content/Context"); | |
| 117 | − EXCEPTION_CHECK; | |
| 118 | − jmethodID get_cache_dir = env->GetMethodID(context_class, "getCacheDir", "()Ljava/io/File;"); | |
| 119 | − EXCEPTION_CHECK; | |
| 120 | − jobject cache_dir = env->CallObjectMethod(context, get_cache_dir); | |
| 121 | − EXCEPTION_CHECK; | |
| 122 | − | |
| 123 | − // temp_file = cache_dir.createTempFile("chuck_temp", "", cache_dir) | |
| 124 | − jclass file_class = env->FindClass("java/io/File"); | |
| 125 | − EXCEPTION_CHECK; | |
| 126 | − jmethodID create_temp_file = env->GetStaticMethodID(file_class, "createTempFile", | |
| 127 | − "(Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)Ljava/io/File;"); | |
| 128 | − EXCEPTION_CHECK; | |
| 129 | − jobject temp_file = env->CallStaticObjectMethod(file_class, create_temp_file, | |
| 130 | − env->NewStringUTF("chuck_temp"), env->NewStringUTF(""), cache_dir); | |
| 131 | − EXCEPTION_CHECK; | |
| 318 | + jclass context_class = jni.FindClass("android/content/Context"); | |
| 319 | + jobject cache_dir = jni.CallObjectMethod(context, context_class, | |
| 320 | + "getCacheDir", "()Ljava/io/File;"); | |
| 321 | + | |
| 322 | + // temp_file = java.io.File.createTempFile("chuck_temp", "", cache_dir) | |
| 323 | + jclass file_class = jni.FindClass("java/io/File"); | |
| 324 | + jobject temp_file = jni.CallStaticObjectMethod(file_class, "createTempFile", | |
| 325 | + "(Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)Ljava/io/File;", | |
| 326 | + jni.NewStringUTF("chuck_temp"), jni.NewStringUTF(""), cache_dir); | |
| 132 | 327 | |
| 133 | 328 | // temp_file_path = temp_file.getPath() |
| 134 | − jmethodID get_path = env->GetMethodID(file_class, "getPath", "()Ljava/lang/String;"); | |
| 135 | − EXCEPTION_CHECK; | |
| 136 | − jstring temp_file_path = (jstring)env->CallObjectMethod(temp_file, get_path); | |
| 137 | − EXCEPTION_CHECK; | |
| 329 | + jstring temp_file_path = (jstring)jni.CallObjectMethod(temp_file, | |
| 330 | + file_class, "getPath", "()Ljava/lang/String;"); | |
| 138 | 331 | |
| 139 | − const char *path_chars = env->GetStringUTFChars(temp_file_path, NULL); | |
| 140 | − FILE *ret = fopen(path_chars, "wb+D"); | |
| 141 | − env->ReleaseStringUTFChars(temp_file_path, path_chars); | |
| 332 | + const char * path_chars = jni.GetStringUTFChars(temp_file_path); | |
| 333 | + std::string ret = path_chars; | |
| 334 | + jni.ReleaseStringUTFChars(temp_file_path, path_chars); | |
| 142 | 335 | |
| 143 | 336 | return ret; |
| 144 | 337 | } |
| 145 | 338 | |
| 339 | +FILE * ChuckAndroid::getTemporaryFile() noexcept | |
| 340 | +{ | |
| 341 | + JavaVM * jvm = getJVM(); | |
| 342 | + if( !jvm ) | |
| 343 | + { | |
| 344 | + return NULL; | |
| 345 | + } | |
| 346 | + JNIEnvWrapper jni(jvm); | |
| 347 | + if( !jni.IsValid() ) | |
| 348 | + { | |
| 349 | + return NULL; | |
| 350 | + } | |
| 351 | + | |
| 352 | + try | |
| 353 | + { | |
| 354 | + const std::string temp_path = GetTemporaryFilePath(jni); | |
| 355 | + return fopen(temp_path.c_str(), "wb+"); | |
| 356 | + } | |
| 357 | + catch (JNIEnvWrapperException) | |
| 358 | + { | |
| 359 | + return NULL; | |
| 360 | + } | |
| 361 | +} | |
| 362 | + | |
| 363 | +bool ChuckAndroid::copyJARURLFileToTemporary(const char * jar_url, int * fd) | |
| 364 | + noexcept | |
| 365 | +{ | |
| 366 | + JavaVM * jvm = getJVM(); | |
| 367 | + if( !jvm ) | |
| 368 | + { | |
| 369 | + return false; | |
| 370 | + } | |
| 371 | + JNIEnvWrapper jni(jvm); | |
| 372 | + if( !jni.IsValid() ) | |
| 373 | + { | |
| 374 | + return false; | |
| 375 | + } | |
| 376 | + | |
| 377 | + int ret = 0; | |
| 378 | + try | |
| 379 | + { | |
| 380 | + const std::string temp_path = GetTemporaryFilePath(jni); | |
| 381 | + ret = open(temp_path.c_str(), O_RDWR | O_CREAT, 0600); | |
| 382 | + if( ret == -1 ) | |
| 383 | + { | |
| 384 | + return false; | |
| 385 | + } | |
| 386 | + // do not pollute temp directory | |
| 387 | + if( unlink(temp_path.c_str()) == -1 ) | |
| 388 | + { | |
| 389 | + return false; | |
| 390 | + } | |
| 391 | + | |
| 392 | + // url = new java.net.URL(jar_url) | |
| 393 | + jclass url_class = jni.FindClass("java/net/URL"); | |
| 394 | + jobject url = jni.ConstructNewObject(url_class, | |
| 395 | + "(Ljava/lang/String;)V", jni.NewStringUTF(jar_url)); | |
| 396 | + | |
| 397 | + // connection = url.openConnection() | |
| 398 | + jobject connection = jni.CallObjectMethod(url, url_class, | |
| 399 | + "openConnection", "()Ljava/net/URLConnection;"); | |
| 400 | + | |
| 401 | + // jar_file = (java.net.JarURLConnection)connection.getJarFile() | |
| 402 | + jclass jar_url_connection_class = jni.FindClass( | |
| 403 | + "java/net/JarURLConnection"); | |
| 404 | + jobject jar_file = jni.CallObjectMethod(connection, | |
| 405 | + jar_url_connection_class, "getJarFile", | |
| 406 | + "()Ljava/util/jar/JarFile;"); | |
| 407 | + | |
| 408 | + // jar_entry = connection.getJarEntry() | |
| 409 | + jobject jar_entry = jni.CallObjectMethod(connection, | |
| 410 | + jar_url_connection_class, "getJarEntry", | |
| 411 | + "()Ljava/util/jar/JarEntry;"); | |
| 412 | + | |
| 413 | + // input_stream = jar_file.getInputStream(jar_entry) | |
| 414 | + jclass jar_file_class = jni.FindClass("java/util/jar/JarFile"); | |
| 415 | + jobject input_stream = jni.CallObjectMethod(jar_file, jar_file_class, | |
| 416 | + "getInputStream", "(Ljava/util/zip/ZipEntry;)Ljava/io/InputStream;", | |
| 417 | + jar_entry); | |
| 418 | + | |
| 419 | + // buffer = new byte[8 * 1024] | |
| 420 | + jbyteArray java_buffer = jni.NewByteArray(8 * 1024); | |
| 421 | + // s = input_stream.read(buffer) | |
| 422 | + jclass input_stream_class = jni.FindClass("java/io/InputStream"); | |
| 423 | + jint s = 0; | |
| 424 | + do | |
| 425 | + { | |
| 426 | + s = jni.CallIntMethod(input_stream, input_stream_class, "read", | |
| 427 | + "([B)I", java_buffer); | |
| 428 | + jbyte * native_buffer = jni.GetByteArrayElements(java_buffer); | |
| 429 | + size_t written = 0; | |
| 430 | + while( written < s ) | |
| 431 | + { | |
| 432 | + ssize_t written_now = write(ret, native_buffer + written, | |
| 433 | + s - written); | |
| 434 | + if( written_now < 0 ) | |
| 435 | + { | |
| 436 | + close(ret); | |
| 437 | + return false; | |
| 438 | + } | |
| 439 | + written += written_now; | |
| 440 | + } | |
| 441 | + jni.ReleaseByteArrayElements(java_buffer, native_buffer); | |
| 442 | + } while( s > 0 ); | |
| 443 | + | |
| 444 | + // jar_file.close() | |
| 445 | + jni.CallVoidMethod(jar_file, jar_file_class, "close", "()V"); | |
| 446 | + } | |
| 447 | + catch (JNIEnvWrapperException) | |
| 448 | + { | |
| 449 | + if( ret != -1 ) | |
| 450 | + { | |
| 451 | + close(ret); | |
| 452 | + } | |
| 453 | + return false; | |
| 454 | + } | |
| 455 | + | |
| 456 | + lseek(ret, 0, SEEK_SET); | |
| 457 | + *fd = ret; | |
| 458 | + return true; | |
| 459 | +} | |
| 460 | + | |
| 146 | 461 | #endif // __ANDROID__ |