2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #define LOG_TAG "CursorWindow"
22 #include <android_runtime/AndroidRuntime.h>
24 #include <utils/Log.h>
25 #include <utils/String8.h>
26 #include <utils/String16.h>
27 #include <utils/Unicode.h>
33 #include <androidfw/CursorWindow.h>
34 #include "android_os_Parcel.h"
35 #include "android_util_Binder.h"
36 #include "android_database_SQLiteCommon.h"
43 } gCharArrayBufferClassInfo;
45 static jstring gEmptyString;
47 static void throwExceptionWithRowCol(JNIEnv* env, jint row, jint column) {
49 msg.appendFormat("Couldn't read row %d, col %d from CursorWindow. "
50 "Make sure the Cursor is initialized correctly before accessing data from it.",
52 jniThrowException(env, "java/lang/IllegalStateException", msg.string());
55 static void throwUnknownTypeException(JNIEnv * env, jint type) {
57 msg.appendFormat("UNKNOWN type %d", type);
58 jniThrowException(env, "java/lang/IllegalStateException", msg.string());
61 static jint nativeCreate(JNIEnv* env, jclass clazz, jstring nameObj, jint cursorWindowSize) {
63 const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
65 env->ReleaseStringUTFChars(nameObj, nameStr);
68 status_t status = CursorWindow::create(name, cursorWindowSize, &window);
69 if (status || !window) {
70 ALOGE("Could not allocate CursorWindow '%s' of size %d due to error %d.",
71 name.string(), cursorWindowSize, status);
75 LOG_WINDOW("nativeInitializeEmpty: window = %p", window);
76 return reinterpret_cast<jint>(window);
79 static jint nativeCreateFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) {
80 Parcel* parcel = parcelForJavaObject(env, parcelObj);
83 status_t status = CursorWindow::createFromParcel(parcel, &window);
84 if (status || !window) {
85 ALOGE("Could not create CursorWindow from Parcel due to error %d.", status);
89 LOG_WINDOW("nativeInitializeFromBinder: numRows = %d, numColumns = %d, window = %p",
90 window->getNumRows(), window->getNumColumns(), window);
91 return reinterpret_cast<jint>(window);
94 static void nativeDispose(JNIEnv* env, jclass clazz, jint windowPtr) {
95 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
97 LOG_WINDOW("Closing window %p", window);
102 static jstring nativeGetName(JNIEnv* env, jclass clazz, jint windowPtr) {
103 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
104 return env->NewStringUTF(window->name().string());
107 static void nativeWriteToParcel(JNIEnv * env, jclass clazz, jint windowPtr,
109 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
110 Parcel* parcel = parcelForJavaObject(env, parcelObj);
112 status_t status = window->writeToParcel(parcel);
115 msg.appendFormat("Could not write CursorWindow to Parcel due to error %d.", status);
116 jniThrowRuntimeException(env, msg.string());
120 static void nativeClear(JNIEnv * env, jclass clazz, jint windowPtr) {
121 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
122 LOG_WINDOW("Clearing window %p", window);
123 status_t status = window->clear();
125 LOG_WINDOW("Could not clear window. error=%d", status);
129 static jint nativeGetNumRows(JNIEnv* env, jclass clazz, jint windowPtr) {
130 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
131 return window->getNumRows();
134 static jboolean nativeSetNumColumns(JNIEnv* env, jclass clazz, jint windowPtr,
136 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
137 status_t status = window->setNumColumns(columnNum);
141 static jboolean nativeAllocRow(JNIEnv* env, jclass clazz, jint windowPtr) {
142 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
143 status_t status = window->allocRow();
147 static void nativeFreeLastRow(JNIEnv* env, jclass clazz, jint windowPtr) {
148 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
149 window->freeLastRow();
152 static jint nativeGetType(JNIEnv* env, jclass clazz, jint windowPtr,
153 jint row, jint column) {
154 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
155 LOG_WINDOW("returning column type affinity for %d,%d from %p", row, column, window);
157 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
159 // FIXME: This is really broken but we have CTS tests that depend
160 // on this legacy behavior.
161 //throwExceptionWithRowCol(env, row, column);
162 return CursorWindow::FIELD_TYPE_NULL;
164 return window->getFieldSlotType(fieldSlot);
167 static jbyteArray nativeGetBlob(JNIEnv* env, jclass clazz, jint windowPtr,
168 jint row, jint column) {
169 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
170 LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window);
172 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
174 throwExceptionWithRowCol(env, row, column);
178 int32_t type = window->getFieldSlotType(fieldSlot);
179 if (type == CursorWindow::FIELD_TYPE_BLOB || type == CursorWindow::FIELD_TYPE_STRING) {
181 const void* value = window->getFieldSlotValueBlob(fieldSlot, &size);
182 jbyteArray byteArray = env->NewByteArray(size);
184 env->ExceptionClear();
185 throw_sqlite3_exception(env, "Native could not create new byte[]");
188 env->SetByteArrayRegion(byteArray, 0, size, static_cast<const jbyte*>(value));
190 } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
191 throw_sqlite3_exception(env, "INTEGER data in nativeGetBlob ");
192 } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
193 throw_sqlite3_exception(env, "FLOAT data in nativeGetBlob ");
194 } else if (type == CursorWindow::FIELD_TYPE_NULL) {
197 throwUnknownTypeException(env, type);
202 static jstring nativeGetString(JNIEnv* env, jclass clazz, jint windowPtr,
203 jint row, jint column) {
204 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
205 LOG_WINDOW("Getting string for %d,%d from %p", row, column, window);
207 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
209 throwExceptionWithRowCol(env, row, column);
213 int32_t type = window->getFieldSlotType(fieldSlot);
214 if (type == CursorWindow::FIELD_TYPE_STRING) {
215 size_t sizeIncludingNull;
216 const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
217 if (sizeIncludingNull <= 1) {
220 // Convert to UTF-16 here instead of calling NewStringUTF. NewStringUTF
221 // doesn't like UTF-8 strings with high codepoints. It actually expects
222 // Modified UTF-8 with encoded surrogate pairs.
223 String16 utf16(value, sizeIncludingNull - 1);
224 return env->NewString(reinterpret_cast<const jchar*>(utf16.string()), utf16.size());
225 } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
226 int64_t value = window->getFieldSlotValueLong(fieldSlot);
228 snprintf(buf, sizeof(buf), "%lld", value);
229 return env->NewStringUTF(buf);
230 } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
231 double value = window->getFieldSlotValueDouble(fieldSlot);
233 snprintf(buf, sizeof(buf), "%g", value);
234 return env->NewStringUTF(buf);
235 } else if (type == CursorWindow::FIELD_TYPE_NULL) {
237 } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
238 throw_sqlite3_exception(env, "Unable to convert BLOB to string");
241 throwUnknownTypeException(env, type);
246 static jcharArray allocCharArrayBuffer(JNIEnv* env, jobject bufferObj, size_t size) {
247 jcharArray dataObj = jcharArray(env->GetObjectField(bufferObj,
248 gCharArrayBufferClassInfo.data));
249 if (dataObj && size) {
250 jsize capacity = env->GetArrayLength(dataObj);
251 if (size_t(capacity) < size) {
252 env->DeleteLocalRef(dataObj);
257 jsize capacity = size;
261 dataObj = env->NewCharArray(capacity); // might throw OOM
263 env->SetObjectField(bufferObj, gCharArrayBufferClassInfo.data, dataObj);
269 static void fillCharArrayBufferUTF(JNIEnv* env, jobject bufferObj,
270 const char* str, size_t len) {
271 ssize_t size = utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str), len);
273 size = 0; // invalid UTF8 string
275 jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, size);
278 jchar* data = static_cast<jchar*>(env->GetPrimitiveArrayCritical(dataObj, NULL));
279 utf8_to_utf16_no_null_terminator(reinterpret_cast<const uint8_t*>(str), len,
280 reinterpret_cast<char16_t*>(data));
281 env->ReleasePrimitiveArrayCritical(dataObj, data, 0);
283 env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, size);
287 static void clearCharArrayBuffer(JNIEnv* env, jobject bufferObj) {
288 jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, 0);
290 env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, 0);
294 static void nativeCopyStringToBuffer(JNIEnv* env, jclass clazz, jint windowPtr,
295 jint row, jint column, jobject bufferObj) {
296 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
297 LOG_WINDOW("Copying string for %d,%d from %p", row, column, window);
299 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
301 throwExceptionWithRowCol(env, row, column);
305 int32_t type = window->getFieldSlotType(fieldSlot);
306 if (type == CursorWindow::FIELD_TYPE_STRING) {
307 size_t sizeIncludingNull;
308 const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
309 if (sizeIncludingNull > 1) {
310 fillCharArrayBufferUTF(env, bufferObj, value, sizeIncludingNull - 1);
312 clearCharArrayBuffer(env, bufferObj);
314 } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
315 int64_t value = window->getFieldSlotValueLong(fieldSlot);
317 snprintf(buf, sizeof(buf), "%lld", value);
318 fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf));
319 } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
320 double value = window->getFieldSlotValueDouble(fieldSlot);
322 snprintf(buf, sizeof(buf), "%g", value);
323 fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf));
324 } else if (type == CursorWindow::FIELD_TYPE_NULL) {
325 clearCharArrayBuffer(env, bufferObj);
326 } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
327 throw_sqlite3_exception(env, "Unable to convert BLOB to string");
329 throwUnknownTypeException(env, type);
333 static jlong nativeGetLong(JNIEnv* env, jclass clazz, jint windowPtr,
334 jint row, jint column) {
335 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
336 LOG_WINDOW("Getting long for %d,%d from %p", row, column, window);
338 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
340 throwExceptionWithRowCol(env, row, column);
344 int32_t type = window->getFieldSlotType(fieldSlot);
345 if (type == CursorWindow::FIELD_TYPE_INTEGER) {
346 return window->getFieldSlotValueLong(fieldSlot);
347 } else if (type == CursorWindow::FIELD_TYPE_STRING) {
348 size_t sizeIncludingNull;
349 const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
350 return sizeIncludingNull > 1 ? strtoll(value, NULL, 0) : 0L;
351 } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
352 return jlong(window->getFieldSlotValueDouble(fieldSlot));
353 } else if (type == CursorWindow::FIELD_TYPE_NULL) {
355 } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
356 throw_sqlite3_exception(env, "Unable to convert BLOB to long");
359 throwUnknownTypeException(env, type);
364 static jdouble nativeGetDouble(JNIEnv* env, jclass clazz, jint windowPtr,
365 jint row, jint column) {
366 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
367 LOG_WINDOW("Getting double for %d,%d from %p", row, column, window);
369 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
371 throwExceptionWithRowCol(env, row, column);
375 int32_t type = window->getFieldSlotType(fieldSlot);
376 if (type == CursorWindow::FIELD_TYPE_FLOAT) {
377 return window->getFieldSlotValueDouble(fieldSlot);
378 } else if (type == CursorWindow::FIELD_TYPE_STRING) {
379 size_t sizeIncludingNull;
380 const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
381 return sizeIncludingNull > 1 ? strtod(value, NULL) : 0.0;
382 } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
383 return jdouble(window->getFieldSlotValueLong(fieldSlot));
384 } else if (type == CursorWindow::FIELD_TYPE_NULL) {
386 } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
387 throw_sqlite3_exception(env, "Unable to convert BLOB to double");
390 throwUnknownTypeException(env, type);
395 static jboolean nativePutBlob(JNIEnv* env, jclass clazz, jint windowPtr,
396 jbyteArray valueObj, jint row, jint column) {
397 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
398 jsize len = env->GetArrayLength(valueObj);
400 void* value = env->GetPrimitiveArrayCritical(valueObj, NULL);
401 status_t status = window->putBlob(row, column, value, len);
402 env->ReleasePrimitiveArrayCritical(valueObj, value, JNI_ABORT);
405 LOG_WINDOW("Failed to put blob. error=%d", status);
409 LOG_WINDOW("%d,%d is BLOB with %u bytes", row, column, len);
413 static jboolean nativePutString(JNIEnv* env, jclass clazz, jint windowPtr,
414 jstring valueObj, jint row, jint column) {
415 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
417 size_t sizeIncludingNull = env->GetStringUTFLength(valueObj) + 1;
418 const char* valueStr = env->GetStringUTFChars(valueObj, NULL);
420 LOG_WINDOW("value can't be transferred to UTFChars");
423 status_t status = window->putString(row, column, valueStr, sizeIncludingNull);
424 env->ReleaseStringUTFChars(valueObj, valueStr);
427 LOG_WINDOW("Failed to put string. error=%d", status);
431 LOG_WINDOW("%d,%d is TEXT with %u bytes", row, column, sizeIncludingNull);
435 static jboolean nativePutLong(JNIEnv* env, jclass clazz, jint windowPtr,
436 jlong value, jint row, jint column) {
437 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
438 status_t status = window->putLong(row, column, value);
441 LOG_WINDOW("Failed to put long. error=%d", status);
445 LOG_WINDOW("%d,%d is INTEGER 0x%016llx", row, column, value);
449 static jboolean nativePutDouble(JNIEnv* env, jclass clazz, jint windowPtr,
450 jdouble value, jint row, jint column) {
451 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
452 status_t status = window->putDouble(row, column, value);
455 LOG_WINDOW("Failed to put double. error=%d", status);
459 LOG_WINDOW("%d,%d is FLOAT %lf", row, column, value);
463 static jboolean nativePutNull(JNIEnv* env, jclass clazz, jint windowPtr,
464 jint row, jint column) {
465 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
466 status_t status = window->putNull(row, column);
469 LOG_WINDOW("Failed to put null. error=%d", status);
473 LOG_WINDOW("%d,%d is NULL", row, column);
477 static JNINativeMethod sMethods[] =
479 /* name, signature, funcPtr */
480 { "nativeCreate", "(Ljava/lang/String;I)I",
481 (void*)nativeCreate },
482 { "nativeCreateFromParcel", "(Landroid/os/Parcel;)I",
483 (void*)nativeCreateFromParcel },
484 { "nativeDispose", "(I)V",
485 (void*)nativeDispose },
486 { "nativeWriteToParcel", "(ILandroid/os/Parcel;)V",
487 (void*)nativeWriteToParcel },
488 { "nativeGetName", "(I)Ljava/lang/String;",
489 (void*)nativeGetName },
490 { "nativeClear", "(I)V",
491 (void*)nativeClear },
492 { "nativeGetNumRows", "(I)I",
493 (void*)nativeGetNumRows },
494 { "nativeSetNumColumns", "(II)Z",
495 (void*)nativeSetNumColumns },
496 { "nativeAllocRow", "(I)Z",
497 (void*)nativeAllocRow },
498 { "nativeFreeLastRow", "(I)V",
499 (void*)nativeFreeLastRow },
500 { "nativeGetType", "(III)I",
501 (void*)nativeGetType },
502 { "nativeGetBlob", "(III)[B",
503 (void*)nativeGetBlob },
504 { "nativeGetString", "(III)Ljava/lang/String;",
505 (void*)nativeGetString },
506 { "nativeGetLong", "(III)J",
507 (void*)nativeGetLong },
508 { "nativeGetDouble", "(III)D",
509 (void*)nativeGetDouble },
510 { "nativeCopyStringToBuffer", "(IIILandroid/database/CharArrayBuffer;)V",
511 (void*)nativeCopyStringToBuffer },
512 { "nativePutBlob", "(I[BII)Z",
513 (void*)nativePutBlob },
514 { "nativePutString", "(ILjava/lang/String;II)Z",
515 (void*)nativePutString },
516 { "nativePutLong", "(IJII)Z",
517 (void*)nativePutLong },
518 { "nativePutDouble", "(IDII)Z",
519 (void*)nativePutDouble },
520 { "nativePutNull", "(III)Z",
521 (void*)nativePutNull },
524 #define FIND_CLASS(var, className) \
525 var = env->FindClass(className); \
526 LOG_FATAL_IF(! var, "Unable to find class " className);
528 #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
529 var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
530 LOG_FATAL_IF(! var, "Unable to find field " fieldName);
532 int register_android_database_CursorWindow(JNIEnv * env)
535 FIND_CLASS(clazz, "android/database/CharArrayBuffer");
537 GET_FIELD_ID(gCharArrayBufferClassInfo.data, clazz,
539 GET_FIELD_ID(gCharArrayBufferClassInfo.sizeCopied, clazz,
542 gEmptyString = jstring(env->NewGlobalRef(env->NewStringUTF("")));
543 LOG_FATAL_IF(!gEmptyString, "Unable to create empty string");
545 return AndroidRuntime::registerNativeMethods(env, "android/database/CursorWindow",
546 sMethods, NELEM(sMethods));
549 } // namespace android