• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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