Merge "Remove our copy of the f_mtp.h kernel header, now that it is in bionic."
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 441370a..c0226f8 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -1047,6 +1047,7 @@
             closeClosable();
             // finalize ALL statements queued up so far
             closePendingStatements();
+            releaseCustomFunctions();
             // close this database instance - regardless of its reference count value
             dbclose();
             if (mConnectionPool != null) {
@@ -1083,6 +1084,54 @@
     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.
      *
      * @return the database version
@@ -1959,12 +2008,17 @@
     }
 
     @Override
-    protected void finalize() {
-        if (isOpen()) {
-            Log.e(TAG, "close() was never explicitly called on database '" +
-                    mPath + "' ", mStackTrace);
-            closeClosable();
-            onAllReferencesReleased();
+    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();
         }
     }
 
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index a23a5a7..10f1d2b 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -289,6 +289,7 @@
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC1);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC2);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC3);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC4);
 
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.NAME);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
diff --git a/core/jni/android_database_SQLiteDatabase.cpp b/core/jni/android_database_SQLiteDatabase.cpp
index 5a92193..290b532 100644
--- a/core/jni/android_database_SQLiteDatabase.cpp
+++ b/core/jni/android_database_SQLiteDatabase.cpp
@@ -62,6 +62,7 @@
 };
 
 static jfieldID offset_db_handle;
+static jmethodID method_custom_function_callback;
 
 static char *createStr(const char *path, short extra) {
     int len = strlen(path) + extra;
@@ -458,6 +459,62 @@
     }
 }
 
+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 */
@@ -472,6 +529,10 @@
     {"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)
@@ -490,6 +551,17 @@
         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));
 }
diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk
index 46d7493..62f824f 100644
--- a/libs/ui/tests/Android.mk
+++ b/libs/ui/tests/Android.mk
@@ -2,6 +2,9 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
+ifneq ($(TARGET_SIMULATOR),true)
+
+# Build the unit tests.
 test_src_files := \
     InputChannel_test.cpp \
     InputDispatcher_test.cpp \
@@ -43,3 +46,5 @@
 
 # Build the manual test programs.
 include $(call all-subdir-makefiles)
+
+endif
\ No newline at end of file
diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk
index f1b8cd5..b9f206a 100644
--- a/libs/utils/tests/Android.mk
+++ b/libs/utils/tests/Android.mk
@@ -2,6 +2,9 @@
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
+ifneq ($(TARGET_SIMULATOR),true)
+
+# Build the unit tests.
 test_src_files := \
 	ObbFile_test.cpp \
 	PollLoop_test.cpp
@@ -37,3 +40,5 @@
     $(eval LOCAL_MODULE_TAGS := $(module_tags)) \
     $(eval include $(BUILD_EXECUTABLE)) \
 )
+
+endif
\ No newline at end of file