android_database_CursorWindow.cpp revision f786b6ca5df079f5cc84838251137f9b65ae10c9
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 28#include <stdio.h> 29#include <string.h> 30#include <unistd.h> 31 32#include "binder/CursorWindow.h" 33#include "sqlite3_exception.h" 34#include "android_util_Binder.h" 35 36 37namespace android { 38 39static jfieldID gWindowField; 40static jfieldID gBufferField; 41static jfieldID gSizeCopiedField; 42 43#define GET_WINDOW(env, object) ((CursorWindow *)env->GetIntField(object, gWindowField)) 44#define SET_WINDOW(env, object, window) (env->SetIntField(object, gWindowField, (int)window)) 45#define SET_BUFFER(env, object, buf) (env->SetObjectField(object, gBufferField, buf)) 46#define SET_SIZE_COPIED(env, object, size) (env->SetIntField(object, gSizeCopiedField, size)) 47 48CursorWindow * get_window_from_object(JNIEnv * env, jobject javaWindow) 49{ 50 return GET_WINDOW(env, javaWindow); 51} 52 53static jint native_init_empty(JNIEnv * env, jobject object, jint cursorWindowSize, 54 jboolean localOnly) 55{ 56 uint8_t * data; 57 size_t size; 58 CursorWindow * window; 59 60 window = new CursorWindow(cursorWindowSize); 61 if (!window) { 62 return 1; 63 } 64 if (!window->initBuffer(localOnly)) { 65 delete window; 66 return 1; 67 } 68 69 LOG_WINDOW("native_init_empty: window = %p", window); 70 SET_WINDOW(env, object, window); 71 return 0; 72} 73 74static jint native_init_memory(JNIEnv * env, jobject object, jobject memObj) 75{ 76 sp<IMemory> memory = interface_cast<IMemory>(ibinderForJavaObject(env, memObj)); 77 if (memory == NULL) { 78 jniThrowException(env, "java/lang/IllegalStateException", "Couldn't get native binder"); 79 return 1; 80 } 81 82 CursorWindow * window = new CursorWindow(); 83 if (!window) { 84 return 1; 85 } 86 if (!window->setMemory(memory)) { 87 delete window; 88 return 1; 89 } 90 91 LOG_WINDOW("native_init_memory: numRows = %d, numColumns = %d, window = %p", window->getNumRows(), window->getNumColumns(), window); 92 SET_WINDOW(env, object, window); 93 return 0; 94} 95 96static jobject native_getBinder(JNIEnv * env, jobject object) 97{ 98 CursorWindow * window = GET_WINDOW(env, object); 99 if (window) { 100 sp<IMemory> memory = window->getMemory(); 101 if (memory != NULL) { 102 sp<IBinder> binder = memory->asBinder(); 103 return javaObjectForIBinder(env, binder); 104 } 105 } 106 return NULL; 107} 108 109static void native_clear(JNIEnv * env, jobject object) 110{ 111 CursorWindow * window = GET_WINDOW(env, object); 112LOG_WINDOW("Clearing window %p", window); 113 if (window == NULL) { 114 jniThrowException(env, "java/lang/IllegalStateException", "clear() called after close()"); 115 return; 116 } 117 window->clear(); 118} 119 120static void native_close(JNIEnv * env, jobject object) 121{ 122 CursorWindow * window = GET_WINDOW(env, object); 123 if (window) { 124LOG_WINDOW("Closing window %p", window); 125 delete window; 126 SET_WINDOW(env, object, 0); 127 } 128} 129 130static void throwExceptionWithRowCol(JNIEnv * env, jint row, jint column) 131{ 132 char buf[200]; 133 snprintf(buf, sizeof(buf), "Couldn't read row %d, col %d from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it", 134 row, column); 135 jniThrowException(env, "java/lang/IllegalStateException", buf); 136} 137 138static void throwUnknowTypeException(JNIEnv * env, jint type) 139{ 140 char buf[80]; 141 snprintf(buf, sizeof(buf), "UNKNOWN type %d", type); 142 jniThrowException(env, "java/lang/IllegalStateException", buf); 143} 144 145static jlong getLong_native(JNIEnv * env, jobject object, jint row, jint column) 146{ 147 int32_t err; 148 CursorWindow * window = GET_WINDOW(env, object); 149LOG_WINDOW("Getting long for %d,%d from %p", row, column, window); 150 151 field_slot_t field; 152 err = window->read_field_slot(row, column, &field); 153 if (err != 0) { 154 throwExceptionWithRowCol(env, row, column); 155 return 0; 156 } 157 158 uint8_t type = field.type; 159 if (type == FIELD_TYPE_INTEGER) { 160 int64_t value; 161 if (window->getLong(row, column, &value)) { 162 return value; 163 } 164 return 0; 165 } else if (type == FIELD_TYPE_STRING) { 166 uint32_t size = field.data.buffer.size; 167 if (size > 0) { 168#if WINDOW_STORAGE_UTF8 169 return strtoll((char const *)window->offsetToPtr(field.data.buffer.offset), NULL, 0); 170#else 171 String8 ascii((char16_t *) window->offsetToPtr(field.data.buffer.offset), size / 2); 172 char const * str = ascii.string(); 173 return strtoll(str, NULL, 0); 174#endif 175 } else { 176 return 0; 177 } 178 } else if (type == FIELD_TYPE_FLOAT) { 179 double value; 180 if (window->getDouble(row, column, &value)) { 181 return value; 182 } 183 return 0; 184 } else if (type == FIELD_TYPE_NULL) { 185 return 0; 186 } else if (type == FIELD_TYPE_BLOB) { 187 throw_sqlite3_exception(env, "Unable to convert BLOB to long"); 188 return 0; 189 } else { 190 throwUnknowTypeException(env, type); 191 return 0; 192 } 193} 194 195static jbyteArray getBlob_native(JNIEnv* env, jobject object, jint row, jint column) 196{ 197 int32_t err; 198 CursorWindow * window = GET_WINDOW(env, object); 199LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window); 200 201 field_slot_t field; 202 err = window->read_field_slot(row, column, &field); 203 if (err != 0) { 204 throwExceptionWithRowCol(env, row, column); 205 return NULL; 206 } 207 208 uint8_t type = field.type; 209 if (type == FIELD_TYPE_BLOB || type == FIELD_TYPE_STRING) { 210 jbyteArray byteArray = env->NewByteArray(field.data.buffer.size); 211 if (!byteArray) { 212 throw_sqlite3_exception(env, "Native could not create new byte[]"); 213 return NULL; 214 } 215 env->SetByteArrayRegion(byteArray, 0, field.data.buffer.size, 216 (const jbyte*)window->offsetToPtr(field.data.buffer.offset)); 217 return byteArray; 218 } else if (type == FIELD_TYPE_INTEGER) { 219 throw_sqlite3_exception(env, "INTEGER data in getBlob_native "); 220 } else if (type == FIELD_TYPE_FLOAT) { 221 throw_sqlite3_exception(env, "FLOAT data in getBlob_native "); 222 } else if (type == FIELD_TYPE_NULL) { 223 // do nothing 224 } else { 225 throwUnknowTypeException(env, type); 226 } 227 return NULL; 228} 229 230static jstring getString_native(JNIEnv* env, jobject object, jint row, jint column) 231{ 232 int32_t err; 233 CursorWindow * window = GET_WINDOW(env, object); 234LOG_WINDOW("Getting string for %d,%d from %p", row, column, window); 235 236 field_slot_t field; 237 err = window->read_field_slot(row, column, &field); 238 if (err != 0) { 239 throwExceptionWithRowCol(env, row, column); 240 return NULL; 241 } 242 243 uint8_t type = field.type; 244 if (type == FIELD_TYPE_STRING) { 245 uint32_t size = field.data.buffer.size; 246 if (size > 0) { 247#if WINDOW_STORAGE_UTF8 248 // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string 249 String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1); 250 return env->NewString((jchar const *)utf16.string(), utf16.size()); 251#else 252 return env->NewString((jchar const *)window->offsetToPtr(field.data.buffer.offset), size / 2); 253#endif 254 } else { 255 return env->NewStringUTF(""); 256 } 257 } else if (type == FIELD_TYPE_INTEGER) { 258 int64_t value; 259 if (window->getLong(row, column, &value)) { 260 char buf[32]; 261 snprintf(buf, sizeof(buf), "%lld", value); 262 return env->NewStringUTF(buf); 263 } 264 return NULL; 265 } else if (type == FIELD_TYPE_FLOAT) { 266 double value; 267 if (window->getDouble(row, column, &value)) { 268 char buf[32]; 269 snprintf(buf, sizeof(buf), "%g", value); 270 return env->NewStringUTF(buf); 271 } 272 return NULL; 273 } else if (type == FIELD_TYPE_NULL) { 274 return NULL; 275 } else if (type == FIELD_TYPE_BLOB) { 276 throw_sqlite3_exception(env, "Unable to convert BLOB to string"); 277 return NULL; 278 } else { 279 throwUnknowTypeException(env, type); 280 return NULL; 281 } 282} 283 284/** 285 * Use this only to convert characters that are known to be within the 286 * 0-127 range for direct conversion to UTF-16 287 */ 288static jint charToJchar(const char* src, jchar* dst, jint bufferSize) 289{ 290 int32_t len = strlen(src); 291 292 if (bufferSize < len) { 293 len = bufferSize; 294 } 295 296 for (int i = 0; i < len; i++) { 297 *dst++ = (*src++ & 0x7F); 298 } 299 return len; 300} 301 302static jcharArray copyStringToBuffer_native(JNIEnv* env, jobject object, jint row, 303 jint column, jint bufferSize, jobject buf) 304{ 305 int32_t err; 306 CursorWindow * window = GET_WINDOW(env, object); 307LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); 308 309 field_slot_t field; 310 err = window->read_field_slot(row, column, &field); 311 if (err != 0) { 312 jniThrowException(env, "java/lang/IllegalStateException", "Unable to get field slot"); 313 return NULL; 314 } 315 316 jcharArray buffer = (jcharArray)env->GetObjectField(buf, gBufferField); 317 if (buffer == NULL) { 318 jniThrowException(env, "java/lang/IllegalStateException", "buf should not be null"); 319 return NULL; 320 } 321 jchar* dst = env->GetCharArrayElements(buffer, NULL); 322 uint8_t type = field.type; 323 uint32_t sizeCopied = 0; 324 jcharArray newArray = NULL; 325 if (type == FIELD_TYPE_STRING) { 326 uint32_t size = field.data.buffer.size; 327 if (size > 0) { 328#if WINDOW_STORAGE_UTF8 329 // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string 330 String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1); 331 int32_t strSize = utf16.size(); 332 if (strSize > bufferSize || dst == NULL) { 333 newArray = env->NewCharArray(strSize); 334 env->SetCharArrayRegion(newArray, 0, strSize, (jchar const *)utf16.string()); 335 } else { 336 memcpy(dst, (jchar const *)utf16.string(), strSize * 2); 337 } 338 sizeCopied = strSize; 339#else 340 sizeCopied = size/2 + size % 2; 341 if (size > bufferSize * 2 || dst == NULL) { 342 newArray = env->NewCharArray(sizeCopied); 343 memcpy(newArray, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size); 344 } else { 345 memcpy(dst, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size); 346 } 347#endif 348 } 349 } else if (type == FIELD_TYPE_INTEGER) { 350 int64_t value; 351 if (window->getLong(row, column, &value)) { 352 char buf[32]; 353 int len; 354 snprintf(buf, sizeof(buf), "%lld", value); 355 sizeCopied = charToJchar(buf, dst, bufferSize); 356 } 357 } else if (type == FIELD_TYPE_FLOAT) { 358 double value; 359 if (window->getDouble(row, column, &value)) { 360 char tempbuf[32]; 361 snprintf(tempbuf, sizeof(tempbuf), "%g", value); 362 sizeCopied = charToJchar(tempbuf, dst, bufferSize); 363 } 364 } else if (type == FIELD_TYPE_NULL) { 365 } else if (type == FIELD_TYPE_BLOB) { 366 throw_sqlite3_exception(env, "Unable to convert BLOB to string"); 367 } else { 368 LOGE("Unknown field type %d", type); 369 throw_sqlite3_exception(env, "UNKNOWN type in copyStringToBuffer_native()"); 370 } 371 SET_SIZE_COPIED(env, buf, sizeCopied); 372 env->ReleaseCharArrayElements(buffer, dst, JNI_OK); 373 return newArray; 374} 375 376static jdouble getDouble_native(JNIEnv* env, jobject object, jint row, jint column) 377{ 378 int32_t err; 379 CursorWindow * window = GET_WINDOW(env, object); 380LOG_WINDOW("Getting double for %d,%d from %p", row, column, window); 381 382 field_slot_t field; 383 err = window->read_field_slot(row, column, &field); 384 if (err != 0) { 385 throwExceptionWithRowCol(env, row, column); 386 return 0.0; 387 } 388 389 uint8_t type = field.type; 390 if (type == FIELD_TYPE_FLOAT) { 391 double value; 392 if (window->getDouble(row, column, &value)) { 393 return value; 394 } 395 return 0.0; 396 } else if (type == FIELD_TYPE_STRING) { 397 uint32_t size = field.data.buffer.size; 398 if (size > 0) { 399#if WINDOW_STORAGE_UTF8 400 return strtod((char const *)window->offsetToPtr(field.data.buffer.offset), NULL); 401#else 402 String8 ascii((char16_t *) window->offsetToPtr(field.data.buffer.offset), size / 2); 403 char const * str = ascii.string(); 404 return strtod(str, NULL); 405#endif 406 } else { 407 return 0.0; 408 } 409 } else if (type == FIELD_TYPE_INTEGER) { 410 int64_t value; 411 if (window->getLong(row, column, &value)) { 412 return (double) value; 413 } 414 return 0.0; 415 } else if (type == FIELD_TYPE_NULL) { 416 return 0.0; 417 } else if (type == FIELD_TYPE_BLOB) { 418 throw_sqlite3_exception(env, "Unable to convert BLOB to double"); 419 return 0.0; 420 } else { 421 throwUnknowTypeException(env, type); 422 return 0.0; 423 } 424} 425 426bool isNull_native(CursorWindow *window, jint row, jint column) 427{ 428 LOG_WINDOW("Checking for NULL at %d,%d from %p", row, column, window); 429 430 bool isNull; 431 if (window->getNull(row, column, &isNull)) { 432 return isNull; 433 } 434 435 //TODO throw execption? 436 return true; 437} 438 439static jint getNumRows(JNIEnv * env, jobject object) 440{ 441 CursorWindow * window = GET_WINDOW(env, object); 442 return window->getNumRows(); 443} 444 445static jboolean setNumColumns(JNIEnv * env, jobject object, jint columnNum) 446{ 447 CursorWindow * window = GET_WINDOW(env, object); 448 return window->setNumColumns(columnNum); 449} 450 451static jboolean allocRow(JNIEnv * env, jobject object) 452{ 453 CursorWindow * window = GET_WINDOW(env, object); 454 return window->allocRow() != NULL; 455} 456 457static jboolean putBlob_native(JNIEnv * env, jobject object, jbyteArray value, jint row, jint col) 458{ 459 CursorWindow * window = GET_WINDOW(env, object); 460 if (!value) { 461 LOG_WINDOW("How did a null value send to here"); 462 return false; 463 } 464 field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col); 465 if (fieldSlot == NULL) { 466 LOG_WINDOW(" getFieldSlotWithCheck error "); 467 return false; 468 } 469 470 jint len = env->GetArrayLength(value); 471 int offset = window->alloc(len); 472 if (!offset) { 473 LOG_WINDOW("Failed allocating %u bytes", len); 474 return false; 475 } 476 jbyte * bytes = env->GetByteArrayElements(value, NULL); 477 window->copyIn(offset, (uint8_t const *)bytes, len); 478 479 // This must be updated after the call to alloc(), since that 480 // may move the field around in the window 481 fieldSlot->type = FIELD_TYPE_BLOB; 482 fieldSlot->data.buffer.offset = offset; 483 fieldSlot->data.buffer.size = len; 484 env->ReleaseByteArrayElements(value, bytes, JNI_ABORT); 485 LOG_WINDOW("%d,%d is BLOB with %u bytes @ %d", row, col, len, offset); 486 return true; 487} 488 489static jboolean putString_native(JNIEnv * env, jobject object, jstring value, jint row, jint col) 490{ 491 CursorWindow * window = GET_WINDOW(env, object); 492 if (!value) { 493 LOG_WINDOW("How did a null value send to here"); 494 return false; 495 } 496 field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col); 497 if (fieldSlot == NULL) { 498 LOG_WINDOW(" getFieldSlotWithCheck error "); 499 return false; 500 } 501 502#if WINDOW_STORAGE_UTF8 503 int len = env->GetStringUTFLength(value) + 1; 504 char const * valStr = env->GetStringUTFChars(value, NULL); 505#else 506 int len = env->GetStringLength(value); 507 // GetStringLength return number of chars and one char takes 2 bytes 508 len *= 2; 509 const jchar* valStr = env->GetStringChars(value, NULL); 510#endif 511 if (!valStr) { 512 LOG_WINDOW("value can't be transfer to UTFChars"); 513 return false; 514 } 515 516 int offset = window->alloc(len); 517 if (!offset) { 518 LOG_WINDOW("Failed allocating %u bytes", len); 519#if WINDOW_STORAGE_UTF8 520 env->ReleaseStringUTFChars(value, valStr); 521#else 522 env->ReleaseStringChars(value, valStr); 523#endif 524 return false; 525 } 526 527 window->copyIn(offset, (uint8_t const *)valStr, len); 528 529 // This must be updated after the call to alloc(), since that 530 // may move the field around in the window 531 fieldSlot->type = FIELD_TYPE_STRING; 532 fieldSlot->data.buffer.offset = offset; 533 fieldSlot->data.buffer.size = len; 534 535 LOG_WINDOW("%d,%d is TEXT with %u bytes @ %d", row, col, len, offset); 536#if WINDOW_STORAGE_UTF8 537 env->ReleaseStringUTFChars(value, valStr); 538#else 539 env->ReleaseStringChars(value, valStr); 540#endif 541 542 return true; 543} 544 545static jboolean putLong_native(JNIEnv * env, jobject object, jlong value, jint row, jint col) 546{ 547 CursorWindow * window = GET_WINDOW(env, object); 548 if (!window->putLong(row, col, value)) { 549 LOG_WINDOW(" getFieldSlotWithCheck error "); 550 return false; 551 } 552 553 LOG_WINDOW("%d,%d is INTEGER 0x%016llx", row, col, value); 554 555 return true; 556} 557 558static jboolean putDouble_native(JNIEnv * env, jobject object, jdouble value, jint row, jint col) 559{ 560 CursorWindow * window = GET_WINDOW(env, object); 561 if (!window->putDouble(row, col, value)) { 562 LOG_WINDOW(" getFieldSlotWithCheck error "); 563 return false; 564 } 565 566 LOG_WINDOW("%d,%d is FLOAT %lf", row, col, value); 567 568 return true; 569} 570 571static jboolean putNull_native(JNIEnv * env, jobject object, jint row, jint col) 572{ 573 CursorWindow * window = GET_WINDOW(env, object); 574 if (!window->putNull(row, col)) { 575 LOG_WINDOW(" getFieldSlotWithCheck error "); 576 return false; 577 } 578 579 LOG_WINDOW("%d,%d is NULL", row, col); 580 581 return true; 582} 583 584// free the last row 585static void freeLastRow(JNIEnv * env, jobject object) { 586 CursorWindow * window = GET_WINDOW(env, object); 587 window->freeLastRow(); 588} 589 590static jint getType_native(JNIEnv* env, jobject object, jint row, jint column) 591{ 592 int32_t err; 593 CursorWindow * window = GET_WINDOW(env, object); 594 LOG_WINDOW("returning column type affinity for %d,%d from %p", row, column, window); 595 596 if (isNull_native(window, row, column)) { 597 return FIELD_TYPE_NULL; 598 } 599 600 field_slot_t field; 601 err = window->read_field_slot(row, column, &field); 602 if (err != 0) { 603 throwExceptionWithRowCol(env, row, column); 604 return NULL; 605 } 606 607 return field.type; 608} 609 610static JNINativeMethod sMethods[] = 611{ 612 /* name, signature, funcPtr */ 613 {"native_init", "(IZ)I", (void *)native_init_empty}, 614 {"native_init", "(Landroid/os/IBinder;)I", (void *)native_init_memory}, 615 {"native_getBinder", "()Landroid/os/IBinder;", (void *)native_getBinder}, 616 {"native_clear", "()V", (void *)native_clear}, 617 {"close_native", "()V", (void *)native_close}, 618 {"getLong_native", "(II)J", (void *)getLong_native}, 619 {"getBlob_native", "(II)[B", (void *)getBlob_native}, 620 {"getString_native", "(II)Ljava/lang/String;", (void *)getString_native}, 621 {"copyStringToBuffer_native", "(IIILandroid/database/CharArrayBuffer;)[C", (void *)copyStringToBuffer_native}, 622 {"getDouble_native", "(II)D", (void *)getDouble_native}, 623 {"getNumRows_native", "()I", (void *)getNumRows}, 624 {"setNumColumns_native", "(I)Z", (void *)setNumColumns}, 625 {"allocRow_native", "()Z", (void *)allocRow}, 626 {"putBlob_native", "([BII)Z", (void *)putBlob_native}, 627 {"putString_native", "(Ljava/lang/String;II)Z", (void *)putString_native}, 628 {"putLong_native", "(JII)Z", (void *)putLong_native}, 629 {"putDouble_native", "(DII)Z", (void *)putDouble_native}, 630 {"freeLastRow_native", "()V", (void *)freeLastRow}, 631 {"putNull_native", "(II)Z", (void *)putNull_native}, 632 {"getType_native", "(II)I", (void *)getType_native}, 633}; 634 635int register_android_database_CursorWindow(JNIEnv * env) 636{ 637 jclass clazz; 638 639 clazz = env->FindClass("android/database/CursorWindow"); 640 if (clazz == NULL) { 641 LOGE("Can't find android/database/CursorWindow"); 642 return -1; 643 } 644 645 gWindowField = env->GetFieldID(clazz, "nWindow", "I"); 646 647 if (gWindowField == NULL) { 648 LOGE("Error locating fields"); 649 return -1; 650 } 651 652 clazz = env->FindClass("android/database/CharArrayBuffer"); 653 if (clazz == NULL) { 654 LOGE("Can't find android/database/CharArrayBuffer"); 655 return -1; 656 } 657 658 gBufferField = env->GetFieldID(clazz, "data", "[C"); 659 660 if (gBufferField == NULL) { 661 LOGE("Error locating fields data in CharArrayBuffer"); 662 return -1; 663 } 664 665 gSizeCopiedField = env->GetFieldID(clazz, "sizeCopied", "I"); 666 667 if (gSizeCopiedField == NULL) { 668 LOGE("Error locating fields sizeCopied in CharArrayBuffer"); 669 return -1; 670 } 671 672 return AndroidRuntime::registerNativeMethods(env, "android/database/CursorWindow", 673 sMethods, NELEM(sMethods)); 674} 675 676} // namespace android 677