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