1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 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 #define LOG_TAG "PlatformLibrary" 18 #include "utils/Log.h" 19 20 #include <stdlib.h> 21 #include <string.h> 22 #include <unistd.h> 23 #include <assert.h> 24 25 #include "jni.h" 26 27 28 // ---------------------------------------------------------------------------- 29 30 /* 31 * Field/method IDs and class object references. 32 * 33 * You should not need to store the JNIEnv pointer in here. It is 34 * thread-specific and will be passed back in on every call. 35 */ 36 static struct { 37 jclass platformLibraryClass; 38 jfieldID jniInt; 39 jmethodID yodel; 40 } gCachedState; 41 42 // ---------------------------------------------------------------------------- 43 44 /* 45 * Helper function to throw an arbitrary exception. 46 * 47 * Takes the exception class name, a format string, and one optional integer 48 * argument (useful for including an error code, perhaps from errno). 49 */ 50 static void throwException(JNIEnv* env, const char* ex, const char* fmt, 51 int data) { 52 53 if (jclass cls = env->FindClass(ex)) { 54 if (fmt != NULL) { 55 char msg[1000]; 56 snprintf(msg, sizeof(msg), fmt, data); 57 env->ThrowNew(cls, msg); 58 } else { 59 env->ThrowNew(cls, NULL); 60 } 61 62 /* 63 * This is usually not necessary -- local references are released 64 * automatically when the native code returns to the VM. It's 65 * required if the code doesn't actually return, e.g. it's sitting 66 * in a native event loop. 67 */ 68 env->DeleteLocalRef(cls); 69 } 70 } 71 72 /* 73 * Trivial sample method. 74 * 75 * If "bad" is true, this throws an exception. Otherwise, this sets the 76 * "mJniInt" field to 42 and returns 24. 77 */ 78 static jint PlatformLibrary_getJniInt(JNIEnv* env, jobject thiz, jboolean bad) { 79 if (bad) { 80 throwException(env, "java/lang/IllegalStateException", 81 "you are bad", 0); 82 return 0; /* return value will be ignored */ 83 } 84 env->SetIntField(thiz, gCachedState.jniInt, 42); 85 return (jint)24; 86 } 87 88 /* 89 * A more complex sample method. 90 * 91 * This takes a String as an argument, and returns a new String with 92 * characters in reverse order. The new string is passed to another method. 93 * This demonstrates basic String manipulation functions and method 94 * invocation. 95 * 96 * This method is declared "static", so there's no "this" pointer; instead, 97 * we get a pointer to the class object. 98 */ 99 static jstring PlatformLibrary_reverseString(JNIEnv* env, jclass clazz, 100 jstring str) { 101 102 if (str == NULL) { 103 throwException(env, "java/lang/NullPointerException", NULL, 0); 104 return NULL; 105 } 106 107 /* 108 * Get a pointer to the string's UTF-16 character data. The data 109 * may be a copy or a pointer to the original. Since String data 110 * is immutable, we're not allowed to touch it. 111 */ 112 const jchar* strChars = env->GetStringChars(str, NULL); 113 if (strChars == NULL) { 114 /* something went wrong */ 115 ALOGW("Couldn't get string chars\n"); 116 return NULL; 117 } 118 jsize strLength = env->GetStringLength(str); 119 120 /* 121 * Write a progress message to the log. Log messages are UTF-8, so 122 * we want to convert the string to show it. 123 */ 124 const char* printable = env->GetStringUTFChars(str, NULL); 125 if (printable != NULL) { 126 ALOGD("Reversing string '%s'\n", printable); 127 env->ReleaseStringUTFChars(str, printable); 128 } 129 130 /* 131 * Copy the characters to temporary storage, reversing as we go. 132 */ 133 jchar tempChars[strLength]; 134 for (int i = 0; i < strLength; i++) { 135 tempChars[i] = strChars[strLength -1 -i]; 136 } 137 138 /* 139 * Release the original String. That way, if something fails later on, 140 * we don't have to worry about this leading to a memory leak. 141 */ 142 env->ReleaseStringChars(str, strChars); 143 strChars = NULL; /* this pointer no longer valid */ 144 145 /* 146 * Create a new String with the chars. 147 */ 148 jstring result = env->NewString(tempChars, strLength); 149 if (result == NULL) { 150 ALOGE("NewString failed\n"); 151 return NULL; 152 } 153 154 /* 155 * Now let's do something with it. We already have the methodID for 156 * "yodel", so we can invoke it directly. It's in our class, so we 157 * can use the Class object reference that was passed in. 158 */ 159 env->CallStaticVoidMethod(clazz, gCachedState.yodel, result); 160 161 return result; 162 } 163 164 165 // ---------------------------------------------------------------------------- 166 167 /* 168 * Array of methods. 169 * 170 * Each entry has three fields: the name of the method, the method 171 * signature, and a pointer to the native implementation. 172 */ 173 static const JNINativeMethod gMethods[] = { 174 { "getJniInt", "(Z)I", 175 (void*)PlatformLibrary_getJniInt }, 176 { "reverseString", "(Ljava/lang/String;)Ljava/lang/String;", 177 (void*)PlatformLibrary_reverseString }, 178 }; 179 180 /* 181 * Do some (slow-ish) lookups now and save the results. 182 * 183 * Returns 0 on success. 184 */ 185 static int cacheIds(JNIEnv* env, jclass clazz) { 186 /* 187 * Save the class in case we want to use it later. Because this is a 188 * reference to the Class object, we need to convert it to a JNI global 189 * reference. 190 */ 191 gCachedState.platformLibraryClass = (jclass) env->NewGlobalRef(clazz); 192 if (clazz == NULL) { 193 ALOGE("Can't create new global ref\n"); 194 return -1; 195 } 196 197 /* 198 * Cache field and method IDs. IDs are not references, which means we 199 * don't need to call NewGlobalRef on them. 200 */ 201 gCachedState.jniInt = env->GetFieldID(clazz, "mJniInt", "I"); 202 if (gCachedState.jniInt == NULL) { 203 ALOGE("Can't find PlatformLibrary.mJniInt\n"); 204 return -1; 205 } 206 207 gCachedState.yodel = env->GetStaticMethodID(clazz, "yodel", 208 "(Ljava/lang/String;)V"); 209 if (gCachedState.yodel == NULL) { 210 ALOGE("Can't find PlatformLibrary.yodel\n"); 211 return -1; 212 } 213 214 return 0; 215 } 216 217 /* 218 * Explicitly register all methods for our class. 219 * 220 * While we're at it, cache some class references and method/field IDs. 221 * 222 * Returns 0 on success. 223 */ 224 static int registerMethods(JNIEnv* env) { 225 static const char* const kClassName = 226 "com/example/android/platform_library/PlatformLibrary"; 227 jclass clazz; 228 229 /* look up the class */ 230 clazz = env->FindClass(kClassName); 231 if (clazz == NULL) { 232 ALOGE("Can't find class %s\n", kClassName); 233 return -1; 234 } 235 236 /* register all the methods */ 237 if (env->RegisterNatives(clazz, gMethods, 238 sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK) 239 { 240 ALOGE("Failed registering methods for %s\n", kClassName); 241 return -1; 242 } 243 244 /* fill out the rest of the ID cache */ 245 return cacheIds(env, clazz); 246 } 247 248 // ---------------------------------------------------------------------------- 249 250 /* 251 * This is called by the VM when the shared library is first loaded. 252 */ 253 jint JNI_OnLoad(JavaVM* vm, void* reserved) { 254 JNIEnv* env = NULL; 255 jint result = -1; 256 257 if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { 258 ALOGE("ERROR: GetEnv failed\n"); 259 goto bail; 260 } 261 assert(env != NULL); 262 263 if (registerMethods(env) != 0) { 264 ALOGE("ERROR: PlatformLibrary native registration failed\n"); 265 goto bail; 266 } 267 268 /* success -- return valid version number */ 269 result = JNI_VERSION_1_4; 270 271 bail: 272 return result; 273 } 274