SQLiteDatabase: Add mechanism for registering custom sqlite3 functions

This allows you to define a callback in Java that can be called from
sqlite3 database triggers.

Change-Id: I09fdbd38c9807b6b0dd19c2761b01e8db76f1adc
Signed-off-by: Mike Lockwood <lockwood@android.com>
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();
         }
     }