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