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 <nativehelper/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 if (!value) { 209 throw_sqlite3_exception(env, "Native could not read blob slot"); 210 return NULL; 211 } 212 jbyteArray byteArray = env->NewByteArray(size); 213 if (!byteArray) { 214 env->ExceptionClear(); 215 throw_sqlite3_exception(env, "Native could not create new byte[]"); 216 return NULL; 217 } 218 env->SetByteArrayRegion(byteArray, 0, size, static_cast<const jbyte*>(value)); 219 return byteArray; 220 } else if (type == CursorWindow::FIELD_TYPE_INTEGER) { 221 throw_sqlite3_exception(env, "INTEGER data in nativeGetBlob "); 222 } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { 223 throw_sqlite3_exception(env, "FLOAT data in nativeGetBlob "); 224 } else if (type == CursorWindow::FIELD_TYPE_NULL) { 225 // do nothing 226 } else { 227 throwUnknownTypeException(env, type); 228 } 229 return NULL; 230} 231 232static jstring nativeGetString(JNIEnv* env, jclass clazz, jlong windowPtr, 233 jint row, jint column) { 234 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 235 LOG_WINDOW("Getting string for %d,%d from %p", row, column, window); 236 237 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); 238 if (!fieldSlot) { 239 throwExceptionWithRowCol(env, row, column); 240 return NULL; 241 } 242 243 int32_t type = window->getFieldSlotType(fieldSlot); 244 if (type == CursorWindow::FIELD_TYPE_STRING) { 245 size_t sizeIncludingNull; 246 const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull); 247 if (!value) { 248 throw_sqlite3_exception(env, "Native could not read string slot"); 249 return NULL; 250 } 251 if (sizeIncludingNull <= 1) { 252 return gEmptyString; 253 } 254 // Convert to UTF-16 here instead of calling NewStringUTF. NewStringUTF 255 // doesn't like UTF-8 strings with high codepoints. It actually expects 256 // Modified UTF-8 with encoded surrogate pairs. 257 String16 utf16(value, sizeIncludingNull - 1); 258 return env->NewString(reinterpret_cast<const jchar*>(utf16.string()), utf16.size()); 259 } else if (type == CursorWindow::FIELD_TYPE_INTEGER) { 260 int64_t value = window->getFieldSlotValueLong(fieldSlot); 261 char buf[32]; 262 snprintf(buf, sizeof(buf), "%" PRId64, value); 263 return env->NewStringUTF(buf); 264 } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { 265 double value = window->getFieldSlotValueDouble(fieldSlot); 266 char buf[32]; 267 snprintf(buf, sizeof(buf), "%g", value); 268 return env->NewStringUTF(buf); 269 } else if (type == CursorWindow::FIELD_TYPE_NULL) { 270 return NULL; 271 } else if (type == CursorWindow::FIELD_TYPE_BLOB) { 272 throw_sqlite3_exception(env, "Unable to convert BLOB to string"); 273 return NULL; 274 } else { 275 throwUnknownTypeException(env, type); 276 return NULL; 277 } 278} 279 280static jcharArray allocCharArrayBuffer(JNIEnv* env, jobject bufferObj, size_t size) { 281 jcharArray dataObj = jcharArray(env->GetObjectField(bufferObj, 282 gCharArrayBufferClassInfo.data)); 283 if (dataObj && size) { 284 jsize capacity = env->GetArrayLength(dataObj); 285 if (size_t(capacity) < size) { 286 env->DeleteLocalRef(dataObj); 287 dataObj = NULL; 288 } 289 } 290 if (!dataObj) { 291 jsize capacity = size; 292 if (capacity < 64) { 293 capacity = 64; 294 } 295 dataObj = env->NewCharArray(capacity); // might throw OOM 296 if (dataObj) { 297 env->SetObjectField(bufferObj, gCharArrayBufferClassInfo.data, dataObj); 298 } 299 } 300 return dataObj; 301} 302 303static void fillCharArrayBufferUTF(JNIEnv* env, jobject bufferObj, 304 const char* str, size_t len) { 305 ssize_t size = utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str), len); 306 if (size < 0) { 307 size = 0; // invalid UTF8 string 308 } 309 jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, size); 310 if (dataObj) { 311 if (size) { 312 jchar* data = static_cast<jchar*>(env->GetPrimitiveArrayCritical(dataObj, NULL)); 313 utf8_to_utf16_no_null_terminator(reinterpret_cast<const uint8_t*>(str), len, 314 reinterpret_cast<char16_t*>(data), (size_t) size); 315 env->ReleasePrimitiveArrayCritical(dataObj, data, 0); 316 } 317 env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, size); 318 } 319} 320 321static void clearCharArrayBuffer(JNIEnv* env, jobject bufferObj) { 322 jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, 0); 323 if (dataObj) { 324 env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, 0); 325 } 326} 327 328static void nativeCopyStringToBuffer(JNIEnv* env, jclass clazz, jlong windowPtr, 329 jint row, jint column, jobject bufferObj) { 330 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 331 LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); 332 333 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); 334 if (!fieldSlot) { 335 throwExceptionWithRowCol(env, row, column); 336 return; 337 } 338 339 int32_t type = window->getFieldSlotType(fieldSlot); 340 if (type == CursorWindow::FIELD_TYPE_STRING) { 341 size_t sizeIncludingNull; 342 const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull); 343 if (sizeIncludingNull > 1) { 344 fillCharArrayBufferUTF(env, bufferObj, value, sizeIncludingNull - 1); 345 } else { 346 clearCharArrayBuffer(env, bufferObj); 347 } 348 } else if (type == CursorWindow::FIELD_TYPE_INTEGER) { 349 int64_t value = window->getFieldSlotValueLong(fieldSlot); 350 char buf[32]; 351 snprintf(buf, sizeof(buf), "%" PRId64, value); 352 fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf)); 353 } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { 354 double value = window->getFieldSlotValueDouble(fieldSlot); 355 char buf[32]; 356 snprintf(buf, sizeof(buf), "%g", value); 357 fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf)); 358 } else if (type == CursorWindow::FIELD_TYPE_NULL) { 359 clearCharArrayBuffer(env, bufferObj); 360 } else if (type == CursorWindow::FIELD_TYPE_BLOB) { 361 throw_sqlite3_exception(env, "Unable to convert BLOB to string"); 362 } else { 363 throwUnknownTypeException(env, type); 364 } 365} 366 367static jlong nativeGetLong(JNIEnv* env, jclass clazz, jlong windowPtr, 368 jint row, jint column) { 369 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 370 LOG_WINDOW("Getting long for %d,%d from %p", row, column, window); 371 372 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); 373 if (!fieldSlot) { 374 throwExceptionWithRowCol(env, row, column); 375 return 0; 376 } 377 378 int32_t type = window->getFieldSlotType(fieldSlot); 379 if (type == CursorWindow::FIELD_TYPE_INTEGER) { 380 return window->getFieldSlotValueLong(fieldSlot); 381 } else if (type == CursorWindow::FIELD_TYPE_STRING) { 382 size_t sizeIncludingNull; 383 const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull); 384 return sizeIncludingNull > 1 ? strtoll(value, NULL, 0) : 0L; 385 } else if (type == CursorWindow::FIELD_TYPE_FLOAT) { 386 return jlong(window->getFieldSlotValueDouble(fieldSlot)); 387 } else if (type == CursorWindow::FIELD_TYPE_NULL) { 388 return 0; 389 } else if (type == CursorWindow::FIELD_TYPE_BLOB) { 390 throw_sqlite3_exception(env, "Unable to convert BLOB to long"); 391 return 0; 392 } else { 393 throwUnknownTypeException(env, type); 394 return 0; 395 } 396} 397 398static jdouble nativeGetDouble(JNIEnv* env, jclass clazz, jlong windowPtr, 399 jint row, jint column) { 400 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 401 LOG_WINDOW("Getting double for %d,%d from %p", row, column, window); 402 403 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column); 404 if (!fieldSlot) { 405 throwExceptionWithRowCol(env, row, column); 406 return 0.0; 407 } 408 409 int32_t type = window->getFieldSlotType(fieldSlot); 410 if (type == CursorWindow::FIELD_TYPE_FLOAT) { 411 return window->getFieldSlotValueDouble(fieldSlot); 412 } else if (type == CursorWindow::FIELD_TYPE_STRING) { 413 size_t sizeIncludingNull; 414 const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull); 415 return sizeIncludingNull > 1 ? strtod(value, NULL) : 0.0; 416 } else if (type == CursorWindow::FIELD_TYPE_INTEGER) { 417 return jdouble(window->getFieldSlotValueLong(fieldSlot)); 418 } else if (type == CursorWindow::FIELD_TYPE_NULL) { 419 return 0.0; 420 } else if (type == CursorWindow::FIELD_TYPE_BLOB) { 421 throw_sqlite3_exception(env, "Unable to convert BLOB to double"); 422 return 0.0; 423 } else { 424 throwUnknownTypeException(env, type); 425 return 0.0; 426 } 427} 428 429static jboolean nativePutBlob(JNIEnv* env, jclass clazz, jlong windowPtr, 430 jbyteArray valueObj, jint row, jint column) { 431 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 432 jsize len = env->GetArrayLength(valueObj); 433 434 void* value = env->GetPrimitiveArrayCritical(valueObj, NULL); 435 status_t status = window->putBlob(row, column, value, len); 436 env->ReleasePrimitiveArrayCritical(valueObj, value, JNI_ABORT); 437 438 if (status) { 439 LOG_WINDOW("Failed to put blob. error=%d", status); 440 return false; 441 } 442 443 LOG_WINDOW("%d,%d is BLOB with %u bytes", row, column, len); 444 return true; 445} 446 447static jboolean nativePutString(JNIEnv* env, jclass clazz, jlong windowPtr, 448 jstring valueObj, jint row, jint column) { 449 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 450 451 size_t sizeIncludingNull = env->GetStringUTFLength(valueObj) + 1; 452 const char* valueStr = env->GetStringUTFChars(valueObj, NULL); 453 if (!valueStr) { 454 LOG_WINDOW("value can't be transferred to UTFChars"); 455 return false; 456 } 457 status_t status = window->putString(row, column, valueStr, sizeIncludingNull); 458 env->ReleaseStringUTFChars(valueObj, valueStr); 459 460 if (status) { 461 LOG_WINDOW("Failed to put string. error=%d", status); 462 return false; 463 } 464 465 LOG_WINDOW("%d,%d is TEXT with %u bytes", row, column, sizeIncludingNull); 466 return true; 467} 468 469static jboolean nativePutLong(JNIEnv* env, jclass clazz, jlong windowPtr, 470 jlong value, jint row, jint column) { 471 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 472 status_t status = window->putLong(row, column, value); 473 474 if (status) { 475 LOG_WINDOW("Failed to put long. error=%d", status); 476 return false; 477 } 478 479 LOG_WINDOW("%d,%d is INTEGER 0x%016llx", row, column, value); 480 return true; 481} 482 483static jboolean nativePutDouble(JNIEnv* env, jclass clazz, jlong windowPtr, 484 jdouble value, jint row, jint column) { 485 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 486 status_t status = window->putDouble(row, column, value); 487 488 if (status) { 489 LOG_WINDOW("Failed to put double. error=%d", status); 490 return false; 491 } 492 493 LOG_WINDOW("%d,%d is FLOAT %lf", row, column, value); 494 return true; 495} 496 497static jboolean nativePutNull(JNIEnv* env, jclass clazz, jlong windowPtr, 498 jint row, jint column) { 499 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr); 500 status_t status = window->putNull(row, column); 501 502 if (status) { 503 LOG_WINDOW("Failed to put null. error=%d", status); 504 return false; 505 } 506 507 LOG_WINDOW("%d,%d is NULL", row, column); 508 return true; 509} 510 511static const JNINativeMethod sMethods[] = 512{ 513 /* name, signature, funcPtr */ 514 { "nativeCreate", "(Ljava/lang/String;I)J", 515 (void*)nativeCreate }, 516 { "nativeCreateFromParcel", "(Landroid/os/Parcel;)J", 517 (void*)nativeCreateFromParcel }, 518 { "nativeDispose", "(J)V", 519 (void*)nativeDispose }, 520 { "nativeWriteToParcel", "(JLandroid/os/Parcel;)V", 521 (void*)nativeWriteToParcel }, 522 523 { "nativeGetName", "(J)Ljava/lang/String;", 524 (void*)nativeGetName }, 525 { "nativeGetBlob", "(JII)[B", 526 (void*)nativeGetBlob }, 527 { "nativeGetString", "(JII)Ljava/lang/String;", 528 (void*)nativeGetString }, 529 { "nativeCopyStringToBuffer", "(JIILandroid/database/CharArrayBuffer;)V", 530 (void*)nativeCopyStringToBuffer }, 531 { "nativePutBlob", "(J[BII)Z", 532 (void*)nativePutBlob }, 533 { "nativePutString", "(JLjava/lang/String;II)Z", 534 (void*)nativePutString }, 535 536 // ------- @FastNative below here ---------------------- 537 { "nativeClear", "(J)V", 538 (void*)nativeClear }, 539 { "nativeGetNumRows", "(J)I", 540 (void*)nativeGetNumRows }, 541 { "nativeSetNumColumns", "(JI)Z", 542 (void*)nativeSetNumColumns }, 543 { "nativeAllocRow", "(J)Z", 544 (void*)nativeAllocRow }, 545 { "nativeFreeLastRow", "(J)V", 546 (void*)nativeFreeLastRow }, 547 { "nativeGetType", "(JII)I", 548 (void*)nativeGetType }, 549 { "nativeGetLong", "(JII)J", 550 (void*)nativeGetLong }, 551 { "nativeGetDouble", "(JII)D", 552 (void*)nativeGetDouble }, 553 554 { "nativePutLong", "(JJII)Z", 555 (void*)nativePutLong }, 556 { "nativePutDouble", "(JDII)Z", 557 (void*)nativePutDouble }, 558 { "nativePutNull", "(JII)Z", 559 (void*)nativePutNull }, 560}; 561 562int register_android_database_CursorWindow(JNIEnv* env) 563{ 564 jclass clazz = FindClassOrDie(env, "android/database/CharArrayBuffer"); 565 566 gCharArrayBufferClassInfo.data = GetFieldIDOrDie(env, clazz, "data", "[C"); 567 gCharArrayBufferClassInfo.sizeCopied = GetFieldIDOrDie(env, clazz, "sizeCopied", "I"); 568 569 gEmptyString = MakeGlobalRefOrDie(env, env->NewStringUTF("")); 570 571 return RegisterMethodsOrDie(env, "android/database/CursorWindow", sMethods, NELEM(sMethods)); 572} 573 574} // namespace android 575