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