| /* |
| * 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. |
| */ |
| |
| // Derived from |
| // https://cs.android.com/android/platform/superproject/+/android-11.0.0_r1:frameworks/base/core/jni/android_database_CursorWindow.cpp |
| |
| #undef LOG_TAG |
| #define LOG_TAG "CursorWindow" |
| #define LOG_NDEBUG 0 |
| |
| #include <dirent.h> |
| #include <inttypes.h> |
| #include <jni.h> |
| #include <log/log.h> |
| #include <nativehelper/JNIHelp.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <utils/String16.h> |
| #include <utils/String8.h> |
| #include <utils/Unicode.h> |
| |
| #undef LOG_NDEBUG |
| #define LOG_NDEBUG 1 |
| |
| #include <androidfw/CursorWindow.h> |
| // #include "android_os_Parcel.h" |
| // #include "android_util_Binder.h" |
| #include <nativehelper/scoped_local_ref.h> |
| |
| #include "robo_android_database_SQLiteCommon.h" |
| |
| namespace android { |
| |
| // static struct { |
| // jfieldID data; |
| // jfieldID sizeCopied; |
| // } gCharArrayBufferClassInfo; |
| |
| static jfieldID getCharArrayBufferDataFieldId(JNIEnv* env, jobject obj) { |
| jclass clsObj = env->GetObjectClass(obj); |
| if (clsObj == nullptr) { |
| printf("cls obj is null"); |
| } |
| return env->GetFieldID(clsObj, "data", "[C"); |
| } |
| |
| static jfieldID getCharArrayBufferSizeCopiedFieldId(JNIEnv* env, jobject obj) { |
| jclass clsObj = env->GetObjectClass(obj); |
| if (clsObj == nullptr) { |
| printf("cls obj is null"); |
| } |
| return env->GetFieldID(clsObj, "sizeCopied", "I"); |
| } |
| |
| static jstring gEmptyString; |
| |
| static void throwExceptionWithRowCol(JNIEnv* env, jint row, jint column) { |
| String8 msg; |
| msg.appendFormat( |
| "Couldn't read row %d, col %d from CursorWindow. " |
| "Make sure the Cursor is initialized correctly before accessing data " |
| "from it.", |
| row, column); |
| jniThrowException(env, "java/lang/IllegalStateException", msg.string()); |
| } |
| |
| static void throwUnknownTypeException(JNIEnv* env, jint type) { |
| String8 msg; |
| msg.appendFormat("UNKNOWN type %d", type); |
| jniThrowException(env, "java/lang/IllegalStateException", msg.string()); |
| } |
| |
| // static int getFdCount() { |
| // char fdpath[PATH_MAX]; |
| // int count = 0; |
| // snprintf(fdpath, PATH_MAX, "/proc/%d/fd", getpid()); |
| // DIR* dir = opendir(fdpath); |
| // if (dir != NULL) { |
| // struct dirent* dirent; |
| // while ((dirent = readdir(dir))) { |
| // count++; |
| // } |
| // count -= 2; // discount "." and ".." |
| // closedir(dir); |
| // } |
| // return count; |
| // } |
| |
| static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring nameObj, |
| jint cursorWindowSize) { |
| String8 name; |
| const char* nameStr = env->GetStringUTFChars(nameObj, nullptr); |
| name.setTo(nameStr); |
| env->ReleaseStringUTFChars(nameObj, nameStr); |
| |
| CursorWindow* window; |
| status_t status = CursorWindow::create(name, cursorWindowSize, &window); |
| if (status || !window) { |
| jniThrowExceptionFmt( |
| env, "android/database/CursorWindowAllocationException", |
| "Could not allocate CursorWindow '%s' of size %d due to error %d.", |
| name.string(), cursorWindowSize, status); |
| return 0; |
| } |
| |
| LOG_WINDOW("nativeInitializeEmpty: window = %p", window); |
| return reinterpret_cast<jlong>(window); |
| } |
| |
| // static jlong nativeCreateFromParcel(JNIEnv* env, jclass clazz, jobject |
| // parcelObj) { |
| // Parcel* parcel = parcelForJavaObject(env, parcelObj); |
| // |
| // CursorWindow* window; |
| // status_t status = CursorWindow::createFromParcel(parcel, &window); |
| // if (status || !window) { |
| // jniThrowExceptionFmt(env, |
| // "android/database/CursorWindowAllocationException", |
| // "Could not create CursorWindow from Parcel due to |
| // error %d, process fd count=%d", status, |
| // getFdCount()); |
| // return 0; |
| // } |
| // |
| // LOG_WINDOW("nativeInitializeFromBinder: numRows = %d, numColumns = %d, |
| // window = %p", |
| // window->getNumRows(), window->getNumColumns(), window); |
| // return reinterpret_cast<jlong>(window); |
| // } |
| |
| static void nativeDispose(JNIEnv* env, jclass clazz, jlong windowPtr) { |
| CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); |
| if (window) { |
| LOG_WINDOW("Closing window %p", window); |
| delete window; |
| } |
| } |
| |
| static jstring nativeGetName(JNIEnv* env, jclass clazz, jlong windowPtr) { |
| CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); |
| return env->NewStringUTF(window->name().string()); |
| } |
| // |
| // static void nativeWriteToParcel(JNIEnv * env, jclass clazz, jlong windowPtr, |
| // jobject parcelObj) { |
| // CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); |
| // Parcel* parcel = parcelForJavaObject(env, parcelObj); |
| // |
| // status_t status = window->writeToParcel(parcel); |
| // if (status) { |
| // String8 msg; |
| // msg.appendFormat("Could not write CursorWindow to Parcel due to error |
| // %d.", status); jniThrowRuntimeException(env, msg.string()); |
| // } |
| //} |
| |
| static void nativeClear(JNIEnv* env, jclass clazz, jlong windowPtr) { |
| CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); |
| LOG_WINDOW("Clearing window %p", window); |
| status_t status = window->clear(); |
| if (status) { |
| LOG_WINDOW("Could not clear window. error=%d", status); |
| } |
| } |
| |
| static jint nativeGetNumRows(JNIEnv* env, jclass clazz, jlong windowPtr) { |
| CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); |
| return window->getNumRows(); |
| } |
| |
| static jboolean nativeSetNumColumns(JNIEnv* env, jclass clazz, jlong windowPtr, |
| jint columnNum) { |
| CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); |
| status_t status = window->setNumColumns(columnNum); |
| return status == OK; |
| } |
| |
| static jboolean nativeAllocRow(JNIEnv* env, jclass clazz, jlong windowPtr) { |
| CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); |
| status_t status = window->allocRow(); |
| return status == OK; |
| } |
| |
| static void nativeFreeLastRow(JNIEnv* env, jclass clazz, jlong windowPtr) { |
| CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); |
| window->freeLastRow(); |
| } |
| |
| static jint nativeGetType(JNIEnv* env, jclass clazz, jlong windowPtr, jint row, |
| jint column) { |
| CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); |
| LOG_WINDOW("returning column type affinity for %d,%d from %p", row, column, |
| window); |
| |
| CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); |
| if (!fieldSlot) { |
| // FIXME: This is really broken but we have CTS tests that depend |
| // on this legacy behavior. |
| // throwExceptionWithRowCol(env, row, column); |
| return CursorWindow::FIELD_TYPE_NULL; |
| } |
| return window->getFieldSlotType(fieldSlot); |
| } |
| |
| static jbyteArray nativeGetBlob(JNIEnv* env, jclass clazz, jlong windowPtr, |
| jint row, jint column) { |
| CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); |
| LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window); |
| |
| CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); |
| if (!fieldSlot) { |
| throwExceptionWithRowCol(env, row, column); |
| return nullptr; |
| } |
| |
| int32_t type = window->getFieldSlotType(fieldSlot); |
| if (type == CursorWindow::FIELD_TYPE_BLOB || |
| type == CursorWindow::FIELD_TYPE_STRING) { |
| size_t size; |
| const void* value = window->getFieldSlotValueBlob(fieldSlot, &size); |
| if (!value) { |
| throw_sqlite3_exception(env, "Native could not read blob slot"); |
| return nullptr; |
| } |
| jbyteArray byteArray = env->NewByteArray(size); |
| if (!byteArray) { |
| env->ExceptionClear(); |
| throw_sqlite3_exception(env, "Native could not create new byte[]"); |
| return nullptr; |
| } |
| env->SetByteArrayRegion(byteArray, 0, size, |
| static_cast<const jbyte*>(value)); |
| return byteArray; |
| } else if (type == CursorWindow::FIELD_TYPE_INTEGER) { |
| throw_sqlite3_exception(env, "INTEGER data in nativeGetBlob "); |
| } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { |
| throw_sqlite3_exception(env, "FLOAT data in nativeGetBlob "); |
| } else if (type == CursorWindow::FIELD_TYPE_NULL) { |
| // do nothing |
| } else { |
| throwUnknownTypeException(env, type); |
| } |
| return nullptr; |
| } |
| |
| static jstring nativeGetString(JNIEnv* env, jclass clazz, jlong windowPtr, |
| jint row, jint column) { |
| CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); |
| LOG_WINDOW("Getting string for %d,%d from %p", row, column, window); |
| |
| CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); |
| if (!fieldSlot) { |
| throwExceptionWithRowCol(env, row, column); |
| return nullptr; |
| } |
| |
| int32_t type = window->getFieldSlotType(fieldSlot); |
| if (type == CursorWindow::FIELD_TYPE_STRING) { |
| size_t sizeIncludingNull; |
| const char* value = |
| window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull); |
| if (!value) { |
| throw_sqlite3_exception(env, "Native could not read string slot"); |
| return nullptr; |
| } |
| if (sizeIncludingNull <= 1) { |
| return gEmptyString; |
| } |
| // Convert to UTF-16 here instead of calling NewStringUTF. NewStringUTF |
| // doesn't like UTF-8 strings with high codepoints. It actually expects |
| // Modified UTF-8 with encoded surrogate pairs. |
| String16 utf16(value, sizeIncludingNull - 1); |
| return env->NewString(reinterpret_cast<const jchar*>(utf16.string()), |
| utf16.size()); |
| } else if (type == CursorWindow::FIELD_TYPE_INTEGER) { |
| int64_t value = window->getFieldSlotValueLong(fieldSlot); |
| char buf[32]; |
| snprintf(buf, sizeof(buf), "%" PRId64, value); |
| return env->NewStringUTF(buf); |
| } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { |
| double value = window->getFieldSlotValueDouble(fieldSlot); |
| char buf[32]; |
| snprintf(buf, sizeof(buf), "%g", value); |
| return env->NewStringUTF(buf); |
| } else if (type == CursorWindow::FIELD_TYPE_NULL) { |
| return nullptr; |
| } else if (type == CursorWindow::FIELD_TYPE_BLOB) { |
| throw_sqlite3_exception(env, "Unable to convert BLOB to string"); |
| return nullptr; |
| } else { |
| throwUnknownTypeException(env, type); |
| return nullptr; |
| } |
| } |
| |
| static jcharArray allocCharArrayBuffer(JNIEnv* env, jobject bufferObj, |
| size_t size) { |
| jcharArray dataObj = jcharArray(env->GetObjectField( |
| bufferObj, getCharArrayBufferDataFieldId(env, bufferObj))); |
| if (dataObj && size) { |
| jsize capacity = env->GetArrayLength(dataObj); |
| if (size_t(capacity) < size) { |
| env->DeleteLocalRef(dataObj); |
| dataObj = nullptr; |
| } |
| } |
| if (!dataObj) { |
| jsize capacity = size; |
| if (capacity < 64) { |
| capacity = 64; |
| } |
| dataObj = env->NewCharArray(capacity); // might throw OOM |
| if (dataObj) { |
| env->SetObjectField( |
| bufferObj, getCharArrayBufferDataFieldId(env, bufferObj), dataObj); |
| } |
| } |
| return dataObj; |
| } |
| |
| static void fillCharArrayBufferUTF(JNIEnv* env, jobject bufferObj, |
| const char* str, size_t len) { |
| ssize_t size = |
| utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str), len); |
| if (size < 0) { |
| size = 0; // invalid UTF8 string |
| } |
| jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, size); |
| if (dataObj) { |
| if (size) { |
| jchar* data = |
| static_cast<jchar*>(env->GetPrimitiveArrayCritical(dataObj, nullptr)); |
| utf8_to_utf16_no_null_terminator(reinterpret_cast<const uint8_t*>(str), |
| len, reinterpret_cast<char16_t*>(data), |
| static_cast<size_t>(size)); |
| env->ReleasePrimitiveArrayCritical(dataObj, data, 0); |
| } |
| env->SetIntField(bufferObj, |
| getCharArrayBufferSizeCopiedFieldId(env, bufferObj), size); |
| } |
| } |
| |
| static void clearCharArrayBuffer(JNIEnv* env, jobject bufferObj) { |
| jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, 0); |
| if (dataObj) { |
| env->SetIntField(bufferObj, |
| getCharArrayBufferSizeCopiedFieldId(env, bufferObj), 0); |
| } |
| } |
| |
| static void nativeCopyStringToBuffer(JNIEnv* env, jclass clazz, jlong windowPtr, |
| jint row, jint column, jobject bufferObj) { |
| CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); |
| LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); |
| |
| CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); |
| if (!fieldSlot) { |
| throwExceptionWithRowCol(env, row, column); |
| return; |
| } |
| |
| int32_t type = window->getFieldSlotType(fieldSlot); |
| if (type == CursorWindow::FIELD_TYPE_STRING) { |
| size_t sizeIncludingNull; |
| const char* value = |
| window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull); |
| if (sizeIncludingNull > 1) { |
| fillCharArrayBufferUTF(env, bufferObj, value, sizeIncludingNull - 1); |
| } else { |
| clearCharArrayBuffer(env, bufferObj); |
| } |
| } else if (type == CursorWindow::FIELD_TYPE_INTEGER) { |
| int64_t value = window->getFieldSlotValueLong(fieldSlot); |
| char buf[32]; |
| snprintf(buf, sizeof(buf), "%" PRId64, value); |
| fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf)); |
| } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { |
| double value = window->getFieldSlotValueDouble(fieldSlot); |
| char buf[32]; |
| snprintf(buf, sizeof(buf), "%g", value); |
| fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf)); |
| } else if (type == CursorWindow::FIELD_TYPE_NULL) { |
| clearCharArrayBuffer(env, bufferObj); |
| } else if (type == CursorWindow::FIELD_TYPE_BLOB) { |
| throw_sqlite3_exception(env, "Unable to convert BLOB to string"); |
| } else { |
| throwUnknownTypeException(env, type); |
| } |
| } |
| |
| static jlong nativeGetLong(JNIEnv* env, jclass clazz, jlong windowPtr, jint row, |
| jint column) { |
| CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); |
| LOG_WINDOW("Getting long for %d,%d from %p", row, column, window); |
| |
| CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); |
| if (!fieldSlot) { |
| throwExceptionWithRowCol(env, row, column); |
| return 0; |
| } |
| |
| int32_t type = window->getFieldSlotType(fieldSlot); |
| if (type == CursorWindow::FIELD_TYPE_INTEGER) { |
| return window->getFieldSlotValueLong(fieldSlot); |
| } else if (type == CursorWindow::FIELD_TYPE_STRING) { |
| size_t sizeIncludingNull; |
| const char* value = |
| window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull); |
| return sizeIncludingNull > 1 ? strtoll(value, nullptr, 0) : 0L; |
| } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { |
| return jlong(window->getFieldSlotValueDouble(fieldSlot)); |
| } else if (type == CursorWindow::FIELD_TYPE_NULL) { |
| return 0; |
| } else if (type == CursorWindow::FIELD_TYPE_BLOB) { |
| throw_sqlite3_exception(env, "Unable to convert BLOB to long"); |
| return 0; |
| } else { |
| throwUnknownTypeException(env, type); |
| return 0; |
| } |
| } |
| |
| static jdouble nativeGetDouble(JNIEnv* env, jclass clazz, jlong windowPtr, |
| jint row, jint column) { |
| CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); |
| LOG_WINDOW("Getting double for %d,%d from %p", row, column, window); |
| |
| CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); |
| if (!fieldSlot) { |
| throwExceptionWithRowCol(env, row, column); |
| return 0.0; |
| } |
| |
| int32_t type = window->getFieldSlotType(fieldSlot); |
| if (type == CursorWindow::FIELD_TYPE_FLOAT) { |
| return window->getFieldSlotValueDouble(fieldSlot); |
| } else if (type == CursorWindow::FIELD_TYPE_STRING) { |
| size_t sizeIncludingNull; |
| const char* value = |
| window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull); |
| return sizeIncludingNull > 1 ? strtod(value, nullptr) : 0.0; |
| } else if (type == CursorWindow::FIELD_TYPE_INTEGER) { |
| return jdouble(window->getFieldSlotValueLong(fieldSlot)); |
| } else if (type == CursorWindow::FIELD_TYPE_NULL) { |
| return 0.0; |
| } else if (type == CursorWindow::FIELD_TYPE_BLOB) { |
| throw_sqlite3_exception(env, "Unable to convert BLOB to double"); |
| return 0.0; |
| } else { |
| throwUnknownTypeException(env, type); |
| return 0.0; |
| } |
| } |
| |
| static jboolean nativePutBlob(JNIEnv* env, jclass clazz, jlong windowPtr, |
| jbyteArray valueObj, jint row, jint column) { |
| CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); |
| jsize len = env->GetArrayLength(valueObj); |
| |
| void* value = env->GetPrimitiveArrayCritical(valueObj, nullptr); |
| status_t status = window->putBlob(row, column, value, len); |
| env->ReleasePrimitiveArrayCritical(valueObj, value, JNI_ABORT); |
| |
| if (status) { |
| LOG_WINDOW("Failed to put blob. error=%d", status); |
| return false; |
| } |
| |
| LOG_WINDOW("%d,%d is BLOB with %u bytes", row, column, len); |
| return true; |
| } |
| |
| static jboolean nativePutString(JNIEnv* env, jclass clazz, jlong windowPtr, |
| jstring valueObj, jint row, jint column) { |
| CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); |
| |
| size_t sizeIncludingNull = env->GetStringUTFLength(valueObj) + 1; |
| const char* valueStr = env->GetStringUTFChars(valueObj, nullptr); |
| if (!valueStr) { |
| LOG_WINDOW("value can't be transferred to UTFChars"); |
| return false; |
| } |
| status_t status = window->putString(row, column, valueStr, sizeIncludingNull); |
| env->ReleaseStringUTFChars(valueObj, valueStr); |
| |
| if (status) { |
| LOG_WINDOW("Failed to put string. error=%d", status); |
| return false; |
| } |
| |
| LOG_WINDOW("%d,%d is TEXT with %zu bytes", row, column, sizeIncludingNull); |
| return true; |
| } |
| |
| static jboolean nativePutLong(JNIEnv* env, jclass clazz, jlong windowPtr, |
| jlong value, jint row, jint column) { |
| CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); |
| status_t status = window->putLong(row, column, value); |
| |
| if (status) { |
| LOG_WINDOW("Failed to put long. error=%d", status); |
| return false; |
| } |
| |
| LOG_WINDOW("%d,%d is INTEGER %" PRId64, row, column, value); |
| return true; |
| } |
| |
| static jboolean nativePutDouble(JNIEnv* env, jclass clazz, jlong windowPtr, |
| jdouble value, jint row, jint column) { |
| CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); |
| status_t status = window->putDouble(row, column, value); |
| |
| if (status) { |
| LOG_WINDOW("Failed to put double. error=%d", status); |
| return false; |
| } |
| |
| LOG_WINDOW("%d,%d is FLOAT %lf", row, column, value); |
| return true; |
| } |
| |
| static jboolean nativePutNull(JNIEnv* env, jclass clazz, jlong windowPtr, |
| jint row, jint column) { |
| CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); |
| status_t status = window->putNull(row, column); |
| |
| if (status) { |
| LOG_WINDOW("Failed to put null. error=%d", status); |
| return false; |
| } |
| |
| LOG_WINDOW("%d,%d is NULL", row, column); |
| return true; |
| } |
| |
| static const JNINativeMethod sMethods[] = { |
| /* name, signature, funcPtr */ |
| {(char*)"nativeCreate", (char*)"(Ljava/lang/String;I)J", |
| reinterpret_cast<void*>(nativeCreate)}, |
| // { "nativeCreateFromParcel", "(Landroid/os/Parcel;)J", |
| // (void*)nativeCreateFromParcel }, |
| {(char*)"nativeDispose", (char*)"(J)V", |
| reinterpret_cast<void*>(nativeDispose)}, |
| // { "nativeWriteToParcel", "(JLandroid/os/Parcel;)V", |
| // (void*)nativeWriteToParcel }, |
| |
| {const_cast<char*>("nativeGetName"), |
| const_cast<char*>("(J)Ljava/lang/String;"), |
| reinterpret_cast<void*>(nativeGetName)}, |
| {const_cast<char*>("nativeGetBlob"), const_cast<char*>("(JII)[B"), |
| reinterpret_cast<void*>(nativeGetBlob)}, |
| {const_cast<char*>("nativeGetString"), |
| const_cast<char*>("(JII)Ljava/lang/String;"), |
| reinterpret_cast<void*>(nativeGetString)}, |
| {const_cast<char*>("nativeCopyStringToBuffer"), |
| const_cast<char*>("(JIILandroid/database/CharArrayBuffer;)V"), |
| reinterpret_cast<void*>(nativeCopyStringToBuffer)}, |
| {const_cast<char*>("nativePutBlob"), const_cast<char*>("(J[BII)Z"), |
| reinterpret_cast<void*>(nativePutBlob)}, |
| {const_cast<char*>("nativePutString"), |
| const_cast<char*>("(JLjava/lang/String;II)Z"), |
| reinterpret_cast<void*>(nativePutString)}, |
| |
| // ------- @FastNative below here ---------------------- |
| {const_cast<char*>("nativeClear"), const_cast<char*>("(J)V"), |
| reinterpret_cast<void*>(nativeClear)}, |
| {const_cast<char*>("nativeGetNumRows"), const_cast<char*>("(J)I"), |
| reinterpret_cast<void*>(nativeGetNumRows)}, |
| {const_cast<char*>("nativeSetNumColumns"), const_cast<char*>("(JI)Z"), |
| reinterpret_cast<void*>(nativeSetNumColumns)}, |
| {const_cast<char*>("nativeAllocRow"), const_cast<char*>("(J)Z"), |
| reinterpret_cast<void*>(nativeAllocRow)}, |
| {const_cast<char*>("nativeFreeLastRow"), const_cast<char*>("(J)V"), |
| reinterpret_cast<void*>(nativeFreeLastRow)}, |
| {const_cast<char*>("nativeGetType"), const_cast<char*>("(JII)I"), |
| reinterpret_cast<void*>(nativeGetType)}, |
| {const_cast<char*>("nativeGetLong"), const_cast<char*>("(JII)J"), |
| reinterpret_cast<void*>(nativeGetLong)}, |
| {const_cast<char*>("nativeGetDouble"), const_cast<char*>("(JII)D"), |
| reinterpret_cast<void*>(nativeGetDouble)}, |
| {const_cast<char*>("nativePutLong"), const_cast<char*>("(JJII)Z"), |
| reinterpret_cast<void*>(nativePutLong)}, |
| {const_cast<char*>("nativePutDouble"), const_cast<char*>("(JDII)Z"), |
| reinterpret_cast<void*>(nativePutDouble)}, |
| {const_cast<char*>("nativePutNull"), const_cast<char*>("(JII)Z"), |
| reinterpret_cast<void*>(nativePutNull)}, |
| }; |
| |
| int register_android_database_CursorWindow(JNIEnv* env) { |
| gEmptyString = (jstring)env->NewGlobalRef(env->NewStringUTF("")); |
| |
| static const char* kCursorWindowClass = |
| "org/robolectric/nativeruntime/CursorWindowNatives"; |
| |
| ScopedLocalRef<jclass> cls(env, env->FindClass(kCursorWindowClass)); |
| |
| if (cls.get() == nullptr) { |
| ALOGE("jni CursorWindow registration failure, class not found '%s'", |
| kCursorWindowClass); |
| return JNI_ERR; |
| } |
| |
| const jint count = sizeof(sMethods) / sizeof(sMethods[0]); |
| int status = env->RegisterNatives(cls.get(), sMethods, count); |
| if (status < 0) { |
| ALOGE("jni CursorWindow registration failure, status: %d", status); |
| return JNI_ERR; |
| } |
| return JNI_VERSION_1_4; |
| } |
| |
| } // namespace android |