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 | #ifdef __ANDROID__ | 39 | #ifdef __ANDROID__ |
| 40 | //----------------------------------------------------------------------------- | 40 | //----------------------------------------------------------------------------- |
| 41 | #include <jni.h> | 41 | #include <jni.h> |
| 42 | #include <cassert> | 42 | #include <fcntl.h> |
| 43 | #include <memory> | 43 | #include <unistd.h> |
| 44 | #include <functional> | 44 | #include <string> |
| 45 | 45 | ||
| 46 | static JavaVM * jvm_instance = NULL; | 46 | static JavaVM * jvm_instance = NULL; |
| 47 | 47 | ||
| @@ -57,90 +57,405 @@ JavaVM * ChuckAndroid::getJVM() · 9 unmodified lines | |||
| 57 | return jvm_instance; | 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(); | 69 | public: |
| 63 | if( !jvm ) | 70 | JNIEnvWrapper(JavaVM * jvm); |
| 64 | { | 71 | ~JNIEnvWrapper(); |
| 65 | return NULL; | 72 | |
| 66 | } | 73 | bool IsValid() const; |
| 67 | JNIEnv * env = NULL; | 74 | |
| 68 | bool needs_detaching = false; | 75 | jclass FindClass(const char * class_signature); |
| 69 | int reason = jvm->GetEnv((void **)&env, JNI_VERSION_1_6); | 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 | if( reason != JNI_OK && reason != JNI_EDETACHED ) | 117 | if( reason != JNI_OK && reason != JNI_EDETACHED ) |
| 71 | { | 118 | { |
| 72 | return NULL; | 119 | m_env = NULL; |
| 120 | return; | ||
| 73 | } | 121 | } |
| 74 | if( reason == JNI_EDETACHED ) | 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 | 131 | if( m_env->PushLocalFrame(32) != 0 ) | |
| 83 | std::unique_ptr<JavaVM, std::function<void(JavaVM *)>> guard(jvm, | 132 | { |
| 84 | [needs_detaching](JavaVM *jvm) | 133 | if( m_needs_detaching ) |
| 85 | { | 134 | { |
| 86 | if (needs_detaching) | 135 | m_jvm->DetachCurrentThread(); |
| 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; \ | ||
| 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 | // context = android.app.ActivityThread.currentActivityThread().getApplication() | 310 | // context = android.app.ActivityThread.currentActivityThread().getApplication() |
| 102 | jclass thread_class = env->FindClass("android/app/ActivityThread"); | 311 | jclass thread_class = jni.FindClass("android/app/ActivityThread"); |
| 103 | EXCEPTION_CHECK; | 312 | jobject current_thread = jni.CallStaticObjectMethod(thread_class, |
| 104 | jmethodID get_thread = env->GetStaticMethodID( | 313 | "currentActivityThread", "()Landroid/app/ActivityThread;"); |
| 105 | thread_class, "currentActivityThread", "()Landroid/app/ActivityThread;"); | 314 | jobject context = jni.CallObjectMethod(current_thread, thread_class, |
| 106 | EXCEPTION_CHECK; | 315 | "getApplication", "()Landroid/app/Application;"); |
| 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; | ||
| 114 | 316 | ||
| 115 | // cache_dir = context.getCacheDir() | 317 | // cache_dir = context.getCacheDir() |
| 116 | jclass context_class = env->FindClass("android/content/Context"); | 318 | jclass context_class = jni.FindClass("android/content/Context"); |
| 117 | EXCEPTION_CHECK; | 319 | jobject cache_dir = jni.CallObjectMethod(context, context_class, |
| 118 | jmethodID get_cache_dir = env->GetMethodID(context_class, "getCacheDir", "()Ljava/io/File;"); | 320 | "getCacheDir", "()Ljava/io/File;"); |
| 119 | EXCEPTION_CHECK; | 321 | |
| 120 | jobject cache_dir = env->CallObjectMethod(context, get_cache_dir); | 322 | // temp_file = java.io.File.createTempFile("chuck_temp", "", cache_dir) |
| 121 | EXCEPTION_CHECK; | 323 | jclass file_class = jni.FindClass("java/io/File"); |
| 122 | 324 | jobject temp_file = jni.CallStaticObjectMethod(file_class, "createTempFile", | |
| 123 | // temp_file = cache_dir.createTempFile("chuck_temp", "", cache_dir) | 325 | "(Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)Ljava/io/File;", |
| 124 | jclass file_class = env->FindClass("java/io/File"); | 326 | jni.NewStringUTF("chuck_temp"), jni.NewStringUTF(""), cache_dir); |
| 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; | ||
| 132 | 327 | ||
| 133 | // temp_file_path = temp_file.getPath() | 328 | // temp_file_path = temp_file.getPath() |
| 134 | jmethodID get_path = env->GetMethodID(file_class, "getPath", "()Ljava/lang/String;"); | 329 | jstring temp_file_path = (jstring)jni.CallObjectMethod(temp_file, |
| 135 | EXCEPTION_CHECK; | 330 | file_class, "getPath", "()Ljava/lang/String;"); |
| 136 | jstring temp_file_path = (jstring)env->CallObjectMethod(temp_file, get_path); | ||
| 137 | EXCEPTION_CHECK; | ||
| 138 | 331 | ||
| 139 | const char *path_chars = env->GetStringUTFChars(temp_file_path, NULL); | 332 | const char * path_chars = jni.GetStringUTFChars(temp_file_path); |
| 140 | FILE *ret = fopen(path_chars, "wb+D"); | 333 | std::string ret = path_chars; |
| 141 | env->ReleaseStringUTFChars(temp_file_path, path_chars); | 334 | jni.ReleaseStringUTFChars(temp_file_path, path_chars); |
| 142 | 335 | ||
| 143 | return ret; | 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 | #endif // __ANDROID__ | 461 | #endif // __ANDROID__ |