| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 1 | /* | 
| Andy McFadden | d1f4cf7 | 2009-10-21 15:36:48 -0700 | [diff] [blame] | 2 |  * Copyright (C) 2006 The Android Open Source Project | 
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 3 |  * | 
| Andy McFadden | d1f4cf7 | 2009-10-21 15:36:48 -0700 | [diff] [blame] | 4 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 5 |  * you may not use this file except in compliance with the License. | 
 | 6 |  * You may obtain a copy of the License at | 
 | 7 |  * | 
 | 8 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 | 9 |  * | 
 | 10 |  * Unless required by applicable law or agreed to in writing, software | 
 | 11 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 | 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 13 |  * See the License for the specific language governing permissions and | 
 | 14 |  * limitations under the License. | 
 | 15 |  */ | 
 | 16 |  | 
 | 17 | /* | 
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 18 |  * JNI helper functions. | 
 | 19 |  */ | 
 | 20 | #define LOG_TAG "JNIHelp" | 
 | 21 | #include "JNIHelp.h" | 
 | 22 | #include "utils/Log.h" | 
 | 23 |  | 
| Andy McFadden | 37b8005 | 2011-02-08 12:07:32 -0800 | [diff] [blame] | 24 | #include <stdlib.h> | 
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 25 | #include <string.h> | 
 | 26 | #include <assert.h> | 
 | 27 |  | 
 | 28 | /* | 
 | 29 |  * Register native JNI-callable methods. | 
 | 30 |  * | 
 | 31 |  * "className" looks like "java/lang/String". | 
 | 32 |  */ | 
 | 33 | int jniRegisterNativeMethods(JNIEnv* env, const char* className, | 
 | 34 |     const JNINativeMethod* gMethods, int numMethods) | 
 | 35 | { | 
 | 36 |     jclass clazz; | 
 | 37 |  | 
 | 38 |     LOGV("Registering %s natives\n", className); | 
 | 39 |     clazz = (*env)->FindClass(env, className); | 
 | 40 |     if (clazz == NULL) { | 
| Andy McFadden | 5e261cd | 2011-01-07 13:34:47 -0800 | [diff] [blame] | 41 |         LOGE("Native registration unable to find class '%s', aborting\n", | 
 | 42 |             className); | 
 | 43 |         abort(); | 
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 44 |     } | 
| Jeff Brown | 431da18 | 2010-06-01 21:07:08 -0700 | [diff] [blame] | 45 |  | 
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 46 |     if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) { | 
| Andy McFadden | 5e261cd | 2011-01-07 13:34:47 -0800 | [diff] [blame] | 47 |         LOGE("RegisterNatives failed for '%s', aborting\n", className); | 
 | 48 |         abort(); | 
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 49 |     } | 
| Jeff Brown | 431da18 | 2010-06-01 21:07:08 -0700 | [diff] [blame] | 50 |  | 
 | 51 |     (*env)->DeleteLocalRef(env, clazz); | 
| Andy McFadden | 5e261cd | 2011-01-07 13:34:47 -0800 | [diff] [blame] | 52 |     return 0; | 
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 53 | } | 
 | 54 |  | 
 | 55 | /* | 
| Andy McFadden | d1f4cf7 | 2009-10-21 15:36:48 -0700 | [diff] [blame] | 56 |  * Get a human-readable summary of an exception object.  The buffer will | 
 | 57 |  * be populated with the "binary" class name and, if present, the | 
 | 58 |  * exception message. | 
 | 59 |  */ | 
| Jeff Brown | 431da18 | 2010-06-01 21:07:08 -0700 | [diff] [blame] | 60 | static void getExceptionSummary(JNIEnv* env, jthrowable exception, char* buf, size_t bufLen) | 
| Andy McFadden | d1f4cf7 | 2009-10-21 15:36:48 -0700 | [diff] [blame] | 61 | { | 
| Jeff Brown | 431da18 | 2010-06-01 21:07:08 -0700 | [diff] [blame] | 62 |     int success = 0; | 
| Andy McFadden | d1f4cf7 | 2009-10-21 15:36:48 -0700 | [diff] [blame] | 63 |  | 
| Jeff Brown | 431da18 | 2010-06-01 21:07:08 -0700 | [diff] [blame] | 64 |     /* get the name of the exception's class */ | 
 | 65 |     jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail | 
 | 66 |     jclass classClazz = (*env)->GetObjectClass(env, exceptionClazz); // java.lang.Class, can't fail | 
 | 67 |     jmethodID classGetNameMethod = (*env)->GetMethodID( | 
 | 68 |             env, classClazz, "getName", "()Ljava/lang/String;"); | 
 | 69 |     jstring classNameStr = (*env)->CallObjectMethod(env, exceptionClazz, classGetNameMethod); | 
 | 70 |     if (classNameStr != NULL) { | 
 | 71 |         /* get printable string */ | 
 | 72 |         const char* classNameChars = (*env)->GetStringUTFChars(env, classNameStr, NULL); | 
 | 73 |         if (classNameChars != NULL) { | 
 | 74 |             /* if the exception has a message string, get that */ | 
 | 75 |             jmethodID throwableGetMessageMethod = (*env)->GetMethodID( | 
 | 76 |                     env, exceptionClazz, "getMessage", "()Ljava/lang/String;"); | 
 | 77 |             jstring messageStr = (*env)->CallObjectMethod( | 
 | 78 |                     env, exception, throwableGetMessageMethod); | 
| Andy McFadden | d1f4cf7 | 2009-10-21 15:36:48 -0700 | [diff] [blame] | 79 |  | 
| Jeff Brown | 431da18 | 2010-06-01 21:07:08 -0700 | [diff] [blame] | 80 |             if (messageStr != NULL) { | 
 | 81 |                 const char* messageChars = (*env)->GetStringUTFChars(env, messageStr, NULL); | 
 | 82 |                 if (messageChars != NULL) { | 
 | 83 |                     snprintf(buf, bufLen, "%s: %s", classNameChars, messageChars); | 
 | 84 |                     (*env)->ReleaseStringUTFChars(env, messageStr, messageChars); | 
 | 85 |                 } else { | 
 | 86 |                     (*env)->ExceptionClear(env); // clear OOM | 
 | 87 |                     snprintf(buf, bufLen, "%s: <error getting message>", classNameChars); | 
 | 88 |                 } | 
 | 89 |                 (*env)->DeleteLocalRef(env, messageStr); | 
 | 90 |             } else { | 
 | 91 |                 strncpy(buf, classNameChars, bufLen); | 
 | 92 |                 buf[bufLen - 1] = '\0'; | 
 | 93 |             } | 
 | 94 |  | 
 | 95 |             (*env)->ReleaseStringUTFChars(env, classNameStr, classNameChars); | 
 | 96 |             success = 1; | 
 | 97 |         } | 
 | 98 |         (*env)->DeleteLocalRef(env, classNameStr); | 
 | 99 |     } | 
 | 100 |     (*env)->DeleteLocalRef(env, classClazz); | 
 | 101 |     (*env)->DeleteLocalRef(env, exceptionClazz); | 
 | 102 |  | 
 | 103 |     if (! success) { | 
 | 104 |         (*env)->ExceptionClear(env); | 
 | 105 |         snprintf(buf, bufLen, "%s", "<error getting class name>"); | 
 | 106 |     } | 
 | 107 | } | 
 | 108 |  | 
 | 109 | /* | 
 | 110 |  * Formats an exception as a string with its stack trace. | 
 | 111 |  */ | 
 | 112 | static void printStackTrace(JNIEnv* env, jthrowable exception, char* buf, size_t bufLen) | 
 | 113 | { | 
 | 114 |     int success = 0; | 
 | 115 |  | 
 | 116 |     jclass stringWriterClazz = (*env)->FindClass(env, "java/io/StringWriter"); | 
 | 117 |     if (stringWriterClazz != NULL) { | 
 | 118 |         jmethodID stringWriterCtor = (*env)->GetMethodID(env, stringWriterClazz, | 
 | 119 |                 "<init>", "()V"); | 
 | 120 |         jmethodID stringWriterToStringMethod = (*env)->GetMethodID(env, stringWriterClazz, | 
 | 121 |                 "toString", "()Ljava/lang/String;"); | 
 | 122 |  | 
 | 123 |         jclass printWriterClazz = (*env)->FindClass(env, "java/io/PrintWriter"); | 
 | 124 |         if (printWriterClazz != NULL) { | 
 | 125 |             jmethodID printWriterCtor = (*env)->GetMethodID(env, printWriterClazz, | 
 | 126 |                     "<init>", "(Ljava/io/Writer;)V"); | 
 | 127 |  | 
 | 128 |             jobject stringWriterObj = (*env)->NewObject(env, stringWriterClazz, stringWriterCtor); | 
 | 129 |             if (stringWriterObj != NULL) { | 
 | 130 |                 jobject printWriterObj = (*env)->NewObject(env, printWriterClazz, printWriterCtor, | 
 | 131 |                         stringWriterObj); | 
 | 132 |                 if (printWriterObj != NULL) { | 
 | 133 |                     jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail | 
 | 134 |                     jmethodID printStackTraceMethod = (*env)->GetMethodID( | 
 | 135 |                             env, exceptionClazz, "printStackTrace", "(Ljava/io/PrintWriter;)V"); | 
 | 136 |  | 
 | 137 |                     (*env)->CallVoidMethod( | 
 | 138 |                             env, exception, printStackTraceMethod, printWriterObj); | 
 | 139 |                     if (! (*env)->ExceptionCheck(env)) { | 
 | 140 |                         jstring messageStr = (*env)->CallObjectMethod( | 
 | 141 |                                 env, stringWriterObj, stringWriterToStringMethod); | 
 | 142 |                         if (messageStr != NULL) { | 
 | 143 |                             jsize messageStrLength = (*env)->GetStringLength(env, messageStr); | 
 | 144 |                             if (messageStrLength >= (jsize) bufLen) { | 
 | 145 |                                 messageStrLength = bufLen - 1; | 
 | 146 |                             } | 
 | 147 |                             (*env)->GetStringUTFRegion(env, messageStr, 0, messageStrLength, buf); | 
 | 148 |                             (*env)->DeleteLocalRef(env, messageStr); | 
 | 149 |                             buf[messageStrLength] = '\0'; | 
 | 150 |                             success = 1; | 
 | 151 |                         } | 
 | 152 |                     } | 
 | 153 |                     (*env)->DeleteLocalRef(env, exceptionClazz); | 
 | 154 |                     (*env)->DeleteLocalRef(env, printWriterObj); | 
 | 155 |                 } | 
 | 156 |                 (*env)->DeleteLocalRef(env, stringWriterObj); | 
 | 157 |             } | 
 | 158 |             (*env)->DeleteLocalRef(env, printWriterClazz); | 
 | 159 |         } | 
 | 160 |         (*env)->DeleteLocalRef(env, stringWriterClazz); | 
| Andy McFadden | d1f4cf7 | 2009-10-21 15:36:48 -0700 | [diff] [blame] | 161 |     } | 
 | 162 |  | 
| Jeff Brown | 431da18 | 2010-06-01 21:07:08 -0700 | [diff] [blame] | 163 |     if (! success) { | 
 | 164 |         (*env)->ExceptionClear(env); | 
 | 165 |         getExceptionSummary(env, exception, buf, bufLen); | 
| Andy McFadden | d1f4cf7 | 2009-10-21 15:36:48 -0700 | [diff] [blame] | 166 |     } | 
| Andy McFadden | d1f4cf7 | 2009-10-21 15:36:48 -0700 | [diff] [blame] | 167 | } | 
 | 168 |  | 
 | 169 | /* | 
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 170 |  * Throw an exception with the specified class and an optional message. | 
| Andy McFadden | d1f4cf7 | 2009-10-21 15:36:48 -0700 | [diff] [blame] | 171 |  * | 
 | 172 |  * If an exception is currently pending, we log a warning message and | 
 | 173 |  * clear it. | 
 | 174 |  * | 
 | 175 |  * Returns 0 if the specified exception was successfully thrown.  (Some | 
 | 176 |  * sort of exception will always be pending when this returns.) | 
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 177 |  */ | 
 | 178 | int jniThrowException(JNIEnv* env, const char* className, const char* msg) | 
 | 179 | { | 
 | 180 |     jclass exceptionClass; | 
 | 181 |  | 
| Andy McFadden | d1f4cf7 | 2009-10-21 15:36:48 -0700 | [diff] [blame] | 182 |     if ((*env)->ExceptionCheck(env)) { | 
 | 183 |         /* TODO: consider creating the new exception with this as "cause" */ | 
 | 184 |         char buf[256]; | 
 | 185 |  | 
| Jeff Brown | 431da18 | 2010-06-01 21:07:08 -0700 | [diff] [blame] | 186 |         jthrowable exception = (*env)->ExceptionOccurred(env); | 
| Andy McFadden | d1f4cf7 | 2009-10-21 15:36:48 -0700 | [diff] [blame] | 187 |         (*env)->ExceptionClear(env); | 
| Jeff Brown | 431da18 | 2010-06-01 21:07:08 -0700 | [diff] [blame] | 188 |  | 
 | 189 |         if (exception != NULL) { | 
 | 190 |             getExceptionSummary(env, exception, buf, sizeof(buf)); | 
 | 191 |             LOGW("Discarding pending exception (%s) to throw %s\n", buf, className); | 
 | 192 |             (*env)->DeleteLocalRef(env, exception); | 
 | 193 |         } | 
| Andy McFadden | d1f4cf7 | 2009-10-21 15:36:48 -0700 | [diff] [blame] | 194 |     } | 
 | 195 |  | 
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 196 |     exceptionClass = (*env)->FindClass(env, className); | 
 | 197 |     if (exceptionClass == NULL) { | 
 | 198 |         LOGE("Unable to find exception class %s\n", className); | 
| Andy McFadden | d1f4cf7 | 2009-10-21 15:36:48 -0700 | [diff] [blame] | 199 |         /* ClassNotFoundException now pending */ | 
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 200 |         return -1; | 
 | 201 |     } | 
 | 202 |  | 
| Jeff Brown | 431da18 | 2010-06-01 21:07:08 -0700 | [diff] [blame] | 203 |     int result = 0; | 
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 204 |     if ((*env)->ThrowNew(env, exceptionClass, msg) != JNI_OK) { | 
 | 205 |         LOGE("Failed throwing '%s' '%s'\n", className, msg); | 
| Andy McFadden | d1f4cf7 | 2009-10-21 15:36:48 -0700 | [diff] [blame] | 206 |         /* an exception, most likely OOM, will now be pending */ | 
| Jeff Brown | 431da18 | 2010-06-01 21:07:08 -0700 | [diff] [blame] | 207 |         result = -1; | 
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 208 |     } | 
| Jeff Brown | 431da18 | 2010-06-01 21:07:08 -0700 | [diff] [blame] | 209 |  | 
 | 210 |     (*env)->DeleteLocalRef(env, exceptionClass); | 
 | 211 |     return result; | 
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 212 | } | 
 | 213 |  | 
 | 214 | /* | 
| Elliott Hughes | 7f4a5ec | 2010-01-28 13:43:39 -0800 | [diff] [blame] | 215 |  * Throw a java.lang.NullPointerException, with an optional message. | 
 | 216 |  */ | 
 | 217 | int jniThrowNullPointerException(JNIEnv* env, const char* msg) | 
 | 218 | { | 
 | 219 |     return jniThrowException(env, "java/lang/NullPointerException", msg); | 
 | 220 | } | 
 | 221 |  | 
 | 222 | /* | 
| Dan Bornstein | 4a888b0 | 2009-12-07 15:46:23 -0800 | [diff] [blame] | 223 |  * Throw a java.lang.RuntimeException, with an optional message. | 
 | 224 |  */ | 
 | 225 | int jniThrowRuntimeException(JNIEnv* env, const char* msg) | 
 | 226 | { | 
 | 227 |     return jniThrowException(env, "java/lang/RuntimeException", msg); | 
 | 228 | } | 
 | 229 |  | 
 | 230 | /* | 
 | 231 |  * Throw a java.io.IOException, generating the message from errno. | 
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 232 |  */ | 
 | 233 | int jniThrowIOException(JNIEnv* env, int errnum) | 
 | 234 | { | 
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 235 |     char buffer[80]; | 
| Elliott Hughes | 54b596c | 2009-10-20 16:59:01 -0700 | [diff] [blame] | 236 |     const char* message = jniStrError(errnum, buffer, sizeof(buffer)); | 
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 237 |     return jniThrowException(env, "java/io/IOException", message); | 
 | 238 | } | 
 | 239 |  | 
| Jeff Brown | 431da18 | 2010-06-01 21:07:08 -0700 | [diff] [blame] | 240 | /* | 
 | 241 |  * Log an exception. | 
 | 242 |  * If exception is NULL, logs the current exception in the JNI environment, if any. | 
 | 243 |  */ | 
 | 244 | void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable exception) | 
 | 245 | { | 
 | 246 |     int currentException = 0; | 
 | 247 |     if (exception == NULL) { | 
 | 248 |         exception = (*env)->ExceptionOccurred(env); | 
 | 249 |         if (exception == NULL) { | 
 | 250 |             return; | 
 | 251 |         } | 
 | 252 |  | 
 | 253 |         (*env)->ExceptionClear(env); | 
 | 254 |         currentException = 1; | 
 | 255 |     } | 
 | 256 |  | 
 | 257 |     char buffer[1024]; | 
 | 258 |     printStackTrace(env, exception, buffer, sizeof(buffer)); | 
 | 259 |     __android_log_write(priority, tag, buffer); | 
 | 260 |  | 
 | 261 |     if (currentException) { | 
 | 262 |         (*env)->Throw(env, exception); // rethrow | 
 | 263 |         (*env)->DeleteLocalRef(env, exception); | 
 | 264 |     } | 
 | 265 | } | 
 | 266 |  | 
| Elliott Hughes | 54b596c | 2009-10-20 16:59:01 -0700 | [diff] [blame] | 267 | const char* jniStrError(int errnum, char* buf, size_t buflen) | 
 | 268 | { | 
 | 269 |     // note: glibc has a nonstandard strerror_r that returns char* rather | 
 | 270 |     // than POSIX's int. | 
 | 271 |     // char *strerror_r(int errnum, char *buf, size_t n); | 
 | 272 |     char* ret = (char*) strerror_r(errnum, buf, buflen); | 
 | 273 |     if (((int)ret) == 0) { | 
 | 274 |         //POSIX strerror_r, success | 
 | 275 |         return buf; | 
 | 276 |     } else if (((int)ret) == -1) { | 
 | 277 |         //POSIX strerror_r, failure | 
 | 278 |         // (Strictly, POSIX only guarantees a value other than 0. The safest | 
 | 279 |         // way to implement this function is to use C++ and overload on the | 
 | 280 |         // type of strerror_r to accurately distinguish GNU from POSIX. But | 
 | 281 |         // realistic implementations will always return -1.) | 
 | 282 |         snprintf(buf, buflen, "errno %d", errnum); | 
 | 283 |         return buf; | 
 | 284 |     } else { | 
 | 285 |         //glibc strerror_r returning a string | 
 | 286 |         return ret; | 
 | 287 |     } | 
 | 288 | } | 
| Elliott Hughes | e9179f1 | 2010-06-17 13:19:01 -0700 | [diff] [blame] | 289 |  | 
 | 290 | static struct CachedFields { | 
 | 291 |     jclass fileDescriptorClass; | 
 | 292 |     jmethodID fileDescriptorCtor; | 
 | 293 |     jfieldID descriptorField; | 
 | 294 | } gCachedFields; | 
 | 295 |  | 
 | 296 | int registerJniHelp(JNIEnv* env) { | 
 | 297 |     gCachedFields.fileDescriptorClass = | 
 | 298 |             (*env)->NewGlobalRef(env, (*env)->FindClass(env, "java/io/FileDescriptor")); | 
 | 299 |     if (gCachedFields.fileDescriptorClass == NULL) { | 
 | 300 |         return -1; | 
 | 301 |     } | 
 | 302 |  | 
 | 303 |     gCachedFields.fileDescriptorCtor = | 
 | 304 |             (*env)->GetMethodID(env, gCachedFields.fileDescriptorClass, "<init>", "()V"); | 
 | 305 |     if (gCachedFields.fileDescriptorCtor == NULL) { | 
 | 306 |         return -1; | 
 | 307 |     } | 
 | 308 |  | 
 | 309 |     gCachedFields.descriptorField = | 
 | 310 |             (*env)->GetFieldID(env, gCachedFields.fileDescriptorClass, "descriptor", "I"); | 
 | 311 |     if (gCachedFields.descriptorField == NULL) { | 
 | 312 |         return -1; | 
 | 313 |     } | 
 | 314 |  | 
 | 315 |     return 0; | 
 | 316 | } | 
 | 317 |  | 
 | 318 | /* | 
 | 319 |  * Create a java.io.FileDescriptor given an integer fd | 
 | 320 |  */ | 
 | 321 | jobject jniCreateFileDescriptor(JNIEnv* env, int fd) { | 
 | 322 |     jobject fileDescriptor = (*env)->NewObject(env, | 
 | 323 |             gCachedFields.fileDescriptorClass, gCachedFields.fileDescriptorCtor); | 
 | 324 |     jniSetFileDescriptorOfFD(env, fileDescriptor, fd); | 
 | 325 |     return fileDescriptor; | 
 | 326 | } | 
 | 327 |  | 
 | 328 | /* | 
 | 329 |  * Get an int file descriptor from a java.io.FileDescriptor | 
 | 330 |  */ | 
 | 331 | int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) { | 
 | 332 |     return (*env)->GetIntField(env, fileDescriptor, gCachedFields.descriptorField); | 
 | 333 | } | 
 | 334 |  | 
 | 335 | /* | 
 | 336 |  * Set the descriptor of a java.io.FileDescriptor | 
 | 337 |  */ | 
 | 338 | void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) { | 
 | 339 |     (*env)->SetIntField(env, fileDescriptor, gCachedFields.descriptorField, value); | 
 | 340 | } | 
| Andy McFadden | 37b8005 | 2011-02-08 12:07:32 -0800 | [diff] [blame] | 341 |  | 
 | 342 | /* | 
 | 343 |  * DO NOT USE THIS FUNCTION | 
 | 344 |  * | 
 | 345 |  * Get a pointer to the elements of a non-movable array. | 
 | 346 |  * | 
 | 347 |  * The semantics are similar to GetDirectBufferAddress.  Specifically, the VM | 
 | 348 |  * guarantees that the array will not move, and the caller must ensure that | 
 | 349 |  * it does not continue to use the pointer after the object is collected. | 
 | 350 |  * | 
 | 351 |  * We currently use an illegal sequence that trips up CheckJNI when | 
 | 352 |  * the "forcecopy" mode is enabled.  We pass in a magic value to work | 
 | 353 |  * around the problem. | 
 | 354 |  * | 
 | 355 |  * Returns NULL if the array is movable. | 
 | 356 |  */ | 
 | 357 | jbyte* jniGetNonMovableArrayElements(JNIEnv* env, jarray arrayObj) | 
 | 358 | { | 
 | 359 | #define kNoCopyMagic 0xd5aab57f     /* also in CheckJni.c */ | 
 | 360 |  | 
 | 361 |     /* | 
 | 362 |      * Normally the "isCopy" parameter is for a return value only, so the | 
 | 363 |      * non-CheckJNI VM will ignore whatever we pass in. | 
 | 364 |      */ | 
 | 365 |     uint32_t noCopy = kNoCopyMagic; | 
 | 366 |     jbyte *addr = (*env)->GetByteArrayElements(env, arrayObj, | 
 | 367 |             (jboolean*)&noCopy); | 
 | 368 |  | 
 | 369 |     /* | 
 | 370 |      * The non-CheckJNI implementation only cares about the array object, | 
 | 371 |      * so we can replace the element pointer with the magic value. | 
 | 372 |      */ | 
 | 373 |     (*env)->ReleaseByteArrayElements(env, arrayObj, (jbyte*) kNoCopyMagic, 0); | 
 | 374 |     return addr; | 
 | 375 | } |