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