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