274 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			274 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C++
		
	
	
	
/*
 | 
						|
 * Copyright (C) 2008 The Android Open Source Project
 | 
						|
 *
 | 
						|
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
 * you may not use this file except in compliance with the License.
 | 
						|
 * You may obtain a copy of the License at
 | 
						|
 *
 | 
						|
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 *
 | 
						|
 * Unless required by applicable law or agreed to in writing, software
 | 
						|
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
 * See the License for the specific language governing permissions and
 | 
						|
 * limitations under the License.
 | 
						|
 */
 | 
						|
 | 
						|
#define LOG_TAG "PlatformLibrary"
 | 
						|
#include "utils/Log.h"
 | 
						|
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <assert.h>
 | 
						|
 | 
						|
#include "jni.h"
 | 
						|
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
/*
 | 
						|
 * Field/method IDs and class object references.
 | 
						|
 *
 | 
						|
 * You should not need to store the JNIEnv pointer in here.  It is
 | 
						|
 * thread-specific and will be passed back in on every call.
 | 
						|
 */
 | 
						|
static struct {
 | 
						|
    jclass      platformLibraryClass;
 | 
						|
    jfieldID    jniInt;
 | 
						|
    jmethodID   yodel;
 | 
						|
} gCachedState;
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
/*
 | 
						|
 * Helper function to throw an arbitrary exception.
 | 
						|
 *
 | 
						|
 * Takes the exception class name, a format string, and one optional integer
 | 
						|
 * argument (useful for including an error code, perhaps from errno).
 | 
						|
 */
 | 
						|
static void throwException(JNIEnv* env, const char* ex, const char* fmt,
 | 
						|
    int data) {
 | 
						|
 | 
						|
    if (jclass cls = env->FindClass(ex)) {
 | 
						|
        if (fmt != NULL) {
 | 
						|
            char msg[1000];
 | 
						|
            snprintf(msg, sizeof(msg), fmt, data);
 | 
						|
            env->ThrowNew(cls, msg);
 | 
						|
        } else {
 | 
						|
            env->ThrowNew(cls, NULL);
 | 
						|
        }
 | 
						|
 | 
						|
        /*
 | 
						|
         * This is usually not necessary -- local references are released
 | 
						|
         * automatically when the native code returns to the VM.  It's
 | 
						|
         * required if the code doesn't actually return, e.g. it's sitting
 | 
						|
         * in a native event loop.
 | 
						|
         */
 | 
						|
        env->DeleteLocalRef(cls);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Trivial sample method.
 | 
						|
 *
 | 
						|
 * If "bad" is true, this throws an exception.  Otherwise, this sets the
 | 
						|
 * "mJniInt" field to 42 and returns 24.
 | 
						|
 */
 | 
						|
static jint PlatformLibrary_getJniInt(JNIEnv* env, jobject thiz, jboolean bad) {
 | 
						|
    if (bad) {
 | 
						|
        throwException(env, "java/lang/IllegalStateException",
 | 
						|
                "you are bad", 0);
 | 
						|
        return 0;       /* return value will be ignored */
 | 
						|
    }
 | 
						|
    env->SetIntField(thiz, gCachedState.jniInt, 42);
 | 
						|
    return (jint)24;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * A more complex sample method.
 | 
						|
 *
 | 
						|
 * This takes a String as an argument, and returns a new String with
 | 
						|
 * characters in reverse order.  The new string is passed to another method.
 | 
						|
 * This demonstrates basic String manipulation functions and method
 | 
						|
 * invocation.
 | 
						|
 *
 | 
						|
 * This method is declared "static", so there's no "this" pointer; instead,
 | 
						|
 * we get a pointer to the class object.
 | 
						|
 */
 | 
						|
static jstring PlatformLibrary_reverseString(JNIEnv* env, jclass clazz,
 | 
						|
    jstring str) {
 | 
						|
 | 
						|
    if (str == NULL) {
 | 
						|
        throwException(env, "java/lang/NullPointerException", NULL, 0);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    /*
 | 
						|
     * Get a pointer to the string's UTF-16 character data.  The data
 | 
						|
     * may be a copy or a pointer to the original.  Since String data
 | 
						|
     * is immutable, we're not allowed to touch it.
 | 
						|
     */
 | 
						|
    const jchar* strChars = env->GetStringChars(str, NULL);
 | 
						|
    if (strChars == NULL) {
 | 
						|
        /* something went wrong */
 | 
						|
        ALOGW("Couldn't get string chars\n");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    jsize strLength = env->GetStringLength(str);
 | 
						|
 | 
						|
    /*
 | 
						|
     * Write a progress message to the log.  Log messages are UTF-8, so
 | 
						|
     * we want to convert the string to show it.
 | 
						|
     */
 | 
						|
    const char* printable = env->GetStringUTFChars(str, NULL);
 | 
						|
    if (printable != NULL) {
 | 
						|
        ALOGD("Reversing string '%s'\n", printable);
 | 
						|
        env->ReleaseStringUTFChars(str, printable);
 | 
						|
    }
 | 
						|
 | 
						|
    /*
 | 
						|
     * Copy the characters to temporary storage, reversing as we go.
 | 
						|
     */
 | 
						|
    jchar tempChars[strLength];
 | 
						|
    for (int i = 0; i < strLength; i++) {
 | 
						|
        tempChars[i] = strChars[strLength -1 -i];
 | 
						|
    }
 | 
						|
 | 
						|
    /*
 | 
						|
     * Release the original String.  That way, if something fails later on,
 | 
						|
     * we don't have to worry about this leading to a memory leak.
 | 
						|
     */
 | 
						|
    env->ReleaseStringChars(str, strChars);
 | 
						|
    strChars = NULL;            /* this pointer no longer valid */
 | 
						|
 | 
						|
    /*
 | 
						|
     * Create a new String with the chars.
 | 
						|
     */
 | 
						|
    jstring result = env->NewString(tempChars, strLength);
 | 
						|
    if (result == NULL) {
 | 
						|
        ALOGE("NewString failed\n");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    /*
 | 
						|
     * Now let's do something with it.  We already have the methodID for
 | 
						|
     * "yodel", so we can invoke it directly.  It's in our class, so we
 | 
						|
     * can use the Class object reference that was passed in.
 | 
						|
     */
 | 
						|
    env->CallStaticVoidMethod(clazz, gCachedState.yodel, result);
 | 
						|
 | 
						|
    return result;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
/*
 | 
						|
 * Array of methods.
 | 
						|
 *
 | 
						|
 * Each entry has three fields: the name of the method, the method
 | 
						|
 * signature, and a pointer to the native implementation.
 | 
						|
 */
 | 
						|
static const JNINativeMethod gMethods[] = {
 | 
						|
    { "getJniInt",          "(Z)I",
 | 
						|
                        (void*)PlatformLibrary_getJniInt },
 | 
						|
    { "reverseString",      "(Ljava/lang/String;)Ljava/lang/String;",
 | 
						|
                        (void*)PlatformLibrary_reverseString },
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
 * Do some (slow-ish) lookups now and save the results.
 | 
						|
 *
 | 
						|
 * Returns 0 on success.
 | 
						|
 */
 | 
						|
static int cacheIds(JNIEnv* env, jclass clazz) {
 | 
						|
    /*
 | 
						|
     * Save the class in case we want to use it later.  Because this is a
 | 
						|
     * reference to the Class object, we need to convert it to a JNI global
 | 
						|
     * reference.
 | 
						|
     */
 | 
						|
    gCachedState.platformLibraryClass = (jclass) env->NewGlobalRef(clazz);
 | 
						|
    if (clazz == NULL) {
 | 
						|
        ALOGE("Can't create new global ref\n");
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    /*
 | 
						|
     * Cache field and method IDs.  IDs are not references, which means we
 | 
						|
     * don't need to call NewGlobalRef on them.
 | 
						|
     */
 | 
						|
    gCachedState.jniInt = env->GetFieldID(clazz, "mJniInt", "I");
 | 
						|
    if (gCachedState.jniInt == NULL) {
 | 
						|
        ALOGE("Can't find PlatformLibrary.mJniInt\n");
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    gCachedState.yodel = env->GetStaticMethodID(clazz, "yodel",
 | 
						|
        "(Ljava/lang/String;)V");
 | 
						|
    if (gCachedState.yodel == NULL) {
 | 
						|
        ALOGE("Can't find PlatformLibrary.yodel\n");
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Explicitly register all methods for our class.
 | 
						|
 *
 | 
						|
 * While we're at it, cache some class references and method/field IDs.
 | 
						|
 *
 | 
						|
 * Returns 0 on success.
 | 
						|
 */
 | 
						|
static int registerMethods(JNIEnv* env) {
 | 
						|
    static const char* const kClassName =
 | 
						|
        "com/example/android/platform_library/PlatformLibrary";
 | 
						|
    jclass clazz;
 | 
						|
 | 
						|
    /* look up the class */
 | 
						|
    clazz = env->FindClass(kClassName);
 | 
						|
    if (clazz == NULL) {
 | 
						|
        ALOGE("Can't find class %s\n", kClassName);
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    /* register all the methods */
 | 
						|
    if (env->RegisterNatives(clazz, gMethods,
 | 
						|
            sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)
 | 
						|
    {
 | 
						|
        ALOGE("Failed registering methods for %s\n", kClassName);
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    /* fill out the rest of the ID cache */
 | 
						|
    return cacheIds(env, clazz);
 | 
						|
}
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
/*
 | 
						|
 * This is called by the VM when the shared library is first loaded.
 | 
						|
 */
 | 
						|
jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) {
 | 
						|
    JNIEnv* env = NULL;
 | 
						|
    jint result = -1;
 | 
						|
 | 
						|
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
 | 
						|
        ALOGE("ERROR: GetEnv failed\n");
 | 
						|
        goto bail;
 | 
						|
    }
 | 
						|
    assert(env != NULL);
 | 
						|
 | 
						|
    if (registerMethods(env) != 0) {
 | 
						|
        ALOGE("ERROR: PlatformLibrary native registration failed\n");
 | 
						|
        goto bail;
 | 
						|
    }
 | 
						|
 | 
						|
    /* success -- return valid version number */
 | 
						|
    result = JNI_VERSION_1_4;
 | 
						|
 | 
						|
bail:
 | 
						|
    return result;
 | 
						|
}
 |