OSDN Git Service

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