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
throwExceptionWithRowCol(JNIEnv * env,jint row,jint column)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
throwUnknownTypeException(JNIEnv * env,jint type)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
nativeCreate(JNIEnv * env,jclass clazz,jstring nameObj,jint cursorWindowSize)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
nativeCreateFromParcel(JNIEnv * env,jclass clazz,jobject parcelObj)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
nativeDispose(JNIEnv * env,jclass clazz,jlong windowPtr)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
nativeGetName(JNIEnv * env,jclass clazz,jlong windowPtr)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
nativeWriteToParcel(JNIEnv * env,jclass clazz,jlong windowPtr,jobject parcelObj)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
nativeClear(JNIEnv * env,jclass clazz,jlong windowPtr)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
nativeGetNumRows(JNIEnv * env,jclass clazz,jlong windowPtr)130 static jint nativeGetNumRows(JNIEnv* env, jclass clazz, jlong windowPtr) {
131 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
132 return window->getNumRows();
133 }
134
nativeSetNumColumns(JNIEnv * env,jclass clazz,jlong windowPtr,jint columnNum)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
nativeAllocRow(JNIEnv * env,jclass clazz,jlong windowPtr)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
nativeFreeLastRow(JNIEnv * env,jclass clazz,jlong windowPtr)148 static void nativeFreeLastRow(JNIEnv* env, jclass clazz, jlong windowPtr) {
149 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
150 window->freeLastRow();
151 }
152
nativeGetType(JNIEnv * env,jclass clazz,jlong windowPtr,jint row,jint column)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
nativeGetBlob(JNIEnv * env,jclass clazz,jlong windowPtr,jint row,jint column)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
nativeGetString(JNIEnv * env,jclass clazz,jlong windowPtr,jint row,jint column)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
allocCharArrayBuffer(JNIEnv * env,jobject bufferObj,size_t size)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
fillCharArrayBufferUTF(JNIEnv * env,jobject bufferObj,const char * str,size_t len)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
clearCharArrayBuffer(JNIEnv * env,jobject bufferObj)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
nativeCopyStringToBuffer(JNIEnv * env,jclass clazz,jlong windowPtr,jint row,jint column,jobject bufferObj)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
nativeGetLong(JNIEnv * env,jclass clazz,jlong windowPtr,jint row,jint column)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
nativeGetDouble(JNIEnv * env,jclass clazz,jlong windowPtr,jint row,jint column)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
nativePutBlob(JNIEnv * env,jclass clazz,jlong windowPtr,jbyteArray valueObj,jint row,jint column)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
nativePutString(JNIEnv * env,jclass clazz,jlong windowPtr,jstring valueObj,jint row,jint column)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
nativePutLong(JNIEnv * env,jclass clazz,jlong windowPtr,jlong value,jint row,jint column)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
nativePutDouble(JNIEnv * env,jclass clazz,jlong windowPtr,jdouble value,jint row,jint column)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
nativePutNull(JNIEnv * env,jclass clazz,jlong windowPtr,jint row,jint column)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
register_android_database_CursorWindow(JNIEnv * env)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