| /* |
| * 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 or null 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 jboolean isString_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 string or null 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_STRING || field.type == FIELD_TYPE_NULL; |
| } |
| |
| static jboolean isInteger_native(JNIEnv* env, jobject object, jint row, jint column) |
| { |
| int32_t err; |
| CursorWindow * window = GET_WINDOW(env, object); |
| LOG_WINDOW("Checking if column is an integer 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_INTEGER; |
| } |
| |
| static jboolean isFloat_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 float 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_FLOAT; |
| } |
| |
| 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}, |
| {"isString_native", "(II)Z", (void *)isString_native}, |
| {"isFloat_native", "(II)Z", (void *)isFloat_native}, |
| {"isInteger_native", "(II)Z", (void *)isInteger_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 |