summaryrefslogtreecommitdiffstats
path: root/core/jni/android_database_CursorWindow.cpp
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commit54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch)
tree35051494d2af230dce54d6b31c6af8fc24091316 /core/jni/android_database_CursorWindow.cpp
downloadframeworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.zip
frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.gz
frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.bz2
Initial Contribution
Diffstat (limited to 'core/jni/android_database_CursorWindow.cpp')
-rw-r--r--core/jni/android_database_CursorWindow.cpp674
1 files changed, 674 insertions, 0 deletions
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
new file mode 100644
index 0000000..f19fbbf
--- /dev/null
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -0,0 +1,674 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "CursorWindow"
+
+#include <jni.h>
+#include <JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/String16.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "CursorWindow.h"
+#include "sqlite3_exception.h"
+#include "android_util_Binder.h"
+
+
+namespace android {
+
+static jfieldID gWindowField;
+static jfieldID gBufferField;
+static jfieldID gSizeCopiedField;
+
+#define GET_WINDOW(env, object) ((CursorWindow *)env->GetIntField(object, gWindowField))
+#define SET_WINDOW(env, object, window) (env->SetIntField(object, gWindowField, (int)window))
+#define SET_BUFFER(env, object, buf) (env->SetObjectField(object, gBufferField, buf))
+#define SET_SIZE_COPIED(env, object, size) (env->SetIntField(object, gSizeCopiedField, size))
+
+CursorWindow * get_window_from_object(JNIEnv * env, jobject javaWindow)
+{
+ return GET_WINDOW(env, javaWindow);
+}
+
+static void native_init_empty(JNIEnv * env, jobject object, jboolean localOnly)
+{
+ uint8_t * data;
+ size_t size;
+ CursorWindow * window;
+
+ window = new CursorWindow(MAX_WINDOW_SIZE);
+ if (!window) {
+ jniThrowException(env, "java/lang/RuntimeException", "No memory for native window object");
+ return;
+ }
+
+ if (!window->initBuffer(localOnly)) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Couldn't init cursor window");
+ delete window;
+ return;
+ }
+
+LOG_WINDOW("native_init_empty: window = %p", window);
+ SET_WINDOW(env, object, window);
+}
+
+static void native_init_memory(JNIEnv * env, jobject object, jobject memObj)
+{
+ sp<IMemory> memory = interface_cast<IMemory>(ibinderForJavaObject(env, memObj));
+ if (memory == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Couldn't get native binder");
+ return;
+ }
+
+ CursorWindow * window = new CursorWindow();
+ if (!window) {
+ jniThrowException(env, "java/lang/RuntimeException", "No memory for native window object");
+ return;
+ }
+ if (!window->setMemory(memory)) {
+ jniThrowException(env, "java/lang/RuntimeException", "No memory in memObj");
+ delete window;
+ return;
+ }
+
+LOG_WINDOW("native_init_memory: numRows = %d, numColumns = %d, window = %p", window->getNumRows(), window->getNumColumns(), window);
+ SET_WINDOW(env, object, window);
+}
+
+static jobject native_getBinder(JNIEnv * env, jobject object)
+{
+ CursorWindow * window = GET_WINDOW(env, object);
+ if (window) {
+ sp<IMemory> memory = window->getMemory();
+ if (memory != NULL) {
+ sp<IBinder> binder = memory->asBinder();
+ return javaObjectForIBinder(env, binder);
+ }
+ }
+ return NULL;
+}
+
+static void native_clear(JNIEnv * env, jobject object)
+{
+ CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Clearing window %p", window);
+ if (window == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", "clear() called after close()");
+ return;
+ }
+ window->clear();
+}
+
+static void native_close(JNIEnv * env, jobject object)
+{
+ CursorWindow * window = GET_WINDOW(env, object);
+ if (window) {
+LOG_WINDOW("Closing window %p", window);
+ delete window;
+ SET_WINDOW(env, object, 0);
+ }
+}
+
+static void throwExceptionWithRowCol(JNIEnv * env, jint row, jint column)
+{
+ char buf[100];
+ snprintf(buf, sizeof(buf), "get field slot from row %d col %d failed", row, column);
+ jniThrowException(env, "java/lang/IllegalStateException", buf);
+}
+
+static void throwUnknowTypeException(JNIEnv * env, jint type)
+{
+ char buf[80];
+ snprintf(buf, sizeof(buf), "UNKNOWN type %d", type);
+ jniThrowException(env, "java/lang/IllegalStateException", buf);
+}
+
+static jlong getLong_native(JNIEnv * env, jobject object, jint row, jint column)
+{
+ int32_t err;
+ CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Getting long for %d,%d from %p", row, column, window);
+
+ field_slot_t field;
+ err = window->read_field_slot(row, column, &field);
+ if (err != 0) {
+ throwExceptionWithRowCol(env, row, column);
+ return 0;
+ }
+
+ uint8_t type = field.type;
+ if (type == FIELD_TYPE_INTEGER) {
+ int64_t value;
+ if (window->getLong(row, column, &value)) {
+ return value;
+ }
+ return 0;
+ } else if (type == FIELD_TYPE_STRING) {
+ uint32_t size = field.data.buffer.size;
+ if (size > 0) {
+#if WINDOW_STORAGE_UTF8
+ return strtoll((char const *)window->offsetToPtr(field.data.buffer.offset), NULL, 0);
+#else
+ String8 ascii((char16_t *) window->offsetToPtr(field.data.buffer.offset), size / 2);
+ char const * str = ascii.string();
+ return strtoll(str, NULL, 0);
+#endif
+ } else {
+ return 0;
+ }
+ } else if (type == FIELD_TYPE_FLOAT) {
+ double value;
+ if (window->getDouble(row, column, &value)) {
+ return value;
+ }
+ return 0;
+ } else if (type == FIELD_TYPE_NULL) {
+ return 0;
+ } else if (type == FIELD_TYPE_BLOB) {
+ throw_sqlite3_exception(env, "Unable to convert BLOB to long");
+ return 0;
+ } else {
+ throwUnknowTypeException(env, type);
+ return 0;
+ }
+}
+
+static jbyteArray getBlob_native(JNIEnv* env, jobject object, jint row, jint column)
+{
+ int32_t err;
+ CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window);
+
+ field_slot_t field;
+ err = window->read_field_slot(row, column, &field);
+ if (err != 0) {
+ throwExceptionWithRowCol(env, row, column);
+ return NULL;
+ }
+
+ uint8_t type = field.type;
+ if (type == FIELD_TYPE_BLOB || type == FIELD_TYPE_STRING) {
+ jbyteArray byteArray = env->NewByteArray(field.data.buffer.size);
+ LOG_ASSERT(byteArray, "Native could not create new byte[]");
+ env->SetByteArrayRegion(byteArray, 0, field.data.buffer.size,
+ (const jbyte*)window->offsetToPtr(field.data.buffer.offset));
+ return byteArray;
+ } else if (type == FIELD_TYPE_INTEGER) {
+ throw_sqlite3_exception(env, "INTEGER data in getBlob_native ");
+ } else if (type == FIELD_TYPE_FLOAT) {
+ throw_sqlite3_exception(env, "FLOAT data in getBlob_native ");
+ } else if (type == FIELD_TYPE_NULL) {
+ // do nothing
+ } else {
+ throwUnknowTypeException(env, type);
+ }
+ return NULL;
+}
+
+static jboolean isBlob_native(JNIEnv* env, jobject object, jint row, jint column)
+{
+ int32_t err;
+ CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Checking if column is a blob for %d,%d from %p", row, column, window);
+
+ field_slot_t field;
+ err = window->read_field_slot(row, column, &field);
+ if (err != 0) {
+ throwExceptionWithRowCol(env, row, column);
+ return NULL;
+ }
+
+ return field.type == FIELD_TYPE_BLOB || field.type == FIELD_TYPE_NULL;
+}
+
+static jstring getString_native(JNIEnv* env, jobject object, jint row, jint column)
+{
+ int32_t err;
+ CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Getting string for %d,%d from %p", row, column, window);
+
+ field_slot_t field;
+ err = window->read_field_slot(row, column, &field);
+ if (err != 0) {
+ throwExceptionWithRowCol(env, row, column);
+ return NULL;
+ }
+
+ uint8_t type = field.type;
+ if (type == FIELD_TYPE_STRING) {
+ uint32_t size = field.data.buffer.size;
+ if (size > 0) {
+#if WINDOW_STORAGE_UTF8
+ // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string
+ String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1);
+ return env->NewString((jchar const *)utf16.string(), utf16.size());
+#else
+ return env->NewString((jchar const *)window->offsetToPtr(field.data.buffer.offset), size / 2);
+#endif
+ } else {
+ return env->NewStringUTF("");
+ }
+ } else if (type == FIELD_TYPE_INTEGER) {
+ int64_t value;
+ if (window->getLong(row, column, &value)) {
+ char buf[32];
+ snprintf(buf, sizeof(buf), "%lld", value);
+ return env->NewStringUTF(buf);
+ }
+ return NULL;
+ } else if (type == FIELD_TYPE_FLOAT) {
+ double value;
+ if (window->getDouble(row, column, &value)) {
+ char buf[32];
+ snprintf(buf, sizeof(buf), "%g", value);
+ return env->NewStringUTF(buf);
+ }
+ return NULL;
+ } else if (type == FIELD_TYPE_NULL) {
+ return NULL;
+ } else if (type == FIELD_TYPE_BLOB) {
+ throw_sqlite3_exception(env, "Unable to convert BLOB to string");
+ return NULL;
+ } else {
+ throwUnknowTypeException(env, type);
+ return NULL;
+ }
+}
+
+/**
+ * Use this only to convert characters that are known to be within the
+ * 0-127 range for direct conversion to UTF-16
+ */
+static jint charToJchar(const char* src, jchar* dst, jint bufferSize)
+{
+ int32_t len = strlen(src);
+
+ if (bufferSize < len) {
+ len = bufferSize;
+ }
+
+ for (int i = 0; i < len; i++) {
+ *dst++ = (*src++ & 0x7F);
+ }
+ return len;
+}
+
+static jcharArray copyStringToBuffer_native(JNIEnv* env, jobject object, jint row,
+ jint column, jint bufferSize, jobject buf)
+{
+ int32_t err;
+ CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Copying string for %d,%d from %p", row, column, window);
+
+ field_slot_t field;
+ err = window->read_field_slot(row, column, &field);
+ if (err != 0) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Unable to get field slot");
+ return NULL;
+ }
+
+ jcharArray buffer = (jcharArray)env->GetObjectField(buf, gBufferField);
+ if (buffer == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", "buf should not be null");
+ return NULL;
+ }
+ jchar* dst = env->GetCharArrayElements(buffer, NULL);
+ uint8_t type = field.type;
+ uint32_t sizeCopied = 0;
+ jcharArray newArray = NULL;
+ if (type == FIELD_TYPE_STRING) {
+ uint32_t size = field.data.buffer.size;
+ if (size > 0) {
+#if WINDOW_STORAGE_UTF8
+ // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string
+ String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1);
+ int32_t strSize = utf16.size();
+ if (strSize > bufferSize || dst == NULL) {
+ newArray = env->NewCharArray(strSize);
+ env->SetCharArrayRegion(newArray, 0, strSize, (jchar const *)utf16.string());
+ } else {
+ memcpy(dst, (jchar const *)utf16.string(), strSize * 2);
+ }
+ sizeCopied = strSize;
+#else
+ sizeCopied = size/2 + size % 2;
+ if (size > bufferSize * 2 || dst == NULL) {
+ newArray = env->NewCharArray(sizeCopied);
+ memcpy(newArray, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size);
+ } else {
+ memcpy(dst, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size);
+ }
+#endif
+ }
+ } else if (type == FIELD_TYPE_INTEGER) {
+ int64_t value;
+ if (window->getLong(row, column, &value)) {
+ char buf[32];
+ int len;
+ snprintf(buf, sizeof(buf), "%lld", value);
+ jchar* dst = env->GetCharArrayElements(buffer, NULL);
+ sizeCopied = charToJchar(buf, dst, bufferSize);
+ }
+ } else if (type == FIELD_TYPE_FLOAT) {
+ double value;
+ if (window->getDouble(row, column, &value)) {
+ char tempbuf[32];
+ snprintf(tempbuf, sizeof(tempbuf), "%g", value);
+ jchar* dst = env->GetCharArrayElements(buffer, NULL);
+ sizeCopied = charToJchar(tempbuf, dst, bufferSize);
+ }
+ } else if (type == FIELD_TYPE_NULL) {
+ } else if (type == FIELD_TYPE_BLOB) {
+ throw_sqlite3_exception(env, "Unable to convert BLOB to string");
+ } else {
+ LOGE("Unknown field type %d", type);
+ throw_sqlite3_exception(env, "UNKNOWN type in copyStringToBuffer_native()");
+ }
+ SET_SIZE_COPIED(env, buf, sizeCopied);
+ env->ReleaseCharArrayElements(buffer, dst, JNI_OK);
+ return newArray;
+}
+
+static jdouble getDouble_native(JNIEnv* env, jobject object, jint row, jint column)
+{
+ int32_t err;
+ CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Getting double for %d,%d from %p", row, column, window);
+
+ field_slot_t field;
+ err = window->read_field_slot(row, column, &field);
+ if (err != 0) {
+ throwExceptionWithRowCol(env, row, column);
+ return 0.0;
+ }
+
+ uint8_t type = field.type;
+ if (type == FIELD_TYPE_FLOAT) {
+ double value;
+ if (window->getDouble(row, column, &value)) {
+ return value;
+ }
+ return 0.0;
+ } else if (type == FIELD_TYPE_STRING) {
+ uint32_t size = field.data.buffer.size;
+ if (size > 0) {
+#if WINDOW_STORAGE_UTF8
+ return strtod((char const *)window->offsetToPtr(field.data.buffer.offset), NULL);
+#else
+ String8 ascii((char16_t *) window->offsetToPtr(field.data.buffer.offset), size / 2);
+ char const * str = ascii.string();
+ return strtod(str, NULL);
+#endif
+ } else {
+ return 0.0;
+ }
+ } else if (type == FIELD_TYPE_INTEGER) {
+ int64_t value;
+ if (window->getLong(row, column, &value)) {
+ return (double) value;
+ }
+ return 0.0;
+ } else if (type == FIELD_TYPE_NULL) {
+ return 0.0;
+ } else if (type == FIELD_TYPE_BLOB) {
+ throw_sqlite3_exception(env, "Unable to convert BLOB to double");
+ return 0.0;
+ } else {
+ throwUnknowTypeException(env, type);
+ return 0.0;
+ }
+}
+
+static jboolean isNull_native(JNIEnv* env, jobject object, jint row, jint column)
+{
+ CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Checking for NULL at %d,%d from %p", row, column, window);
+
+ bool isNull;
+ if (window->getNull(row, column, &isNull)) {
+ return isNull;
+ }
+
+ //TODO throw execption?
+ return true;
+}
+
+static jint getNumRows(JNIEnv * env, jobject object)
+{
+ CursorWindow * window = GET_WINDOW(env, object);
+ return window->getNumRows();
+}
+
+static jboolean setNumColumns(JNIEnv * env, jobject object, jint columnNum)
+{
+ CursorWindow * window = GET_WINDOW(env, object);
+ return window->setNumColumns(columnNum);
+}
+
+static jboolean allocRow(JNIEnv * env, jobject object)
+{
+ CursorWindow * window = GET_WINDOW(env, object);
+ return window->allocRow() != NULL;
+}
+
+static jboolean putBlob_native(JNIEnv * env, jobject object, jbyteArray value, jint row, jint col)
+{
+ CursorWindow * window = GET_WINDOW(env, object);
+ if (!value) {
+ LOG_WINDOW("How did a null value send to here");
+ return false;
+ }
+ field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col);
+ if (fieldSlot == NULL) {
+ LOG_WINDOW(" getFieldSlotWithCheck error ");
+ return false;
+ }
+
+ jint len = env->GetArrayLength(value);
+ int offset = window->alloc(len);
+ if (!offset) {
+ LOG_WINDOW("Failed allocating %u bytes", len);
+ return false;
+ }
+ jbyte * bytes = env->GetByteArrayElements(value, NULL);
+ window->copyIn(offset, (uint8_t const *)bytes, len);
+
+ // This must be updated after the call to alloc(), since that
+ // may move the field around in the window
+ fieldSlot->type = FIELD_TYPE_BLOB;
+ fieldSlot->data.buffer.offset = offset;
+ fieldSlot->data.buffer.size = len;
+ env->ReleaseByteArrayElements(value, bytes, JNI_ABORT);
+ LOG_WINDOW("%d,%d is BLOB with %u bytes @ %d", row, col, len, offset);
+ return true;
+}
+
+static jboolean putString_native(JNIEnv * env, jobject object, jstring value, jint row, jint col)
+{
+ CursorWindow * window = GET_WINDOW(env, object);
+ if (!value) {
+ LOG_WINDOW("How did a null value send to here");
+ return false;
+ }
+ field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col);
+ if (fieldSlot == NULL) {
+ LOG_WINDOW(" getFieldSlotWithCheck error ");
+ return false;
+ }
+
+#if WINDOW_STORAGE_UTF8
+ int len = env->GetStringUTFLength(value) + 1;
+ char const * valStr = env->GetStringUTFChars(value, NULL);
+#else
+ int len = env->GetStringLength(value);
+ // GetStringLength return number of chars and one char takes 2 bytes
+ len *= 2;
+ const jchar* valStr = env->GetStringChars(value, NULL);
+#endif
+ if (!valStr) {
+ LOG_WINDOW("value can't be transfer to UTFChars");
+ return false;
+ }
+
+ int offset = window->alloc(len);
+ if (!offset) {
+ LOG_WINDOW("Failed allocating %u bytes", len);
+#if WINDOW_STORAGE_UTF8
+ env->ReleaseStringUTFChars(value, valStr);
+#else
+ env->ReleaseStringChars(value, valStr);
+#endif
+ return false;
+ }
+
+ window->copyIn(offset, (uint8_t const *)valStr, len);
+
+ // This must be updated after the call to alloc(), since that
+ // may move the field around in the window
+ fieldSlot->type = FIELD_TYPE_STRING;
+ fieldSlot->data.buffer.offset = offset;
+ fieldSlot->data.buffer.size = len;
+
+ LOG_WINDOW("%d,%d is TEXT with %u bytes @ %d", row, col, len, offset);
+#if WINDOW_STORAGE_UTF8
+ env->ReleaseStringUTFChars(value, valStr);
+#else
+ env->ReleaseStringChars(value, valStr);
+#endif
+
+ return true;
+}
+
+static jboolean putLong_native(JNIEnv * env, jobject object, jlong value, jint row, jint col)
+{
+ CursorWindow * window = GET_WINDOW(env, object);
+ if (!window->putLong(row, col, value)) {
+ LOG_WINDOW(" getFieldSlotWithCheck error ");
+ return false;
+ }
+
+ LOG_WINDOW("%d,%d is INTEGER 0x%016llx", row, col, value);
+
+ return true;
+}
+
+static jboolean putDouble_native(JNIEnv * env, jobject object, jdouble value, jint row, jint col)
+{
+ CursorWindow * window = GET_WINDOW(env, object);
+ if (!window->putDouble(row, col, value)) {
+ LOG_WINDOW(" getFieldSlotWithCheck error ");
+ return false;
+ }
+
+ LOG_WINDOW("%d,%d is FLOAT %lf", row, col, value);
+
+ return true;
+}
+
+static jboolean putNull_native(JNIEnv * env, jobject object, jint row, jint col)
+{
+ CursorWindow * window = GET_WINDOW(env, object);
+ if (!window->putNull(row, col)) {
+ LOG_WINDOW(" getFieldSlotWithCheck error ");
+ return false;
+ }
+
+ LOG_WINDOW("%d,%d is NULL", row, col);
+
+ return true;
+}
+
+// free the last row
+static void freeLastRow(JNIEnv * env, jobject object) {
+ CursorWindow * window = GET_WINDOW(env, object);
+ window->freeLastRow();
+}
+
+static JNINativeMethod sMethods[] =
+{
+ /* name, signature, funcPtr */
+ {"native_init", "(Z)V", (void *)native_init_empty},
+ {"native_init", "(Landroid/os/IBinder;)V", (void *)native_init_memory},
+ {"native_getBinder", "()Landroid/os/IBinder;", (void *)native_getBinder},
+ {"native_clear", "()V", (void *)native_clear},
+ {"close_native", "()V", (void *)native_close},
+ {"getLong_native", "(II)J", (void *)getLong_native},
+ {"getBlob_native", "(II)[B", (void *)getBlob_native},
+ {"isBlob_native", "(II)Z", (void *)isBlob_native},
+ {"getString_native", "(II)Ljava/lang/String;", (void *)getString_native},
+ {"copyStringToBuffer_native", "(IIILandroid/database/CharArrayBuffer;)[C", (void *)copyStringToBuffer_native},
+ {"getDouble_native", "(II)D", (void *)getDouble_native},
+ {"isNull_native", "(II)Z", (void *)isNull_native},
+ {"getNumRows_native", "()I", (void *)getNumRows},
+ {"setNumColumns_native", "(I)Z", (void *)setNumColumns},
+ {"allocRow_native", "()Z", (void *)allocRow},
+ {"putBlob_native", "([BII)Z", (void *)putBlob_native},
+ {"putString_native", "(Ljava/lang/String;II)Z", (void *)putString_native},
+ {"putLong_native", "(JII)Z", (void *)putLong_native},
+ {"putDouble_native", "(DII)Z", (void *)putDouble_native},
+ {"freeLastRow_native", "()V", (void *)freeLastRow},
+ {"putNull_native", "(II)Z", (void *)putNull_native},
+};
+
+int register_android_database_CursorWindow(JNIEnv * env)
+{
+ jclass clazz;
+
+ clazz = env->FindClass("android/database/CursorWindow");
+ if (clazz == NULL) {
+ LOGE("Can't find android/database/CursorWindow");
+ return -1;
+ }
+
+ gWindowField = env->GetFieldID(clazz, "nWindow", "I");
+
+ if (gWindowField == NULL) {
+ LOGE("Error locating fields");
+ return -1;
+ }
+
+ clazz = env->FindClass("android/database/CharArrayBuffer");
+ if (clazz == NULL) {
+ LOGE("Can't find android/database/CharArrayBuffer");
+ return -1;
+ }
+
+ gBufferField = env->GetFieldID(clazz, "data", "[C");
+
+ if (gBufferField == NULL) {
+ LOGE("Error locating fields data in CharArrayBuffer");
+ return -1;
+ }
+
+ gSizeCopiedField = env->GetFieldID(clazz, "sizeCopied", "I");
+
+ if (gSizeCopiedField == NULL) {
+ LOGE("Error locating fields sizeCopied in CharArrayBuffer");
+ return -1;
+ }
+
+ return AndroidRuntime::registerNativeMethods(env, "android/database/CursorWindow",
+ sMethods, NELEM(sMethods));
+}
+
+} // namespace android