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