android_database_CursorWindow.cpp revision 0a2c6cc0138ceac8164061b3cdc3758441916c18
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 "binder/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 jstring getString_native(JNIEnv* env, jobject object, jint row, jint column)
229{
230    int32_t err;
231    CursorWindow * window = GET_WINDOW(env, object);
232LOG_WINDOW("Getting string 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    uint8_t type = field.type;
242    if (type == FIELD_TYPE_STRING) {
243        uint32_t size = field.data.buffer.size;
244        if (size > 0) {
245#if WINDOW_STORAGE_UTF8
246            // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string
247            String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1);
248            return env->NewString((jchar const *)utf16.string(), utf16.size());
249#else
250            return env->NewString((jchar const *)window->offsetToPtr(field.data.buffer.offset), size / 2);
251#endif
252        } else {
253            return env->NewStringUTF("");
254        }
255    } else if (type == FIELD_TYPE_INTEGER) {
256        int64_t value;
257        if (window->getLong(row, column, &value)) {
258            char buf[32];
259            snprintf(buf, sizeof(buf), "%lld", value);
260            return env->NewStringUTF(buf);
261        }
262        return NULL;
263    } else if (type == FIELD_TYPE_FLOAT) {
264        double value;
265        if (window->getDouble(row, column, &value)) {
266            char buf[32];
267            snprintf(buf, sizeof(buf), "%g", value);
268            return env->NewStringUTF(buf);
269        }
270        return NULL;
271    } else if (type == FIELD_TYPE_NULL) {
272        return NULL;
273    } else if (type == FIELD_TYPE_BLOB) {
274        throw_sqlite3_exception(env, "Unable to convert BLOB to string");
275        return NULL;
276    } else {
277        throwUnknowTypeException(env, type);
278        return NULL;
279    }
280}
281
282/**
283 * Use this only to convert characters that are known to be within the
284 * 0-127 range for direct conversion to UTF-16
285 */
286static jint charToJchar(const char* src, jchar* dst, jint bufferSize)
287{
288    int32_t len = strlen(src);
289
290    if (bufferSize < len) {
291        len = bufferSize;
292    }
293
294    for (int i = 0; i < len; i++) {
295        *dst++ = (*src++ & 0x7F);
296    }
297    return len;
298}
299
300static jcharArray copyStringToBuffer_native(JNIEnv* env, jobject object, jint row,
301                                      jint column, jint bufferSize, jobject buf)
302{
303    int32_t err;
304    CursorWindow * window = GET_WINDOW(env, object);
305LOG_WINDOW("Copying string for %d,%d from %p", row, column, window);
306
307    field_slot_t field;
308    err = window->read_field_slot(row, column, &field);
309    if (err != 0) {
310        jniThrowException(env, "java/lang/IllegalStateException", "Unable to get field slot");
311        return NULL;
312    }
313
314    jcharArray buffer = (jcharArray)env->GetObjectField(buf, gBufferField);
315    if (buffer == NULL) {
316        jniThrowException(env, "java/lang/IllegalStateException", "buf should not be null");
317        return NULL;
318    }
319    jchar* dst = env->GetCharArrayElements(buffer, NULL);
320    uint8_t type = field.type;
321    uint32_t sizeCopied = 0;
322    jcharArray newArray = NULL;
323    if (type == FIELD_TYPE_STRING) {
324        uint32_t size = field.data.buffer.size;
325        if (size > 0) {
326#if WINDOW_STORAGE_UTF8
327            // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string
328            String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1);
329            int32_t strSize = utf16.size();
330            if (strSize > bufferSize || dst == NULL) {
331                newArray = env->NewCharArray(strSize);
332                env->SetCharArrayRegion(newArray, 0, strSize, (jchar const *)utf16.string());
333            } else {
334                memcpy(dst, (jchar const *)utf16.string(), strSize * 2);
335            }
336            sizeCopied = strSize;
337#else
338            sizeCopied = size/2 + size % 2;
339            if (size > bufferSize * 2 || dst == NULL) {
340                newArray = env->NewCharArray(sizeCopied);
341                memcpy(newArray, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size);
342            } else {
343                memcpy(dst, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size);
344            }
345#endif
346        }
347    } else if (type == FIELD_TYPE_INTEGER) {
348        int64_t value;
349        if (window->getLong(row, column, &value)) {
350            char buf[32];
351            int len;
352            snprintf(buf, sizeof(buf), "%lld", value);
353            jchar* dst = env->GetCharArrayElements(buffer, NULL);
354            sizeCopied = charToJchar(buf, dst, bufferSize);
355         }
356    } else if (type == FIELD_TYPE_FLOAT) {
357        double value;
358        if (window->getDouble(row, column, &value)) {
359            char tempbuf[32];
360            snprintf(tempbuf, sizeof(tempbuf), "%g", value);
361            jchar* dst = env->GetCharArrayElements(buffer, NULL);
362            sizeCopied = charToJchar(tempbuf, dst, bufferSize);
363        }
364    } else if (type == FIELD_TYPE_NULL) {
365    } else if (type == FIELD_TYPE_BLOB) {
366        throw_sqlite3_exception(env, "Unable to convert BLOB to string");
367    } else {
368        LOGE("Unknown field type %d", type);
369        throw_sqlite3_exception(env, "UNKNOWN type in copyStringToBuffer_native()");
370    }
371    SET_SIZE_COPIED(env, buf, sizeCopied);
372    env->ReleaseCharArrayElements(buffer, dst, JNI_OK);
373    return newArray;
374}
375
376static jdouble getDouble_native(JNIEnv* env, jobject object, jint row, jint column)
377{
378    int32_t err;
379    CursorWindow * window = GET_WINDOW(env, object);
380LOG_WINDOW("Getting double for %d,%d from %p", row, column, window);
381
382    field_slot_t field;
383    err = window->read_field_slot(row, column, &field);
384    if (err != 0) {
385        throwExceptionWithRowCol(env, row, column);
386        return 0.0;
387    }
388
389    uint8_t type = field.type;
390    if (type == FIELD_TYPE_FLOAT) {
391        double value;
392        if (window->getDouble(row, column, &value)) {
393            return value;
394        }
395        return 0.0;
396    } else if (type == FIELD_TYPE_STRING) {
397        uint32_t size = field.data.buffer.size;
398        if (size > 0) {
399#if WINDOW_STORAGE_UTF8
400            return strtod((char const *)window->offsetToPtr(field.data.buffer.offset), NULL);
401#else
402            String8 ascii((char16_t *) window->offsetToPtr(field.data.buffer.offset), size / 2);
403            char const * str = ascii.string();
404            return strtod(str, NULL);
405#endif
406        } else {
407            return 0.0;
408        }
409    } else if (type == FIELD_TYPE_INTEGER) {
410        int64_t value;
411        if (window->getLong(row, column, &value)) {
412            return (double) value;
413        }
414        return 0.0;
415    } else if (type == FIELD_TYPE_NULL) {
416        return 0.0;
417    } else if (type == FIELD_TYPE_BLOB) {
418        throw_sqlite3_exception(env, "Unable to convert BLOB to double");
419        return 0.0;
420    } else {
421        throwUnknowTypeException(env, type);
422        return 0.0;
423    }
424}
425
426bool isNull_native(CursorWindow *window, jint row, jint column)
427{
428    LOG_WINDOW("Checking for NULL at %d,%d from %p", row, column, window);
429
430    bool isNull;
431    if (window->getNull(row, column, &isNull)) {
432        return isNull;
433    }
434
435    //TODO throw execption?
436    return true;
437}
438
439static jint getNumRows(JNIEnv * env, jobject object)
440{
441    CursorWindow * window = GET_WINDOW(env, object);
442    return window->getNumRows();
443}
444
445static jboolean setNumColumns(JNIEnv * env, jobject object, jint columnNum)
446{
447    CursorWindow * window = GET_WINDOW(env, object);
448    return window->setNumColumns(columnNum);
449}
450
451static jboolean allocRow(JNIEnv * env, jobject object)
452{
453    CursorWindow * window = GET_WINDOW(env, object);
454    return window->allocRow() != NULL;
455}
456
457static jboolean putBlob_native(JNIEnv * env, jobject object, jbyteArray value, jint row, jint col)
458{
459    CursorWindow * window = GET_WINDOW(env, object);
460    if (!value) {
461        LOG_WINDOW("How did a null value send to here");
462        return false;
463    }
464    field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col);
465    if (fieldSlot == NULL) {
466        LOG_WINDOW(" getFieldSlotWithCheck error ");
467        return false;
468    }
469
470    jint len = env->GetArrayLength(value);
471    int offset = window->alloc(len);
472    if (!offset) {
473        LOG_WINDOW("Failed allocating %u bytes", len);
474        return false;
475    }
476    jbyte * bytes = env->GetByteArrayElements(value, NULL);
477    window->copyIn(offset, (uint8_t const *)bytes, len);
478
479    // This must be updated after the call to alloc(), since that
480    // may move the field around in the window
481    fieldSlot->type = FIELD_TYPE_BLOB;
482    fieldSlot->data.buffer.offset = offset;
483    fieldSlot->data.buffer.size = len;
484    env->ReleaseByteArrayElements(value, bytes, JNI_ABORT);
485    LOG_WINDOW("%d,%d is BLOB with %u bytes @ %d", row, col, len, offset);
486    return true;
487}
488
489static jboolean putString_native(JNIEnv * env, jobject object, jstring value, jint row, jint col)
490{
491    CursorWindow * window = GET_WINDOW(env, object);
492    if (!value) {
493        LOG_WINDOW("How did a null value send to here");
494        return false;
495    }
496    field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col);
497    if (fieldSlot == NULL) {
498        LOG_WINDOW(" getFieldSlotWithCheck error ");
499        return false;
500    }
501
502#if WINDOW_STORAGE_UTF8
503    int len = env->GetStringUTFLength(value) + 1;
504    char const * valStr = env->GetStringUTFChars(value, NULL);
505#else
506    int len = env->GetStringLength(value);
507    // GetStringLength return number of chars and one char takes 2 bytes
508    len *= 2;
509    const jchar* valStr = env->GetStringChars(value, NULL);
510#endif
511    if (!valStr) {
512        LOG_WINDOW("value can't be transfer to UTFChars");
513        return false;
514    }
515
516    int offset = window->alloc(len);
517    if (!offset) {
518        LOG_WINDOW("Failed allocating %u bytes", len);
519#if WINDOW_STORAGE_UTF8
520        env->ReleaseStringUTFChars(value, valStr);
521#else
522        env->ReleaseStringChars(value, valStr);
523#endif
524        return false;
525    }
526
527    window->copyIn(offset, (uint8_t const *)valStr, len);
528
529    // This must be updated after the call to alloc(), since that
530    // may move the field around in the window
531    fieldSlot->type = FIELD_TYPE_STRING;
532    fieldSlot->data.buffer.offset = offset;
533    fieldSlot->data.buffer.size = len;
534
535    LOG_WINDOW("%d,%d is TEXT with %u bytes @ %d", row, col, len, offset);
536#if WINDOW_STORAGE_UTF8
537    env->ReleaseStringUTFChars(value, valStr);
538#else
539    env->ReleaseStringChars(value, valStr);
540#endif
541
542    return true;
543}
544
545static jboolean putLong_native(JNIEnv * env, jobject object, jlong value, jint row, jint col)
546{
547    CursorWindow * window = GET_WINDOW(env, object);
548    if (!window->putLong(row, col, value)) {
549        LOG_WINDOW(" getFieldSlotWithCheck error ");
550        return false;
551    }
552
553    LOG_WINDOW("%d,%d is INTEGER 0x%016llx", row, col, value);
554
555    return true;
556}
557
558static jboolean putDouble_native(JNIEnv * env, jobject object, jdouble value, jint row, jint col)
559{
560    CursorWindow * window = GET_WINDOW(env, object);
561    if (!window->putDouble(row, col, value)) {
562        LOG_WINDOW(" getFieldSlotWithCheck error ");
563        return false;
564    }
565
566    LOG_WINDOW("%d,%d is FLOAT %lf", row, col, value);
567
568    return true;
569}
570
571static jboolean putNull_native(JNIEnv * env, jobject object, jint row, jint col)
572{
573    CursorWindow * window = GET_WINDOW(env, object);
574    if (!window->putNull(row, col)) {
575        LOG_WINDOW(" getFieldSlotWithCheck error ");
576        return false;
577    }
578
579    LOG_WINDOW("%d,%d is NULL", row, col);
580
581    return true;
582}
583
584// free the last row
585static void freeLastRow(JNIEnv * env, jobject object) {
586    CursorWindow * window = GET_WINDOW(env, object);
587    window->freeLastRow();
588}
589
590static jint getType_native(JNIEnv* env, jobject object, jint row, jint column)
591{
592    int32_t err;
593    CursorWindow * window = GET_WINDOW(env, object);
594    LOG_WINDOW("returning column type affinity for %d,%d from %p", row, column, window);
595
596    if (isNull_native(window, row, column)) {
597      return FIELD_TYPE_NULL;
598    }
599
600    field_slot_t field;
601    err = window->read_field_slot(row, column, &field);
602    if (err != 0) {
603        throwExceptionWithRowCol(env, row, column);
604        return NULL;
605    }
606
607    return field.type;
608}
609
610static JNINativeMethod sMethods[] =
611{
612     /* name, signature, funcPtr */
613    {"native_init", "(Z)V", (void *)native_init_empty},
614    {"native_init", "(Landroid/os/IBinder;)V", (void *)native_init_memory},
615    {"native_getBinder", "()Landroid/os/IBinder;", (void *)native_getBinder},
616    {"native_clear", "()V", (void *)native_clear},
617    {"close_native", "()V", (void *)native_close},
618    {"getLong_native", "(II)J", (void *)getLong_native},
619    {"getBlob_native", "(II)[B", (void *)getBlob_native},
620    {"getString_native", "(II)Ljava/lang/String;", (void *)getString_native},
621    {"copyStringToBuffer_native", "(IIILandroid/database/CharArrayBuffer;)[C", (void *)copyStringToBuffer_native},
622    {"getDouble_native", "(II)D", (void *)getDouble_native},
623    {"getNumRows_native", "()I", (void *)getNumRows},
624    {"setNumColumns_native", "(I)Z", (void *)setNumColumns},
625    {"allocRow_native", "()Z", (void *)allocRow},
626    {"putBlob_native", "([BII)Z", (void *)putBlob_native},
627    {"putString_native", "(Ljava/lang/String;II)Z", (void *)putString_native},
628    {"putLong_native", "(JII)Z", (void *)putLong_native},
629    {"putDouble_native", "(DII)Z", (void *)putDouble_native},
630    {"freeLastRow_native", "()V", (void *)freeLastRow},
631    {"putNull_native", "(II)Z", (void *)putNull_native},
632    {"getType_native", "(II)I", (void *)getType_native},
633};
634
635int register_android_database_CursorWindow(JNIEnv * env)
636{
637    jclass clazz;
638
639    clazz = env->FindClass("android/database/CursorWindow");
640    if (clazz == NULL) {
641        LOGE("Can't find android/database/CursorWindow");
642        return -1;
643    }
644
645    gWindowField = env->GetFieldID(clazz, "nWindow", "I");
646
647    if (gWindowField == NULL) {
648        LOGE("Error locating fields");
649        return -1;
650    }
651
652    clazz =  env->FindClass("android/database/CharArrayBuffer");
653    if (clazz == NULL) {
654        LOGE("Can't find android/database/CharArrayBuffer");
655        return -1;
656    }
657
658    gBufferField = env->GetFieldID(clazz, "data", "[C");
659
660    if (gBufferField == NULL) {
661        LOGE("Error locating fields data in CharArrayBuffer");
662        return -1;
663    }
664
665    gSizeCopiedField = env->GetFieldID(clazz, "sizeCopied", "I");
666
667    if (gSizeCopiedField == NULL) {
668        LOGE("Error locating fields sizeCopied in CharArrayBuffer");
669        return -1;
670    }
671
672    return AndroidRuntime::registerNativeMethods(env, "android/database/CursorWindow",
673            sMethods, NELEM(sMethods));
674}
675
676} // namespace android
677