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