Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #undef LOG_TAG
     18 #define LOG_TAG "CursorWindow"
     19 
     20 #include <inttypes.h>
     21 #include <jni.h>
     22 #include <JNIHelp.h>
     23 #include <android_runtime/AndroidRuntime.h>
     24 
     25 #include <utils/Log.h>
     26 #include <utils/String8.h>
     27 #include <utils/String16.h>
     28 #include <utils/Unicode.h>
     29 
     30 #include <stdio.h>
     31 #include <string.h>
     32 #include <unistd.h>
     33 
     34 #include <androidfw/CursorWindow.h>
     35 #include "android_os_Parcel.h"
     36 #include "android_util_Binder.h"
     37 #include "android_database_SQLiteCommon.h"
     38 
     39 #include "core_jni_helpers.h"
     40 
     41 namespace android {
     42 
     43 static struct {
     44     jfieldID data;
     45     jfieldID sizeCopied;
     46 } gCharArrayBufferClassInfo;
     47 
     48 static jstring gEmptyString;
     49 
     50 static void throwExceptionWithRowCol(JNIEnv* env, jint row, jint column) {
     51     String8 msg;
     52     msg.appendFormat("Couldn't read row %d, col %d from CursorWindow.  "
     53             "Make sure the Cursor is initialized correctly before accessing data from it.",
     54             row, column);
     55     jniThrowException(env, "java/lang/IllegalStateException", msg.string());
     56 }
     57 
     58 static void throwUnknownTypeException(JNIEnv * env, jint type) {
     59     String8 msg;
     60     msg.appendFormat("UNKNOWN type %d", type);
     61     jniThrowException(env, "java/lang/IllegalStateException", msg.string());
     62 }
     63 
     64 static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring nameObj, jint cursorWindowSize) {
     65     String8 name;
     66     const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
     67     name.setTo(nameStr);
     68     env->ReleaseStringUTFChars(nameObj, nameStr);
     69 
     70     CursorWindow* window;
     71     status_t status = CursorWindow::create(name, cursorWindowSize, &window);
     72     if (status || !window) {
     73         ALOGE("Could not allocate CursorWindow '%s' of size %d due to error %d.",
     74                 name.string(), cursorWindowSize, status);
     75         return 0;
     76     }
     77 
     78     LOG_WINDOW("nativeInitializeEmpty: window = %p", window);
     79     return reinterpret_cast<jlong>(window);
     80 }
     81 
     82 static jlong nativeCreateFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) {
     83     Parcel* parcel = parcelForJavaObject(env, parcelObj);
     84 
     85     CursorWindow* window;
     86     status_t status = CursorWindow::createFromParcel(parcel, &window);
     87     if (status || !window) {
     88         ALOGE("Could not create CursorWindow from Parcel due to error %d.", status);
     89         return 0;
     90     }
     91 
     92     LOG_WINDOW("nativeInitializeFromBinder: numRows = %d, numColumns = %d, window = %p",
     93             window->getNumRows(), window->getNumColumns(), window);
     94     return reinterpret_cast<jlong>(window);
     95 }
     96 
     97 static void nativeDispose(JNIEnv* env, jclass clazz, jlong windowPtr) {
     98     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
     99     if (window) {
    100         LOG_WINDOW("Closing window %p", window);
    101         delete window;
    102     }
    103 }
    104 
    105 static jstring nativeGetName(JNIEnv* env, jclass clazz, jlong windowPtr) {
    106     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
    107     return env->NewStringUTF(window->name().string());
    108 }
    109 
    110 static void nativeWriteToParcel(JNIEnv * env, jclass clazz, jlong windowPtr,
    111         jobject parcelObj) {
    112     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
    113     Parcel* parcel = parcelForJavaObject(env, parcelObj);
    114 
    115     status_t status = window->writeToParcel(parcel);
    116     if (status) {
    117         String8 msg;
    118         msg.appendFormat("Could not write CursorWindow to Parcel due to error %d.", status);
    119         jniThrowRuntimeException(env, msg.string());
    120     }
    121 }
    122 
    123 static void nativeClear(JNIEnv * env, jclass clazz, jlong windowPtr) {
    124     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
    125     LOG_WINDOW("Clearing window %p", window);
    126     status_t status = window->clear();
    127     if (status) {
    128         LOG_WINDOW("Could not clear window. error=%d", status);
    129     }
    130 }
    131 
    132 static jint nativeGetNumRows(JNIEnv* env, jclass clazz, jlong windowPtr) {
    133     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
    134     return window->getNumRows();
    135 }
    136 
    137 static jboolean nativeSetNumColumns(JNIEnv* env, jclass clazz, jlong windowPtr,
    138         jint columnNum) {
    139     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
    140     status_t status = window->setNumColumns(columnNum);
    141     return status == OK;
    142 }
    143 
    144 static jboolean nativeAllocRow(JNIEnv* env, jclass clazz, jlong windowPtr) {
    145     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
    146     status_t status = window->allocRow();
    147     return status == OK;
    148 }
    149 
    150 static void nativeFreeLastRow(JNIEnv* env, jclass clazz, jlong windowPtr) {
    151     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
    152     window->freeLastRow();
    153 }
    154 
    155 static jint nativeGetType(JNIEnv* env, jclass clazz, jlong windowPtr,
    156         jint row, jint column) {
    157     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
    158     LOG_WINDOW("returning column type affinity for %d,%d from %p", row, column, window);
    159 
    160     CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
    161     if (!fieldSlot) {
    162         // FIXME: This is really broken but we have CTS tests that depend
    163         // on this legacy behavior.
    164         //throwExceptionWithRowCol(env, row, column);
    165         return CursorWindow::FIELD_TYPE_NULL;
    166     }
    167     return window->getFieldSlotType(fieldSlot);
    168 }
    169 
    170 static jbyteArray nativeGetBlob(JNIEnv* env, jclass clazz, jlong windowPtr,
    171         jint row, jint column) {
    172     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
    173     LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window);
    174 
    175     CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
    176     if (!fieldSlot) {
    177         throwExceptionWithRowCol(env, row, column);
    178         return NULL;
    179     }
    180 
    181     int32_t type = window->getFieldSlotType(fieldSlot);
    182     if (type == CursorWindow::FIELD_TYPE_BLOB || type == CursorWindow::FIELD_TYPE_STRING) {
    183         size_t size;
    184         const void* value = window->getFieldSlotValueBlob(fieldSlot, &size);
    185         jbyteArray byteArray = env->NewByteArray(size);
    186         if (!byteArray) {
    187             env->ExceptionClear();
    188             throw_sqlite3_exception(env, "Native could not create new byte[]");
    189             return NULL;
    190         }
    191         env->SetByteArrayRegion(byteArray, 0, size, static_cast<const jbyte*>(value));
    192         return byteArray;
    193     } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
    194         throw_sqlite3_exception(env, "INTEGER data in nativeGetBlob ");
    195     } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
    196         throw_sqlite3_exception(env, "FLOAT data in nativeGetBlob ");
    197     } else if (type == CursorWindow::FIELD_TYPE_NULL) {
    198         // do nothing
    199     } else {
    200         throwUnknownTypeException(env, type);
    201     }
    202     return NULL;
    203 }
    204 
    205 static jstring nativeGetString(JNIEnv* env, jclass clazz, jlong windowPtr,
    206         jint row, jint column) {
    207     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
    208     LOG_WINDOW("Getting string for %d,%d from %p", row, column, window);
    209 
    210     CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
    211     if (!fieldSlot) {
    212         throwExceptionWithRowCol(env, row, column);
    213         return NULL;
    214     }
    215 
    216     int32_t type = window->getFieldSlotType(fieldSlot);
    217     if (type == CursorWindow::FIELD_TYPE_STRING) {
    218         size_t sizeIncludingNull;
    219         const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
    220         if (sizeIncludingNull <= 1) {
    221             return gEmptyString;
    222         }
    223         // Convert to UTF-16 here instead of calling NewStringUTF.  NewStringUTF
    224         // doesn't like UTF-8 strings with high codepoints.  It actually expects
    225         // Modified UTF-8 with encoded surrogate pairs.
    226         String16 utf16(value, sizeIncludingNull - 1);
    227         return env->NewString(reinterpret_cast<const jchar*>(utf16.string()), utf16.size());
    228     } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
    229         int64_t value = window->getFieldSlotValueLong(fieldSlot);
    230         char buf[32];
    231         snprintf(buf, sizeof(buf), "%" PRId64, value);
    232         return env->NewStringUTF(buf);
    233     } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
    234         double value = window->getFieldSlotValueDouble(fieldSlot);
    235         char buf[32];
    236         snprintf(buf, sizeof(buf), "%g", value);
    237         return env->NewStringUTF(buf);
    238     } else if (type == CursorWindow::FIELD_TYPE_NULL) {
    239         return NULL;
    240     } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
    241         throw_sqlite3_exception(env, "Unable to convert BLOB to string");
    242         return NULL;
    243     } else {
    244         throwUnknownTypeException(env, type);
    245         return NULL;
    246     }
    247 }
    248 
    249 static jcharArray allocCharArrayBuffer(JNIEnv* env, jobject bufferObj, size_t size) {
    250     jcharArray dataObj = jcharArray(env->GetObjectField(bufferObj,
    251             gCharArrayBufferClassInfo.data));
    252     if (dataObj && size) {
    253         jsize capacity = env->GetArrayLength(dataObj);
    254         if (size_t(capacity) < size) {
    255             env->DeleteLocalRef(dataObj);
    256             dataObj = NULL;
    257         }
    258     }
    259     if (!dataObj) {
    260         jsize capacity = size;
    261         if (capacity < 64) {
    262             capacity = 64;
    263         }
    264         dataObj = env->NewCharArray(capacity); // might throw OOM
    265         if (dataObj) {
    266             env->SetObjectField(bufferObj, gCharArrayBufferClassInfo.data, dataObj);
    267         }
    268     }
    269     return dataObj;
    270 }
    271 
    272 static void fillCharArrayBufferUTF(JNIEnv* env, jobject bufferObj,
    273         const char* str, size_t len) {
    274     ssize_t size = utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str), len);
    275     if (size < 0) {
    276         size = 0; // invalid UTF8 string
    277     }
    278     jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, size);
    279     if (dataObj) {
    280         if (size) {
    281             jchar* data = static_cast<jchar*>(env->GetPrimitiveArrayCritical(dataObj, NULL));
    282             utf8_to_utf16_no_null_terminator(reinterpret_cast<const uint8_t*>(str), len,
    283                     reinterpret_cast<char16_t*>(data));
    284             env->ReleasePrimitiveArrayCritical(dataObj, data, 0);
    285         }
    286         env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, size);
    287     }
    288 }
    289 
    290 static void clearCharArrayBuffer(JNIEnv* env, jobject bufferObj) {
    291     jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, 0);
    292     if (dataObj) {
    293         env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, 0);
    294     }
    295 }
    296 
    297 static void nativeCopyStringToBuffer(JNIEnv* env, jclass clazz, jlong windowPtr,
    298         jint row, jint column, jobject bufferObj) {
    299     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
    300     LOG_WINDOW("Copying string for %d,%d from %p", row, column, window);
    301 
    302     CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
    303     if (!fieldSlot) {
    304         throwExceptionWithRowCol(env, row, column);
    305         return;
    306     }
    307 
    308     int32_t type = window->getFieldSlotType(fieldSlot);
    309     if (type == CursorWindow::FIELD_TYPE_STRING) {
    310         size_t sizeIncludingNull;
    311         const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
    312         if (sizeIncludingNull > 1) {
    313             fillCharArrayBufferUTF(env, bufferObj, value, sizeIncludingNull - 1);
    314         } else {
    315             clearCharArrayBuffer(env, bufferObj);
    316         }
    317     } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
    318         int64_t value = window->getFieldSlotValueLong(fieldSlot);
    319         char buf[32];
    320         snprintf(buf, sizeof(buf), "%" PRId64, value);
    321         fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf));
    322     } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
    323         double value = window->getFieldSlotValueDouble(fieldSlot);
    324         char buf[32];
    325         snprintf(buf, sizeof(buf), "%g", value);
    326         fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf));
    327     } else if (type == CursorWindow::FIELD_TYPE_NULL) {
    328         clearCharArrayBuffer(env, bufferObj);
    329     } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
    330         throw_sqlite3_exception(env, "Unable to convert BLOB to string");
    331     } else {
    332         throwUnknownTypeException(env, type);
    333     }
    334 }
    335 
    336 static jlong nativeGetLong(JNIEnv* env, jclass clazz, jlong windowPtr,
    337         jint row, jint column) {
    338     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
    339     LOG_WINDOW("Getting long for %d,%d from %p", row, column, window);
    340 
    341     CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
    342     if (!fieldSlot) {
    343         throwExceptionWithRowCol(env, row, column);
    344         return 0;
    345     }
    346 
    347     int32_t type = window->getFieldSlotType(fieldSlot);
    348     if (type == CursorWindow::FIELD_TYPE_INTEGER) {
    349         return window->getFieldSlotValueLong(fieldSlot);
    350     } else if (type == CursorWindow::FIELD_TYPE_STRING) {
    351         size_t sizeIncludingNull;
    352         const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
    353         return sizeIncludingNull > 1 ? strtoll(value, NULL, 0) : 0L;
    354     } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
    355         return jlong(window->getFieldSlotValueDouble(fieldSlot));
    356     } else if (type == CursorWindow::FIELD_TYPE_NULL) {
    357         return 0;
    358     } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
    359         throw_sqlite3_exception(env, "Unable to convert BLOB to long");
    360         return 0;
    361     } else {
    362         throwUnknownTypeException(env, type);
    363         return 0;
    364     }
    365 }
    366 
    367 static jdouble nativeGetDouble(JNIEnv* env, jclass clazz, jlong windowPtr,
    368         jint row, jint column) {
    369     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
    370     LOG_WINDOW("Getting double for %d,%d from %p", row, column, window);
    371 
    372     CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
    373     if (!fieldSlot) {
    374         throwExceptionWithRowCol(env, row, column);
    375         return 0.0;
    376     }
    377 
    378     int32_t type = window->getFieldSlotType(fieldSlot);
    379     if (type == CursorWindow::FIELD_TYPE_FLOAT) {
    380         return window->getFieldSlotValueDouble(fieldSlot);
    381     } else if (type == CursorWindow::FIELD_TYPE_STRING) {
    382         size_t sizeIncludingNull;
    383         const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
    384         return sizeIncludingNull > 1 ? strtod(value, NULL) : 0.0;
    385     } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
    386         return jdouble(window->getFieldSlotValueLong(fieldSlot));
    387     } else if (type == CursorWindow::FIELD_TYPE_NULL) {
    388         return 0.0;
    389     } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
    390         throw_sqlite3_exception(env, "Unable to convert BLOB to double");
    391         return 0.0;
    392     } else {
    393         throwUnknownTypeException(env, type);
    394         return 0.0;
    395     }
    396 }
    397 
    398 static jboolean nativePutBlob(JNIEnv* env, jclass clazz, jlong windowPtr,
    399         jbyteArray valueObj, jint row, jint column) {
    400     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
    401     jsize len = env->GetArrayLength(valueObj);
    402 
    403     void* value = env->GetPrimitiveArrayCritical(valueObj, NULL);
    404     status_t status = window->putBlob(row, column, value, len);
    405     env->ReleasePrimitiveArrayCritical(valueObj, value, JNI_ABORT);
    406 
    407     if (status) {
    408         LOG_WINDOW("Failed to put blob. error=%d", status);
    409         return false;
    410     }
    411 
    412     LOG_WINDOW("%d,%d is BLOB with %u bytes", row, column, len);
    413     return true;
    414 }
    415 
    416 static jboolean nativePutString(JNIEnv* env, jclass clazz, jlong windowPtr,
    417         jstring valueObj, jint row, jint column) {
    418     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
    419 
    420     size_t sizeIncludingNull = env->GetStringUTFLength(valueObj) + 1;
    421     const char* valueStr = env->GetStringUTFChars(valueObj, NULL);
    422     if (!valueStr) {
    423         LOG_WINDOW("value can't be transferred to UTFChars");
    424         return false;
    425     }
    426     status_t status = window->putString(row, column, valueStr, sizeIncludingNull);
    427     env->ReleaseStringUTFChars(valueObj, valueStr);
    428 
    429     if (status) {
    430         LOG_WINDOW("Failed to put string. error=%d", status);
    431         return false;
    432     }
    433 
    434     LOG_WINDOW("%d,%d is TEXT with %u bytes", row, column, sizeIncludingNull);
    435     return true;
    436 }
    437 
    438 static jboolean nativePutLong(JNIEnv* env, jclass clazz, jlong windowPtr,
    439         jlong value, jint row, jint column) {
    440     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
    441     status_t status = window->putLong(row, column, value);
    442 
    443     if (status) {
    444         LOG_WINDOW("Failed to put long. error=%d", status);
    445         return false;
    446     }
    447 
    448     LOG_WINDOW("%d,%d is INTEGER 0x%016llx", row, column, value);
    449     return true;
    450 }
    451 
    452 static jboolean nativePutDouble(JNIEnv* env, jclass clazz, jlong windowPtr,
    453         jdouble value, jint row, jint column) {
    454     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
    455     status_t status = window->putDouble(row, column, value);
    456 
    457     if (status) {
    458         LOG_WINDOW("Failed to put double. error=%d", status);
    459         return false;
    460     }
    461 
    462     LOG_WINDOW("%d,%d is FLOAT %lf", row, column, value);
    463     return true;
    464 }
    465 
    466 static jboolean nativePutNull(JNIEnv* env, jclass clazz, jlong windowPtr,
    467         jint row, jint column) {
    468     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
    469     status_t status = window->putNull(row, column);
    470 
    471     if (status) {
    472         LOG_WINDOW("Failed to put null. error=%d", status);
    473         return false;
    474     }
    475 
    476     LOG_WINDOW("%d,%d is NULL", row, column);
    477     return true;
    478 }
    479 
    480 static const JNINativeMethod sMethods[] =
    481 {
    482     /* name, signature, funcPtr */
    483     { "nativeCreate", "(Ljava/lang/String;I)J",
    484             (void*)nativeCreate },
    485     { "nativeCreateFromParcel", "(Landroid/os/Parcel;)J",
    486             (void*)nativeCreateFromParcel },
    487     { "nativeDispose", "(J)V",
    488             (void*)nativeDispose },
    489     { "nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
    490             (void*)nativeWriteToParcel },
    491     { "nativeGetName", "(J)Ljava/lang/String;",
    492             (void*)nativeGetName },
    493     { "nativeClear", "(J)V",
    494             (void*)nativeClear },
    495     { "nativeGetNumRows", "(J)I",
    496             (void*)nativeGetNumRows },
    497     { "nativeSetNumColumns", "(JI)Z",
    498             (void*)nativeSetNumColumns },
    499     { "nativeAllocRow", "(J)Z",
    500             (void*)nativeAllocRow },
    501     { "nativeFreeLastRow", "(J)V",
    502             (void*)nativeFreeLastRow },
    503     { "nativeGetType", "(JII)I",
    504             (void*)nativeGetType },
    505     { "nativeGetBlob", "(JII)[B",
    506             (void*)nativeGetBlob },
    507     { "nativeGetString", "(JII)Ljava/lang/String;",
    508             (void*)nativeGetString },
    509     { "nativeGetLong", "(JII)J",
    510             (void*)nativeGetLong },
    511     { "nativeGetDouble", "(JII)D",
    512             (void*)nativeGetDouble },
    513     { "nativeCopyStringToBuffer", "(JIILandroid/database/CharArrayBuffer;)V",
    514             (void*)nativeCopyStringToBuffer },
    515     { "nativePutBlob", "(J[BII)Z",
    516             (void*)nativePutBlob },
    517     { "nativePutString", "(JLjava/lang/String;II)Z",
    518             (void*)nativePutString },
    519     { "nativePutLong", "(JJII)Z",
    520             (void*)nativePutLong },
    521     { "nativePutDouble", "(JDII)Z",
    522             (void*)nativePutDouble },
    523     { "nativePutNull", "(JII)Z",
    524             (void*)nativePutNull },
    525 };
    526 
    527 int register_android_database_CursorWindow(JNIEnv* env)
    528 {
    529     jclass clazz = FindClassOrDie(env, "android/database/CharArrayBuffer");
    530 
    531     gCharArrayBufferClassInfo.data = GetFieldIDOrDie(env, clazz, "data", "[C");
    532     gCharArrayBufferClassInfo.sizeCopied = GetFieldIDOrDie(env, clazz, "sizeCopied", "I");
    533 
    534     gEmptyString = MakeGlobalRefOrDie(env, env->NewStringUTF(""));
    535 
    536     return RegisterMethodsOrDie(env, "android/database/CursorWindow", sMethods, NELEM(sMethods));
    537 }
    538 
    539 } // namespace android
    540