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 */
throwException(JNIEnv * env,const char * ex,const char * fmt,int data)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 */
PlatformLibrary_getJniInt(JNIEnv * env,jobject thiz,jboolean bad)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 */
PlatformLibrary_reverseString(JNIEnv * env,jclass clazz,jstring str)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 */
cacheIds(JNIEnv * env,jclass clazz)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 */
registerMethods(JNIEnv * env)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 */
JNI_OnLoad(JavaVM * vm,void *)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