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