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 <jni.h>
21#include <JNIHelp.h>
22#include <android_runtime/AndroidRuntime.h>
23
24#include <utils/Log.h>
25#include <utils/String8.h>
26#include <utils/String16.h>
27
28#include <stdio.h>
29#include <string.h>
30#include <unistd.h>
31
32#include "CursorWindow.h"
33#include "sqlite3_exception.h"
34#include "android_util_Binder.h"
35
36
37namespace android {
38
39static jfieldID gWindowField;
40static jfieldID gBufferField;
41static jfieldID gSizeCopiedField;
42
43#define GET_WINDOW(env, object) ((CursorWindow *)env->GetIntField(object, gWindowField))
44#define SET_WINDOW(env, object, window) (env->SetIntField(object, gWindowField, (int)window))
45#define SET_BUFFER(env, object, buf) (env->SetObjectField(object, gBufferField, buf))
46#define SET_SIZE_COPIED(env, object, size) (env->SetIntField(object, gSizeCopiedField, size))
47
48CursorWindow * get_window_from_object(JNIEnv * env, jobject javaWindow)
49{
50    return GET_WINDOW(env, javaWindow);
51}
52
53static void native_init_empty(JNIEnv * env, jobject object, jboolean localOnly)
54{
55    uint8_t * data;
56    size_t size;
57    CursorWindow * window;
58
59    window = new CursorWindow(MAX_WINDOW_SIZE);
60    if (!window) {
61        jniThrowException(env, "java/lang/RuntimeException", "No memory for native window object");
62        return;
63    }
64
65    if (!window->initBuffer(localOnly)) {
66        jniThrowException(env, "java/lang/IllegalStateException", "Couldn't init cursor window");
67        delete window;
68        return;
69    }
70
71LOG_WINDOW("native_init_empty: window = %p", window);
72    SET_WINDOW(env, object, window);
73}
74
75static void native_init_memory(JNIEnv * env, jobject object, jobject memObj)
76{
77    sp<IMemory> memory = interface_cast<IMemory>(ibinderForJavaObject(env, memObj));
78    if (memory == NULL) {
79        jniThrowException(env, "java/lang/IllegalStateException", "Couldn't get native binder");
80        return;
81    }
82
83    CursorWindow * window = new CursorWindow();
84    if (!window) {
85        jniThrowException(env, "java/lang/RuntimeException", "No memory for native window object");
86        return;
87    }
88    if (!window->setMemory(memory)) {
89        jniThrowException(env, "java/lang/RuntimeException", "No memory in memObj");
90        delete window;
91        return;
92    }
93
94LOG_WINDOW("native_init_memory: numRows = %d, numColumns = %d, window = %p", window->getNumRows(), window->getNumColumns(), window);
95    SET_WINDOW(env, object, window);
96}
97
98static jobject native_getBinder(JNIEnv * env, jobject object)
99{
100    CursorWindow * window = GET_WINDOW(env, object);
101    if (window) {
102        sp<IMemory> memory = window->getMemory();
103        if (memory != NULL) {
104            sp<IBinder> binder = memory->asBinder();
105            return javaObjectForIBinder(env, binder);
106        }
107    }
108    return NULL;
109}
110
111static void native_clear(JNIEnv * env, jobject object)
112{
113    CursorWindow * window = GET_WINDOW(env, object);
114LOG_WINDOW("Clearing window %p", window);
115    if (window == NULL) {
116        jniThrowException(env, "java/lang/IllegalStateException", "clear() called after close()");
117        return;
118    }
119    window->clear();
120}
121
122static void native_close(JNIEnv * env, jobject object)
123{
124    CursorWindow * window = GET_WINDOW(env, object);
125    if (window) {
126LOG_WINDOW("Closing window %p", window);
127        delete window;
128        SET_WINDOW(env, object, 0);
129    }
130}
131
132static void throwExceptionWithRowCol(JNIEnv * env, jint row, jint column)
133{
134    char buf[100];
135    snprintf(buf, sizeof(buf), "get field slot from row %d col %d failed", row, column);
136    jniThrowException(env, "java/lang/IllegalStateException", buf);
137}
138
139static void throwUnknowTypeException(JNIEnv * env, jint type)
140{
141    char buf[80];
142    snprintf(buf, sizeof(buf), "UNKNOWN type %d", type);
143    jniThrowException(env, "java/lang/IllegalStateException", buf);
144}
145
146static jlong getLong_native(JNIEnv * env, jobject object, jint row, jint column)
147{
148    int32_t err;
149    CursorWindow * window = GET_WINDOW(env, object);
150LOG_WINDOW("Getting long for %d,%d from %p", row, column, window);
151
152    field_slot_t field;
153    err = window->read_field_slot(row, column, &field);
154    if (err != 0) {
155        throwExceptionWithRowCol(env, row, column);
156        return 0;
157    }
158
159    uint8_t type = field.type;
160    if (type == FIELD_TYPE_INTEGER) {
161        int64_t value;
162        if (window->getLong(row, column, &value)) {
163            return value;
164        }
165        return 0;
166    } else if (type == FIELD_TYPE_STRING) {
167        uint32_t size = field.data.buffer.size;
168        if (size > 0) {
169#if WINDOW_STORAGE_UTF8
170            return strtoll((char const *)window->offsetToPtr(field.data.buffer.offset), NULL, 0);
171#else
172            String8 ascii((char16_t *) window->offsetToPtr(field.data.buffer.offset), size / 2);
173            char const * str = ascii.string();
174            return strtoll(str, NULL, 0);
175#endif
176        } else {
177            return 0;
178        }
179    } else if (type == FIELD_TYPE_FLOAT) {
180        double value;
181        if (window->getDouble(row, column, &value)) {
182            return value;
183        }
184        return 0;
185    } else if (type == FIELD_TYPE_NULL) {
186        return 0;
187    } else if (type == FIELD_TYPE_BLOB) {
188        throw_sqlite3_exception(env, "Unable to convert BLOB to long");
189        return 0;
190    } else {
191        throwUnknowTypeException(env, type);
192        return 0;
193    }
194}
195
196static jbyteArray getBlob_native(JNIEnv* env, jobject object, jint row, jint column)
197{
198    int32_t err;
199    CursorWindow * window = GET_WINDOW(env, object);
200LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window);
201
202    field_slot_t field;
203    err = window->read_field_slot(row, column, &field);
204    if (err != 0) {
205        throwExceptionWithRowCol(env, row, column);
206        return NULL;
207    }
208
209    uint8_t type = field.type;
210    if (type == FIELD_TYPE_BLOB || type == FIELD_TYPE_STRING) {
211        jbyteArray byteArray = env->NewByteArray(field.data.buffer.size);
212        LOG_ASSERT(byteArray, "Native could not create new byte[]");
213        env->SetByteArrayRegion(byteArray, 0, field.data.buffer.size,
214            (const jbyte*)window->offsetToPtr(field.data.buffer.offset));
215        return byteArray;
216    } else if (type == FIELD_TYPE_INTEGER) {
217        throw_sqlite3_exception(env, "INTEGER data in getBlob_native ");
218    } else if (type == FIELD_TYPE_FLOAT) {
219        throw_sqlite3_exception(env, "FLOAT data in getBlob_native ");
220    } else if (type == FIELD_TYPE_NULL) {
221        // do nothing
222    } else {
223        throwUnknowTypeException(env, type);
224    }
225    return NULL;
226}
227
228static jboolean isBlob_native(JNIEnv* env, jobject object, jint row, jint column)
229{
230    int32_t err;
231    CursorWindow * window = GET_WINDOW(env, object);
232LOG_WINDOW("Checking if column is a blob or null for %d,%d from %p", row, column, window);
233
234    field_slot_t field;
235    err = window->read_field_slot(row, column, &field);
236    if (err != 0) {
237        throwExceptionWithRowCol(env, row, column);
238        return NULL;
239    }
240
241    return field.type == FIELD_TYPE_BLOB || field.type == FIELD_TYPE_NULL;
242}
243
244static jboolean isString_native(JNIEnv* env, jobject object, jint row, jint column)
245{
246    int32_t err;
247    CursorWindow * window = GET_WINDOW(env, object);
248LOG_WINDOW("Checking if column is a string or null for %d,%d from %p", row, column, window);
249
250    field_slot_t field;
251    err = window->read_field_slot(row, column, &field);
252    if (err != 0) {
253        throwExceptionWithRowCol(env, row, column);
254        return NULL;
255    }
256
257    return field.type == FIELD_TYPE_STRING || field.type == FIELD_TYPE_NULL;
258}
259
260static jboolean isInteger_native(JNIEnv* env, jobject object, jint row, jint column)
261{
262    int32_t err;
263    CursorWindow * window = GET_WINDOW(env, object);
264LOG_WINDOW("Checking if column is an integer for %d,%d from %p", row, column, window);
265
266    field_slot_t field;
267    err = window->read_field_slot(row, column, &field);
268    if (err != 0) {
269        throwExceptionWithRowCol(env, row, column);
270        return NULL;
271    }
272
273    return field.type == FIELD_TYPE_INTEGER;
274}
275
276static jboolean isFloat_native(JNIEnv* env, jobject object, jint row, jint column)
277{
278    int32_t err;
279    CursorWindow * window = GET_WINDOW(env, object);
280LOG_WINDOW("Checking if column is a float for %d,%d from %p", row, column, window);
281
282    field_slot_t field;
283    err = window->read_field_slot(row, column, &field);
284    if (err != 0) {
285        throwExceptionWithRowCol(env, row, column);
286        return NULL;
287    }
288
289    return field.type == FIELD_TYPE_FLOAT;
290}
291
292static jstring getString_native(JNIEnv* env, jobject object, jint row, jint column)
293{
294    int32_t err;
295    CursorWindow * window = GET_WINDOW(env, object);
296LOG_WINDOW("Getting string for %d,%d from %p", row, column, window);
297
298    field_slot_t field;
299    err = window->read_field_slot(row, column, &field);
300    if (err != 0) {
301        throwExceptionWithRowCol(env, row, column);
302        return NULL;
303    }
304
305    uint8_t type = field.type;
306    if (type == FIELD_TYPE_STRING) {
307        uint32_t size = field.data.buffer.size;
308        if (size > 0) {
309#if WINDOW_STORAGE_UTF8
310            // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string
311            String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1);
312            return env->NewString((jchar const *)utf16.string(), utf16.size());
313#else
314            return env->NewString((jchar const *)window->offsetToPtr(field.data.buffer.offset), size / 2);
315#endif
316        } else {
317            return env->NewStringUTF("");
318        }
319    } else if (type == FIELD_TYPE_INTEGER) {
320        int64_t value;
321        if (window->getLong(row, column, &value)) {
322            char buf[32];
323            snprintf(buf, sizeof(buf), "%lld", value);
324            return env->NewStringUTF(buf);
325        }
326        return NULL;
327    } else if (type == FIELD_TYPE_FLOAT) {
328        double value;
329        if (window->getDouble(row, column, &value)) {
330            char buf[32];
331            snprintf(buf, sizeof(buf), "%g", value);
332            return env->NewStringUTF(buf);
333        }
334        return NULL;
335    } else if (type == FIELD_TYPE_NULL) {
336        return NULL;
337    } else if (type == FIELD_TYPE_BLOB) {
338        throw_sqlite3_exception(env, "Unable to convert BLOB to string");
339        return NULL;
340    } else {
341        throwUnknowTypeException(env, type);
342        return NULL;
343    }
344}
345
346/**
347 * Use this only to convert characters that are known to be within the
348 * 0-127 range for direct conversion to UTF-16
349 */
350static jint charToJchar(const char* src, jchar* dst, jint bufferSize)
351{
352    int32_t len = strlen(src);
353
354    if (bufferSize < len) {
355        len = bufferSize;
356    }
357
358    for (int i = 0; i < len; i++) {
359        *dst++ = (*src++ & 0x7F);
360    }
361    return len;
362}
363
364static jcharArray copyStringToBuffer_native(JNIEnv* env, jobject object, jint row,
365                                      jint column, jint bufferSize, jobject buf)
366{
367    int32_t err;
368    CursorWindow * window = GET_WINDOW(env, object);
369LOG_WINDOW("Copying string for %d,%d from %p", row, column, window);
370
371    field_slot_t field;
372    err = window->read_field_slot(row, column, &field);
373    if (err != 0) {
374        jniThrowException(env, "java/lang/IllegalStateException", "Unable to get field slot");
375        return NULL;
376    }
377
378    jcharArray buffer = (jcharArray)env->GetObjectField(buf, gBufferField);
379    if (buffer == NULL) {
380        jniThrowException(env, "java/lang/IllegalStateException", "buf should not be null");
381        return NULL;
382    }
383    jchar* dst = env->GetCharArrayElements(buffer, NULL);
384    uint8_t type = field.type;
385    uint32_t sizeCopied = 0;
386    jcharArray newArray = NULL;
387    if (type == FIELD_TYPE_STRING) {
388        uint32_t size = field.data.buffer.size;
389        if (size > 0) {
390#if WINDOW_STORAGE_UTF8
391            // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string
392            String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1);
393            int32_t strSize = utf16.size();
394            if (strSize > bufferSize || dst == NULL) {
395                newArray = env->NewCharArray(strSize);
396                env->SetCharArrayRegion(newArray, 0, strSize, (jchar const *)utf16.string());
397            } else {
398                memcpy(dst, (jchar const *)utf16.string(), strSize * 2);
399            }
400            sizeCopied = strSize;
401#else
402            sizeCopied = size/2 + size % 2;
403            if (size > bufferSize * 2 || dst == NULL) {
404                newArray = env->NewCharArray(sizeCopied);
405                memcpy(newArray, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size);
406            } else {
407                memcpy(dst, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size);
408            }
409#endif
410        }
411    } else if (type == FIELD_TYPE_INTEGER) {
412        int64_t value;
413        if (window->getLong(row, column, &value)) {
414            char buf[32];
415            int len;
416            snprintf(buf, sizeof(buf), "%lld", value);
417            jchar* dst = env->GetCharArrayElements(buffer, NULL);
418            sizeCopied = charToJchar(buf, dst, bufferSize);
419         }
420    } else if (type == FIELD_TYPE_FLOAT) {
421        double value;
422        if (window->getDouble(row, column, &value)) {
423            char tempbuf[32];
424            snprintf(tempbuf, sizeof(tempbuf), "%g", value);
425            jchar* dst = env->GetCharArrayElements(buffer, NULL);
426            sizeCopied = charToJchar(tempbuf, dst, bufferSize);
427        }
428    } else if (type == FIELD_TYPE_NULL) {
429    } else if (type == FIELD_TYPE_BLOB) {
430        throw_sqlite3_exception(env, "Unable to convert BLOB to string");
431    } else {
432        LOGE("Unknown field type %d", type);
433        throw_sqlite3_exception(env, "UNKNOWN type in copyStringToBuffer_native()");
434    }
435    SET_SIZE_COPIED(env, buf, sizeCopied);
436    env->ReleaseCharArrayElements(buffer, dst, JNI_OK);
437    return newArray;
438}
439
440static jdouble getDouble_native(JNIEnv* env, jobject object, jint row, jint column)
441{
442    int32_t err;
443    CursorWindow * window = GET_WINDOW(env, object);
444LOG_WINDOW("Getting double for %d,%d from %p", row, column, window);
445
446    field_slot_t field;
447    err = window->read_field_slot(row, column, &field);
448    if (err != 0) {
449        throwExceptionWithRowCol(env, row, column);
450        return 0.0;
451    }
452
453    uint8_t type = field.type;
454    if (type == FIELD_TYPE_FLOAT) {
455        double value;
456        if (window->getDouble(row, column, &value)) {
457            return value;
458        }
459        return 0.0;
460    } else if (type == FIELD_TYPE_STRING) {
461        uint32_t size = field.data.buffer.size;
462        if (size > 0) {
463#if WINDOW_STORAGE_UTF8
464            return strtod((char const *)window->offsetToPtr(field.data.buffer.offset), NULL);
465#else
466            String8 ascii((char16_t *) window->offsetToPtr(field.data.buffer.offset), size / 2);
467            char const * str = ascii.string();
468            return strtod(str, NULL);
469#endif
470        } else {
471            return 0.0;
472        }
473    } else if (type == FIELD_TYPE_INTEGER) {
474        int64_t value;
475        if (window->getLong(row, column, &value)) {
476            return (double) value;
477        }
478        return 0.0;
479    } else if (type == FIELD_TYPE_NULL) {
480        return 0.0;
481    } else if (type == FIELD_TYPE_BLOB) {
482        throw_sqlite3_exception(env, "Unable to convert BLOB to double");
483        return 0.0;
484    } else {
485        throwUnknowTypeException(env, type);
486        return 0.0;
487    }
488}
489
490static jboolean isNull_native(JNIEnv* env, jobject object, jint row, jint column)
491{
492    CursorWindow * window = GET_WINDOW(env, object);
493LOG_WINDOW("Checking for NULL at %d,%d from %p", row, column, window);
494
495    bool isNull;
496    if (window->getNull(row, column, &isNull)) {
497        return isNull;
498    }
499
500    //TODO throw execption?
501    return true;
502}
503
504static jint getNumRows(JNIEnv * env, jobject object)
505{
506    CursorWindow * window = GET_WINDOW(env, object);
507    return window->getNumRows();
508}
509
510static jboolean setNumColumns(JNIEnv * env, jobject object, jint columnNum)
511{
512    CursorWindow * window = GET_WINDOW(env, object);
513    return window->setNumColumns(columnNum);
514}
515
516static jboolean allocRow(JNIEnv * env, jobject object)
517{
518    CursorWindow * window = GET_WINDOW(env, object);
519    return window->allocRow() != NULL;
520}
521
522static jboolean putBlob_native(JNIEnv * env, jobject object, jbyteArray value, jint row, jint col)
523{
524    CursorWindow * window = GET_WINDOW(env, object);
525    if (!value) {
526        LOG_WINDOW("How did a null value send to here");
527        return false;
528    }
529    field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col);
530    if (fieldSlot == NULL) {
531        LOG_WINDOW(" getFieldSlotWithCheck error ");
532        return false;
533    }
534
535    jint len = env->GetArrayLength(value);
536    int offset = window->alloc(len);
537    if (!offset) {
538        LOG_WINDOW("Failed allocating %u bytes", len);
539        return false;
540    }
541    jbyte * bytes = env->GetByteArrayElements(value, NULL);
542    window->copyIn(offset, (uint8_t const *)bytes, len);
543
544    // This must be updated after the call to alloc(), since that
545    // may move the field around in the window
546    fieldSlot->type = FIELD_TYPE_BLOB;
547    fieldSlot->data.buffer.offset = offset;
548    fieldSlot->data.buffer.size = len;
549    env->ReleaseByteArrayElements(value, bytes, JNI_ABORT);
550    LOG_WINDOW("%d,%d is BLOB with %u bytes @ %d", row, col, len, offset);
551    return true;
552}
553
554static jboolean putString_native(JNIEnv * env, jobject object, jstring value, jint row, jint col)
555{
556    CursorWindow * window = GET_WINDOW(env, object);
557    if (!value) {
558        LOG_WINDOW("How did a null value send to here");
559        return false;
560    }
561    field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col);
562    if (fieldSlot == NULL) {
563        LOG_WINDOW(" getFieldSlotWithCheck error ");
564        return false;
565    }
566
567#if WINDOW_STORAGE_UTF8
568    int len = env->GetStringUTFLength(value) + 1;
569    char const * valStr = env->GetStringUTFChars(value, NULL);
570#else
571    int len = env->GetStringLength(value);
572    // GetStringLength return number of chars and one char takes 2 bytes
573    len *= 2;
574    const jchar* valStr = env->GetStringChars(value, NULL);
575#endif
576    if (!valStr) {
577        LOG_WINDOW("value can't be transfer to UTFChars");
578        return false;
579    }
580
581    int offset = window->alloc(len);
582    if (!offset) {
583        LOG_WINDOW("Failed allocating %u bytes", len);
584#if WINDOW_STORAGE_UTF8
585        env->ReleaseStringUTFChars(value, valStr);
586#else
587        env->ReleaseStringChars(value, valStr);
588#endif
589        return false;
590    }
591
592    window->copyIn(offset, (uint8_t const *)valStr, len);
593
594    // This must be updated after the call to alloc(), since that
595    // may move the field around in the window
596    fieldSlot->type = FIELD_TYPE_STRING;
597    fieldSlot->data.buffer.offset = offset;
598    fieldSlot->data.buffer.size = len;
599
600    LOG_WINDOW("%d,%d is TEXT with %u bytes @ %d", row, col, len, offset);
601#if WINDOW_STORAGE_UTF8
602    env->ReleaseStringUTFChars(value, valStr);
603#else
604    env->ReleaseStringChars(value, valStr);
605#endif
606
607    return true;
608}
609
610static jboolean putLong_native(JNIEnv * env, jobject object, jlong value, jint row, jint col)
611{
612    CursorWindow * window = GET_WINDOW(env, object);
613    if (!window->putLong(row, col, value)) {
614        LOG_WINDOW(" getFieldSlotWithCheck error ");
615        return false;
616    }
617
618    LOG_WINDOW("%d,%d is INTEGER 0x%016llx", row, col, value);
619
620    return true;
621}
622
623static jboolean putDouble_native(JNIEnv * env, jobject object, jdouble value, jint row, jint col)
624{
625    CursorWindow * window = GET_WINDOW(env, object);
626    if (!window->putDouble(row, col, value)) {
627        LOG_WINDOW(" getFieldSlotWithCheck error ");
628        return false;
629    }
630
631    LOG_WINDOW("%d,%d is FLOAT %lf", row, col, value);
632
633    return true;
634}
635
636static jboolean putNull_native(JNIEnv * env, jobject object, jint row, jint col)
637{
638    CursorWindow * window = GET_WINDOW(env, object);
639    if (!window->putNull(row, col)) {
640        LOG_WINDOW(" getFieldSlotWithCheck error ");
641        return false;
642    }
643
644    LOG_WINDOW("%d,%d is NULL", row, col);
645
646    return true;
647}
648
649// free the last row
650static void freeLastRow(JNIEnv * env, jobject object) {
651    CursorWindow * window = GET_WINDOW(env, object);
652    window->freeLastRow();
653}
654
655static JNINativeMethod sMethods[] =
656{
657     /* name, signature, funcPtr */
658    {"native_init", "(Z)V", (void *)native_init_empty},
659    {"native_init", "(Landroid/os/IBinder;)V", (void *)native_init_memory},
660    {"native_getBinder", "()Landroid/os/IBinder;", (void *)native_getBinder},
661    {"native_clear", "()V", (void *)native_clear},
662    {"close_native", "()V", (void *)native_close},
663    {"getLong_native", "(II)J", (void *)getLong_native},
664    {"getBlob_native", "(II)[B", (void *)getBlob_native},
665    {"isBlob_native", "(II)Z", (void *)isBlob_native},
666    {"getString_native", "(II)Ljava/lang/String;", (void *)getString_native},
667    {"copyStringToBuffer_native", "(IIILandroid/database/CharArrayBuffer;)[C", (void *)copyStringToBuffer_native},
668    {"getDouble_native", "(II)D", (void *)getDouble_native},
669    {"isNull_native", "(II)Z", (void *)isNull_native},
670    {"getNumRows_native", "()I", (void *)getNumRows},
671    {"setNumColumns_native", "(I)Z", (void *)setNumColumns},
672    {"allocRow_native", "()Z", (void *)allocRow},
673    {"putBlob_native", "([BII)Z", (void *)putBlob_native},
674    {"putString_native", "(Ljava/lang/String;II)Z", (void *)putString_native},
675    {"putLong_native", "(JII)Z", (void *)putLong_native},
676    {"putDouble_native", "(DII)Z", (void *)putDouble_native},
677    {"freeLastRow_native", "()V", (void *)freeLastRow},
678    {"putNull_native", "(II)Z", (void *)putNull_native},
679    {"isString_native", "(II)Z", (void *)isString_native},
680    {"isFloat_native", "(II)Z", (void *)isFloat_native},
681    {"isInteger_native", "(II)Z", (void *)isInteger_native},
682};
683
684int register_android_database_CursorWindow(JNIEnv * env)
685{
686    jclass clazz;
687
688    clazz = env->FindClass("android/database/CursorWindow");
689    if (clazz == NULL) {
690        LOGE("Can't find android/database/CursorWindow");
691        return -1;
692    }
693
694    gWindowField = env->GetFieldID(clazz, "nWindow", "I");
695
696    if (gWindowField == NULL) {
697        LOGE("Error locating fields");
698        return -1;
699    }
700
701    clazz =  env->FindClass("android/database/CharArrayBuffer");
702    if (clazz == NULL) {
703        LOGE("Can't find android/database/CharArrayBuffer");
704        return -1;
705    }
706
707    gBufferField = env->GetFieldID(clazz, "data", "[C");
708
709    if (gBufferField == NULL) {
710        LOGE("Error locating fields data in CharArrayBuffer");
711        return -1;
712    }
713
714    gSizeCopiedField = env->GetFieldID(clazz, "sizeCopied", "I");
715
716    if (gSizeCopiedField == NULL) {
717        LOGE("Error locating fields sizeCopied in CharArrayBuffer");
718        return -1;
719    }
720
721    return AndroidRuntime::registerNativeMethods(env, "android/database/CursorWindow",
722            sMethods, NELEM(sMethods));
723}
724
725} // namespace android
726