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