Loading core/java/android/database/sqlite/SQLiteDatabase.java +60 −6 Original line number Diff line number Diff line Loading @@ -1047,6 +1047,7 @@ public class SQLiteDatabase extends SQLiteClosable { closeClosable(); // finalize ALL statements queued up so far closePendingStatements(); releaseCustomFunctions(); // close this database instance - regardless of its reference count value dbclose(); if (mConnectionPool != null) { Loading Loading @@ -1082,6 +1083,54 @@ public class SQLiteDatabase extends SQLiteClosable { */ private native void dbclose(); /** * A callback interface for a custom sqlite3 function. * This can be used to create a function that can be called from * sqlite3 database triggers. * @hide */ public interface CustomFunction { public void callback(String[] args); } /** * Registers a CustomFunction callback as a function that can be called from * sqlite3 database triggers. * @param name the name of the sqlite3 function * @param numArgs the number of arguments for the function * @param function callback to call when the function is executed * @hide */ public void addCustomFunction(String name, int numArgs, CustomFunction function) { verifyDbIsOpen(); synchronized (mCustomFunctions) { int ref = native_addCustomFunction(name, numArgs, function); if (ref != 0) { // save a reference to the function for cleanup later mCustomFunctions.add(new Integer(ref)); } else { throw new SQLiteException("failed to add custom function " + name); } } } private void releaseCustomFunctions() { synchronized (mCustomFunctions) { for (int i = 0; i < mCustomFunctions.size(); i++) { Integer function = mCustomFunctions.get(i); native_releaseCustomFunction(function.intValue()); } mCustomFunctions.clear(); } } // list of CustomFunction references so we can clean up when the database closes private final ArrayList<Integer> mCustomFunctions = new ArrayList<Integer>(); private native int native_addCustomFunction(String name, int numArgs, CustomFunction function); private native void native_releaseCustomFunction(int function); /** * Gets the database version. * Loading Loading @@ -1959,12 +2008,17 @@ public class SQLiteDatabase extends SQLiteClosable { } @Override protected void finalize() { protected void finalize() throws Throwable { try { if (isOpen()) { Log.e(TAG, "close() was never explicitly called on database '" + mPath + "' ", mStackTrace); closeClosable(); onAllReferencesReleased(); releaseCustomFunctions(); } } finally { super.finalize(); } } Loading core/jni/android_database_SQLiteDatabase.cpp +72 −0 Original line number Diff line number Diff line Loading @@ -62,6 +62,7 @@ enum { }; static jfieldID offset_db_handle; static jmethodID method_custom_function_callback; static char *createStr(const char *path, short extra) { int len = strlen(path) + extra; Loading Loading @@ -458,6 +459,62 @@ static void native_finalize(JNIEnv* env, jobject object, jint statementId) } } static void custom_function_callback(sqlite3_context * context, int argc, sqlite3_value ** argv) { JNIEnv* env = AndroidRuntime::getJNIEnv(); if (!env) { LOGE("custom_function_callback cannot call into Java on this thread"); return; } // pack up the arguments into a string array jobjectArray strArray = env->NewObjectArray(argc, env->FindClass("java/lang/String"), NULL); if (!strArray) { jniThrowException(env, "java/lang/OutOfMemoryError", NULL); return; } for (int i = 0; i < argc; i++) { char* arg = (char *)sqlite3_value_text(argv[i]); jobject obj = env->NewStringUTF(arg); if (!obj) { jniThrowException(env, "java/lang/OutOfMemoryError", NULL); return; } env->SetObjectArrayElement(strArray, i, obj); env->DeleteLocalRef(obj); } // get global ref to CustomFunction object from our user data jobject function = (jobject)sqlite3_user_data(context); env->CallVoidMethod(function, method_custom_function_callback, strArray); } static jint native_addCustomFunction(JNIEnv* env, jobject object, jstring name, jint numArgs, jobject function) { sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle); char const *nameStr = env->GetStringUTFChars(name, NULL); jobject ref = env->NewGlobalRef(function); LOGD("native_addCustomFunction %s ref: %d", nameStr, ref); int err = sqlite3_create_function(handle, nameStr, numArgs, SQLITE_UTF8, (void *)ref, custom_function_callback, NULL, NULL); env->ReleaseStringUTFChars(name, nameStr); if (err == SQLITE_OK) return (int)ref; else { LOGE("sqlite3_create_function returned %d", err); env->DeleteGlobalRef(ref); throw_sqlite3_exception(env, handle); return 0; } } static void native_releaseCustomFunction(JNIEnv* env, jobject object, jint ref) { LOGD("native_releaseCustomFunction %d", ref); env->DeleteGlobalRef((jobject)ref); } static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ Loading @@ -472,6 +529,10 @@ static JNINativeMethod sMethods[] = {"native_getDbLookaside", "()I", (void *)native_getDbLookaside}, {"releaseMemory", "()I", (void *)native_releaseMemory}, {"native_finalize", "(I)V", (void *)native_finalize}, {"native_addCustomFunction", "(Ljava/lang/String;ILandroid/database/sqlite/SQLiteDatabase$CustomFunction;)I", (void *)native_addCustomFunction}, {"native_releaseCustomFunction", "(I)V", (void *)native_releaseCustomFunction}, }; int register_android_database_SQLiteDatabase(JNIEnv *env) Loading @@ -490,6 +551,17 @@ int register_android_database_SQLiteDatabase(JNIEnv *env) return -1; } clazz = env->FindClass("android/database/sqlite/SQLiteDatabase$CustomFunction"); if (clazz == NULL) { LOGE("Can't find android/database/sqlite/SQLiteDatabase$CustomFunction\n"); return -1; } method_custom_function_callback = env->GetMethodID(clazz, "callback", "([Ljava/lang/String;)V"); if (method_custom_function_callback == NULL) { LOGE("Can't find method SQLiteDatabase.CustomFunction.callback\n"); return -1; } return AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteDatabase", sMethods, NELEM(sMethods)); } Loading Loading
core/java/android/database/sqlite/SQLiteDatabase.java +60 −6 Original line number Diff line number Diff line Loading @@ -1047,6 +1047,7 @@ public class SQLiteDatabase extends SQLiteClosable { closeClosable(); // finalize ALL statements queued up so far closePendingStatements(); releaseCustomFunctions(); // close this database instance - regardless of its reference count value dbclose(); if (mConnectionPool != null) { Loading Loading @@ -1082,6 +1083,54 @@ public class SQLiteDatabase extends SQLiteClosable { */ private native void dbclose(); /** * A callback interface for a custom sqlite3 function. * This can be used to create a function that can be called from * sqlite3 database triggers. * @hide */ public interface CustomFunction { public void callback(String[] args); } /** * Registers a CustomFunction callback as a function that can be called from * sqlite3 database triggers. * @param name the name of the sqlite3 function * @param numArgs the number of arguments for the function * @param function callback to call when the function is executed * @hide */ public void addCustomFunction(String name, int numArgs, CustomFunction function) { verifyDbIsOpen(); synchronized (mCustomFunctions) { int ref = native_addCustomFunction(name, numArgs, function); if (ref != 0) { // save a reference to the function for cleanup later mCustomFunctions.add(new Integer(ref)); } else { throw new SQLiteException("failed to add custom function " + name); } } } private void releaseCustomFunctions() { synchronized (mCustomFunctions) { for (int i = 0; i < mCustomFunctions.size(); i++) { Integer function = mCustomFunctions.get(i); native_releaseCustomFunction(function.intValue()); } mCustomFunctions.clear(); } } // list of CustomFunction references so we can clean up when the database closes private final ArrayList<Integer> mCustomFunctions = new ArrayList<Integer>(); private native int native_addCustomFunction(String name, int numArgs, CustomFunction function); private native void native_releaseCustomFunction(int function); /** * Gets the database version. * Loading Loading @@ -1959,12 +2008,17 @@ public class SQLiteDatabase extends SQLiteClosable { } @Override protected void finalize() { protected void finalize() throws Throwable { try { if (isOpen()) { Log.e(TAG, "close() was never explicitly called on database '" + mPath + "' ", mStackTrace); closeClosable(); onAllReferencesReleased(); releaseCustomFunctions(); } } finally { super.finalize(); } } Loading
core/jni/android_database_SQLiteDatabase.cpp +72 −0 Original line number Diff line number Diff line Loading @@ -62,6 +62,7 @@ enum { }; static jfieldID offset_db_handle; static jmethodID method_custom_function_callback; static char *createStr(const char *path, short extra) { int len = strlen(path) + extra; Loading Loading @@ -458,6 +459,62 @@ static void native_finalize(JNIEnv* env, jobject object, jint statementId) } } static void custom_function_callback(sqlite3_context * context, int argc, sqlite3_value ** argv) { JNIEnv* env = AndroidRuntime::getJNIEnv(); if (!env) { LOGE("custom_function_callback cannot call into Java on this thread"); return; } // pack up the arguments into a string array jobjectArray strArray = env->NewObjectArray(argc, env->FindClass("java/lang/String"), NULL); if (!strArray) { jniThrowException(env, "java/lang/OutOfMemoryError", NULL); return; } for (int i = 0; i < argc; i++) { char* arg = (char *)sqlite3_value_text(argv[i]); jobject obj = env->NewStringUTF(arg); if (!obj) { jniThrowException(env, "java/lang/OutOfMemoryError", NULL); return; } env->SetObjectArrayElement(strArray, i, obj); env->DeleteLocalRef(obj); } // get global ref to CustomFunction object from our user data jobject function = (jobject)sqlite3_user_data(context); env->CallVoidMethod(function, method_custom_function_callback, strArray); } static jint native_addCustomFunction(JNIEnv* env, jobject object, jstring name, jint numArgs, jobject function) { sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle); char const *nameStr = env->GetStringUTFChars(name, NULL); jobject ref = env->NewGlobalRef(function); LOGD("native_addCustomFunction %s ref: %d", nameStr, ref); int err = sqlite3_create_function(handle, nameStr, numArgs, SQLITE_UTF8, (void *)ref, custom_function_callback, NULL, NULL); env->ReleaseStringUTFChars(name, nameStr); if (err == SQLITE_OK) return (int)ref; else { LOGE("sqlite3_create_function returned %d", err); env->DeleteGlobalRef(ref); throw_sqlite3_exception(env, handle); return 0; } } static void native_releaseCustomFunction(JNIEnv* env, jobject object, jint ref) { LOGD("native_releaseCustomFunction %d", ref); env->DeleteGlobalRef((jobject)ref); } static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ Loading @@ -472,6 +529,10 @@ static JNINativeMethod sMethods[] = {"native_getDbLookaside", "()I", (void *)native_getDbLookaside}, {"releaseMemory", "()I", (void *)native_releaseMemory}, {"native_finalize", "(I)V", (void *)native_finalize}, {"native_addCustomFunction", "(Ljava/lang/String;ILandroid/database/sqlite/SQLiteDatabase$CustomFunction;)I", (void *)native_addCustomFunction}, {"native_releaseCustomFunction", "(I)V", (void *)native_releaseCustomFunction}, }; int register_android_database_SQLiteDatabase(JNIEnv *env) Loading @@ -490,6 +551,17 @@ int register_android_database_SQLiteDatabase(JNIEnv *env) return -1; } clazz = env->FindClass("android/database/sqlite/SQLiteDatabase$CustomFunction"); if (clazz == NULL) { LOGE("Can't find android/database/sqlite/SQLiteDatabase$CustomFunction\n"); return -1; } method_custom_function_callback = env->GetMethodID(clazz, "callback", "([Ljava/lang/String;)V"); if (method_custom_function_callback == NULL) { LOGE("Can't find method SQLiteDatabase.CustomFunction.callback\n"); return -1; } return AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteDatabase", sMethods, NELEM(sMethods)); } Loading