blob: af6cc72b588b6e7908ee029da5a522e9d2d484e3 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
Mark Salyzyn85394032014-04-16 10:28:37 -070020#include <inttypes.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021#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>
Jeff Brown3bc6bbc2011-10-06 13:11:04 -070028#include <utils/Unicode.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029
30#include <stdio.h>
31#include <string.h>
32#include <unistd.h>
33
Mathias Agopian49d2b182012-02-27 18:11:20 -080034#include <androidfw/CursorWindow.h>
Jeff Sharkeyd84e1ce2012-03-06 18:26:19 -080035#include "android_os_Parcel.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036#include "android_util_Binder.h"
Jeff Browne5360fb2011-10-31 17:48:13 -070037#include "android_database_SQLiteCommon.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039namespace android {
40
Jeff Brown3bc6bbc2011-10-06 13:11:04 -070041static struct {
42 jfieldID data;
43 jfieldID sizeCopied;
44} gCharArrayBufferClassInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045
Jeff Brown3bc6bbc2011-10-06 13:11:04 -070046static jstring gEmptyString;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047
Jeff Brown3bc6bbc2011-10-06 13:11:04 -070048static void throwExceptionWithRowCol(JNIEnv* env, jint row, jint column) {
49 String8 msg;
50 msg.appendFormat("Couldn't read row %d, col %d from CursorWindow. "
51 "Make sure the Cursor is initialized correctly before accessing data from it.",
52 row, column);
53 jniThrowException(env, "java/lang/IllegalStateException", msg.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054}
55
Jeff Brown3bc6bbc2011-10-06 13:11:04 -070056static void throwUnknownTypeException(JNIEnv * env, jint type) {
57 String8 msg;
58 msg.appendFormat("UNKNOWN type %d", type);
59 jniThrowException(env, "java/lang/IllegalStateException", msg.string());
60}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061
Ashok Bhat738702d2014-01-02 13:42:56 +000062static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring nameObj, jint cursorWindowSize) {
Jeff Brown0cde89f2011-10-10 14:50:10 -070063 String8 name;
Jeff Brown5a05c232012-01-12 12:04:22 -080064 const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
65 name.setTo(nameStr);
66 env->ReleaseStringUTFChars(nameObj, nameStr);
Jeff Brown0cde89f2011-10-10 14:50:10 -070067
68 CursorWindow* window;
Jeff Brown5e5d6d82011-10-12 15:41:34 -070069 status_t status = CursorWindow::create(name, cursorWindowSize, &window);
Jeff Brown0cde89f2011-10-10 14:50:10 -070070 if (status || !window) {
Steve Block3762c312012-01-06 19:20:56 +000071 ALOGE("Could not allocate CursorWindow '%s' of size %d due to error %d.",
Jeff Brown0cde89f2011-10-10 14:50:10 -070072 name.string(), cursorWindowSize, status);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -070073 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 }
75
Jeff Brown3bc6bbc2011-10-06 13:11:04 -070076 LOG_WINDOW("nativeInitializeEmpty: window = %p", window);
Ashok Bhat738702d2014-01-02 13:42:56 +000077 return reinterpret_cast<jlong>(window);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078}
79
Ashok Bhat738702d2014-01-02 13:42:56 +000080static jlong nativeCreateFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) {
Jeff Brown0cde89f2011-10-10 14:50:10 -070081 Parcel* parcel = parcelForJavaObject(env, parcelObj);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082
Jeff Brown0cde89f2011-10-10 14:50:10 -070083 CursorWindow* window;
84 status_t status = CursorWindow::createFromParcel(parcel, &window);
85 if (status || !window) {
Steve Block3762c312012-01-06 19:20:56 +000086 ALOGE("Could not create CursorWindow from Parcel due to error %d.", status);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -070087 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088 }
89
Jeff Brown3bc6bbc2011-10-06 13:11:04 -070090 LOG_WINDOW("nativeInitializeFromBinder: numRows = %d, numColumns = %d, window = %p",
91 window->getNumRows(), window->getNumColumns(), window);
Ashok Bhat738702d2014-01-02 13:42:56 +000092 return reinterpret_cast<jlong>(window);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093}
94
Ashok Bhat738702d2014-01-02 13:42:56 +000095static void nativeDispose(JNIEnv* env, jclass clazz, jlong windowPtr) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -070096 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
97 if (window) {
98 LOG_WINDOW("Closing window %p", window);
99 delete window;
100 }
101}
102
Ashok Bhat738702d2014-01-02 13:42:56 +0000103static jstring nativeGetName(JNIEnv* env, jclass clazz, jlong windowPtr) {
Jeff Brown650de3d2011-10-27 14:52:28 -0700104 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
105 return env->NewStringUTF(window->name().string());
106}
107
Ashok Bhat738702d2014-01-02 13:42:56 +0000108static void nativeWriteToParcel(JNIEnv * env, jclass clazz, jlong windowPtr,
Jeff Brown0cde89f2011-10-10 14:50:10 -0700109 jobject parcelObj) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700110 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700111 Parcel* parcel = parcelForJavaObject(env, parcelObj);
112
113 status_t status = window->writeToParcel(parcel);
114 if (status) {
115 String8 msg;
116 msg.appendFormat("Could not write CursorWindow to Parcel due to error %d.", status);
117 jniThrowRuntimeException(env, msg.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119}
120
Ashok Bhat738702d2014-01-02 13:42:56 +0000121static void nativeClear(JNIEnv * env, jclass clazz, jlong windowPtr) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700122 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
123 LOG_WINDOW("Clearing window %p", window);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700124 status_t status = window->clear();
125 if (status) {
126 LOG_WINDOW("Could not clear window. error=%d", status);
127 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128}
129
Ashok Bhat738702d2014-01-02 13:42:56 +0000130static jint nativeGetNumRows(JNIEnv* env, jclass clazz, jlong windowPtr) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700131 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
132 return window->getNumRows();
133}
134
Ashok Bhat738702d2014-01-02 13:42:56 +0000135static jboolean nativeSetNumColumns(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700136 jint columnNum) {
137 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700138 status_t status = window->setNumColumns(columnNum);
139 return status == OK;
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700140}
141
Ashok Bhat738702d2014-01-02 13:42:56 +0000142static jboolean nativeAllocRow(JNIEnv* env, jclass clazz, jlong windowPtr) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700143 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700144 status_t status = window->allocRow();
145 return status == OK;
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700146}
147
Ashok Bhat738702d2014-01-02 13:42:56 +0000148static void nativeFreeLastRow(JNIEnv* env, jclass clazz, jlong windowPtr) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700149 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
150 window->freeLastRow();
151}
152
Ashok Bhat738702d2014-01-02 13:42:56 +0000153static jint nativeGetType(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700154 jint row, jint column) {
155 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
156 LOG_WINDOW("returning column type affinity for %d,%d from %p", row, column, window);
157
Jeff Brown0cde89f2011-10-10 14:50:10 -0700158 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700159 if (!fieldSlot) {
Jeff Brownaa32c302011-10-07 13:15:59 -0700160 // FIXME: This is really broken but we have CTS tests that depend
161 // on this legacy behavior.
162 //throwExceptionWithRowCol(env, row, column);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700163 return CursorWindow::FIELD_TYPE_NULL;
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700164 }
Jeff Brown0cde89f2011-10-10 14:50:10 -0700165 return window->getFieldSlotType(fieldSlot);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700166}
167
Ashok Bhat738702d2014-01-02 13:42:56 +0000168static jbyteArray nativeGetBlob(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700169 jint row, jint column) {
170 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
171 LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window);
172
Jeff Brown0cde89f2011-10-10 14:50:10 -0700173 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700174 if (!fieldSlot) {
175 throwExceptionWithRowCol(env, row, column);
176 return NULL;
177 }
178
Jeff Brown0cde89f2011-10-10 14:50:10 -0700179 int32_t type = window->getFieldSlotType(fieldSlot);
180 if (type == CursorWindow::FIELD_TYPE_BLOB || type == CursorWindow::FIELD_TYPE_STRING) {
181 size_t size;
182 const void* value = window->getFieldSlotValueBlob(fieldSlot, &size);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700183 jbyteArray byteArray = env->NewByteArray(size);
184 if (!byteArray) {
185 env->ExceptionClear();
186 throw_sqlite3_exception(env, "Native could not create new byte[]");
187 return NULL;
188 }
Jeff Brown0cde89f2011-10-10 14:50:10 -0700189 env->SetByteArrayRegion(byteArray, 0, size, static_cast<const jbyte*>(value));
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700190 return byteArray;
Jeff Brown0cde89f2011-10-10 14:50:10 -0700191 } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700192 throw_sqlite3_exception(env, "INTEGER data in nativeGetBlob ");
Jeff Brown0cde89f2011-10-10 14:50:10 -0700193 } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700194 throw_sqlite3_exception(env, "FLOAT data in nativeGetBlob ");
Jeff Brown0cde89f2011-10-10 14:50:10 -0700195 } else if (type == CursorWindow::FIELD_TYPE_NULL) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700196 // do nothing
197 } else {
198 throwUnknownTypeException(env, type);
199 }
200 return NULL;
201}
202
Ashok Bhat738702d2014-01-02 13:42:56 +0000203static jstring nativeGetString(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700204 jint row, jint column) {
205 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
206 LOG_WINDOW("Getting string for %d,%d from %p", row, column, window);
207
Jeff Brown0cde89f2011-10-10 14:50:10 -0700208 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700209 if (!fieldSlot) {
210 throwExceptionWithRowCol(env, row, column);
211 return NULL;
212 }
213
Jeff Brown0cde89f2011-10-10 14:50:10 -0700214 int32_t type = window->getFieldSlotType(fieldSlot);
215 if (type == CursorWindow::FIELD_TYPE_STRING) {
216 size_t sizeIncludingNull;
217 const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
218 if (sizeIncludingNull <= 1) {
Jeff Brown715311f2011-10-07 14:17:09 -0700219 return gEmptyString;
220 }
221 // Convert to UTF-16 here instead of calling NewStringUTF. NewStringUTF
222 // doesn't like UTF-8 strings with high codepoints. It actually expects
223 // Modified UTF-8 with encoded surrogate pairs.
Jeff Brown0cde89f2011-10-10 14:50:10 -0700224 String16 utf16(value, sizeIncludingNull - 1);
Jeff Brown715311f2011-10-07 14:17:09 -0700225 return env->NewString(reinterpret_cast<const jchar*>(utf16.string()), utf16.size());
Jeff Brown0cde89f2011-10-10 14:50:10 -0700226 } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700227 int64_t value = window->getFieldSlotValueLong(fieldSlot);
228 char buf[32];
Mark Salyzyn85394032014-04-16 10:28:37 -0700229 snprintf(buf, sizeof(buf), "%" PRId64, value);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700230 return env->NewStringUTF(buf);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700231 } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700232 double value = window->getFieldSlotValueDouble(fieldSlot);
233 char buf[32];
234 snprintf(buf, sizeof(buf), "%g", value);
235 return env->NewStringUTF(buf);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700236 } else if (type == CursorWindow::FIELD_TYPE_NULL) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700237 return NULL;
Jeff Brown0cde89f2011-10-10 14:50:10 -0700238 } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700239 throw_sqlite3_exception(env, "Unable to convert BLOB to string");
240 return NULL;
241 } else {
242 throwUnknownTypeException(env, type);
243 return NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 }
245}
246
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700247static jcharArray allocCharArrayBuffer(JNIEnv* env, jobject bufferObj, size_t size) {
248 jcharArray dataObj = jcharArray(env->GetObjectField(bufferObj,
249 gCharArrayBufferClassInfo.data));
250 if (dataObj && size) {
251 jsize capacity = env->GetArrayLength(dataObj);
252 if (size_t(capacity) < size) {
253 env->DeleteLocalRef(dataObj);
254 dataObj = NULL;
255 }
256 }
257 if (!dataObj) {
258 jsize capacity = size;
259 if (capacity < 64) {
260 capacity = 64;
261 }
262 dataObj = env->NewCharArray(capacity); // might throw OOM
263 if (dataObj) {
264 env->SetObjectField(bufferObj, gCharArrayBufferClassInfo.data, dataObj);
265 }
266 }
267 return dataObj;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268}
269
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700270static void fillCharArrayBufferUTF(JNIEnv* env, jobject bufferObj,
271 const char* str, size_t len) {
272 ssize_t size = utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str), len);
273 if (size < 0) {
274 size = 0; // invalid UTF8 string
275 }
276 jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, size);
277 if (dataObj) {
278 if (size) {
279 jchar* data = static_cast<jchar*>(env->GetPrimitiveArrayCritical(dataObj, NULL));
Jeff Brownd0ff68d2011-10-07 13:28:18 -0700280 utf8_to_utf16_no_null_terminator(reinterpret_cast<const uint8_t*>(str), len,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700281 reinterpret_cast<char16_t*>(data));
282 env->ReleasePrimitiveArrayCritical(dataObj, data, 0);
283 }
284 env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, size);
285 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286}
287
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700288static void clearCharArrayBuffer(JNIEnv* env, jobject bufferObj) {
289 jcharArray dataObj = allocCharArrayBuffer(env, bufferObj, 0);
290 if (dataObj) {
291 env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, 0);
292 }
293}
294
Ashok Bhat738702d2014-01-02 13:42:56 +0000295static void nativeCopyStringToBuffer(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700296 jint row, jint column, jobject bufferObj) {
297 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
298 LOG_WINDOW("Copying string for %d,%d from %p", row, column, window);
299
Jeff Brown0cde89f2011-10-10 14:50:10 -0700300 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700301 if (!fieldSlot) {
302 throwExceptionWithRowCol(env, row, column);
303 return;
304 }
305
Jeff Brown0cde89f2011-10-10 14:50:10 -0700306 int32_t type = window->getFieldSlotType(fieldSlot);
307 if (type == CursorWindow::FIELD_TYPE_STRING) {
308 size_t sizeIncludingNull;
309 const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
310 if (sizeIncludingNull > 1) {
311 fillCharArrayBufferUTF(env, bufferObj, value, sizeIncludingNull - 1);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700312 } else {
313 clearCharArrayBuffer(env, bufferObj);
314 }
Jeff Brown0cde89f2011-10-10 14:50:10 -0700315 } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700316 int64_t value = window->getFieldSlotValueLong(fieldSlot);
317 char buf[32];
Mark Salyzyn85394032014-04-16 10:28:37 -0700318 snprintf(buf, sizeof(buf), "%" PRId64, value);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700319 fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf));
Jeff Brown0cde89f2011-10-10 14:50:10 -0700320 } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700321 double value = window->getFieldSlotValueDouble(fieldSlot);
322 char buf[32];
323 snprintf(buf, sizeof(buf), "%g", value);
324 fillCharArrayBufferUTF(env, bufferObj, buf, strlen(buf));
Jeff Brown0cde89f2011-10-10 14:50:10 -0700325 } else if (type == CursorWindow::FIELD_TYPE_NULL) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700326 clearCharArrayBuffer(env, bufferObj);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700327 } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700328 throw_sqlite3_exception(env, "Unable to convert BLOB to string");
329 } else {
330 throwUnknownTypeException(env, type);
331 }
332}
333
Ashok Bhat738702d2014-01-02 13:42:56 +0000334static jlong nativeGetLong(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700335 jint row, jint column) {
336 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
337 LOG_WINDOW("Getting long for %d,%d from %p", row, column, window);
338
Jeff Brown0cde89f2011-10-10 14:50:10 -0700339 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700340 if (!fieldSlot) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 throwExceptionWithRowCol(env, row, column);
342 return 0;
343 }
344
Jeff Brown0cde89f2011-10-10 14:50:10 -0700345 int32_t type = window->getFieldSlotType(fieldSlot);
346 if (type == CursorWindow::FIELD_TYPE_INTEGER) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700347 return window->getFieldSlotValueLong(fieldSlot);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700348 } else if (type == CursorWindow::FIELD_TYPE_STRING) {
349 size_t sizeIncludingNull;
350 const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
351 return sizeIncludingNull > 1 ? strtoll(value, NULL, 0) : 0L;
352 } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700353 return jlong(window->getFieldSlotValueDouble(fieldSlot));
Jeff Brown0cde89f2011-10-10 14:50:10 -0700354 } else if (type == CursorWindow::FIELD_TYPE_NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 return 0;
Jeff Brown0cde89f2011-10-10 14:50:10 -0700356 } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 throw_sqlite3_exception(env, "Unable to convert BLOB to long");
358 return 0;
359 } else {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700360 throwUnknownTypeException(env, type);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 return 0;
362 }
363}
364
Ashok Bhat738702d2014-01-02 13:42:56 +0000365static jdouble nativeGetDouble(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700366 jint row, jint column) {
367 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
368 LOG_WINDOW("Getting double for %d,%d from %p", row, column, window);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369
Jeff Brown0cde89f2011-10-10 14:50:10 -0700370 CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700371 if (!fieldSlot) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 throwExceptionWithRowCol(env, row, column);
373 return 0.0;
374 }
375
Jeff Brown0cde89f2011-10-10 14:50:10 -0700376 int32_t type = window->getFieldSlotType(fieldSlot);
377 if (type == CursorWindow::FIELD_TYPE_FLOAT) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700378 return window->getFieldSlotValueDouble(fieldSlot);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700379 } else if (type == CursorWindow::FIELD_TYPE_STRING) {
380 size_t sizeIncludingNull;
381 const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
382 return sizeIncludingNull > 1 ? strtod(value, NULL) : 0.0;
383 } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700384 return jdouble(window->getFieldSlotValueLong(fieldSlot));
Jeff Brown0cde89f2011-10-10 14:50:10 -0700385 } else if (type == CursorWindow::FIELD_TYPE_NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 return 0.0;
Jeff Brown0cde89f2011-10-10 14:50:10 -0700387 } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 throw_sqlite3_exception(env, "Unable to convert BLOB to double");
389 return 0.0;
390 } else {
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700391 throwUnknownTypeException(env, type);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 return 0.0;
393 }
394}
395
Ashok Bhat738702d2014-01-02 13:42:56 +0000396static jboolean nativePutBlob(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700397 jbyteArray valueObj, jint row, jint column) {
398 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700399 jsize len = env->GetArrayLength(valueObj);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700401 void* value = env->GetPrimitiveArrayCritical(valueObj, NULL);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700402 status_t status = window->putBlob(row, column, value, len);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700403 env->ReleasePrimitiveArrayCritical(valueObj, value, JNI_ABORT);
404
Jeff Brown0cde89f2011-10-10 14:50:10 -0700405 if (status) {
406 LOG_WINDOW("Failed to put blob. error=%d", status);
407 return false;
408 }
409
410 LOG_WINDOW("%d,%d is BLOB with %u bytes", row, column, len);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 return true;
412}
413
Ashok Bhat738702d2014-01-02 13:42:56 +0000414static jboolean nativePutString(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700415 jstring valueObj, jint row, jint column) {
416 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417
Jeff Brown0cde89f2011-10-10 14:50:10 -0700418 size_t sizeIncludingNull = env->GetStringUTFLength(valueObj) + 1;
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700419 const char* valueStr = env->GetStringUTFChars(valueObj, NULL);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700420 if (!valueStr) {
Jeff Brown0cde89f2011-10-10 14:50:10 -0700421 LOG_WINDOW("value can't be transferred to UTFChars");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 return false;
423 }
Jeff Brown0cde89f2011-10-10 14:50:10 -0700424 status_t status = window->putString(row, column, valueStr, sizeIncludingNull);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700425 env->ReleaseStringUTFChars(valueObj, valueStr);
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700426
Jeff Brown0cde89f2011-10-10 14:50:10 -0700427 if (status) {
428 LOG_WINDOW("Failed to put string. error=%d", status);
429 return false;
430 }
431
432 LOG_WINDOW("%d,%d is TEXT with %u bytes", row, column, sizeIncludingNull);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433 return true;
434}
435
Ashok Bhat738702d2014-01-02 13:42:56 +0000436static jboolean nativePutLong(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700437 jlong value, jint row, jint column) {
438 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700439 status_t status = window->putLong(row, column, value);
440
441 if (status) {
442 LOG_WINDOW("Failed to put long. error=%d", status);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443 return false;
444 }
445
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700446 LOG_WINDOW("%d,%d is INTEGER 0x%016llx", row, column, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 return true;
448}
449
Ashok Bhat738702d2014-01-02 13:42:56 +0000450static jboolean nativePutDouble(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700451 jdouble value, jint row, jint column) {
452 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700453 status_t status = window->putDouble(row, column, value);
454
455 if (status) {
456 LOG_WINDOW("Failed to put double. error=%d", status);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 return false;
458 }
459
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700460 LOG_WINDOW("%d,%d is FLOAT %lf", row, column, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 return true;
462}
463
Ashok Bhat738702d2014-01-02 13:42:56 +0000464static jboolean nativePutNull(JNIEnv* env, jclass clazz, jlong windowPtr,
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700465 jint row, jint column) {
466 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
Jeff Brown0cde89f2011-10-10 14:50:10 -0700467 status_t status = window->putNull(row, column);
468
469 if (status) {
470 LOG_WINDOW("Failed to put null. error=%d", status);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800471 return false;
472 }
473
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700474 LOG_WINDOW("%d,%d is NULL", row, column);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800475 return true;
476}
477
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478static JNINativeMethod sMethods[] =
479{
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700480 /* name, signature, funcPtr */
Ashok Bhat738702d2014-01-02 13:42:56 +0000481 { "nativeCreate", "(Ljava/lang/String;I)J",
Jeff Brown0cde89f2011-10-10 14:50:10 -0700482 (void*)nativeCreate },
Ashok Bhat738702d2014-01-02 13:42:56 +0000483 { "nativeCreateFromParcel", "(Landroid/os/Parcel;)J",
Jeff Brown0cde89f2011-10-10 14:50:10 -0700484 (void*)nativeCreateFromParcel },
Ashok Bhat738702d2014-01-02 13:42:56 +0000485 { "nativeDispose", "(J)V",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700486 (void*)nativeDispose },
Ashok Bhat738702d2014-01-02 13:42:56 +0000487 { "nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
Jeff Brown0cde89f2011-10-10 14:50:10 -0700488 (void*)nativeWriteToParcel },
Ashok Bhat738702d2014-01-02 13:42:56 +0000489 { "nativeGetName", "(J)Ljava/lang/String;",
Jeff Brown650de3d2011-10-27 14:52:28 -0700490 (void*)nativeGetName },
Ashok Bhat738702d2014-01-02 13:42:56 +0000491 { "nativeClear", "(J)V",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700492 (void*)nativeClear },
Ashok Bhat738702d2014-01-02 13:42:56 +0000493 { "nativeGetNumRows", "(J)I",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700494 (void*)nativeGetNumRows },
Ashok Bhat738702d2014-01-02 13:42:56 +0000495 { "nativeSetNumColumns", "(JI)Z",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700496 (void*)nativeSetNumColumns },
Ashok Bhat738702d2014-01-02 13:42:56 +0000497 { "nativeAllocRow", "(J)Z",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700498 (void*)nativeAllocRow },
Ashok Bhat738702d2014-01-02 13:42:56 +0000499 { "nativeFreeLastRow", "(J)V",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700500 (void*)nativeFreeLastRow },
Ashok Bhat738702d2014-01-02 13:42:56 +0000501 { "nativeGetType", "(JII)I",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700502 (void*)nativeGetType },
Ashok Bhat738702d2014-01-02 13:42:56 +0000503 { "nativeGetBlob", "(JII)[B",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700504 (void*)nativeGetBlob },
Ashok Bhat738702d2014-01-02 13:42:56 +0000505 { "nativeGetString", "(JII)Ljava/lang/String;",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700506 (void*)nativeGetString },
Ashok Bhat738702d2014-01-02 13:42:56 +0000507 { "nativeGetLong", "(JII)J",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700508 (void*)nativeGetLong },
Ashok Bhat738702d2014-01-02 13:42:56 +0000509 { "nativeGetDouble", "(JII)D",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700510 (void*)nativeGetDouble },
Ashok Bhat738702d2014-01-02 13:42:56 +0000511 { "nativeCopyStringToBuffer", "(JIILandroid/database/CharArrayBuffer;)V",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700512 (void*)nativeCopyStringToBuffer },
Ashok Bhat738702d2014-01-02 13:42:56 +0000513 { "nativePutBlob", "(J[BII)Z",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700514 (void*)nativePutBlob },
Ashok Bhat738702d2014-01-02 13:42:56 +0000515 { "nativePutString", "(JLjava/lang/String;II)Z",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700516 (void*)nativePutString },
Ashok Bhat738702d2014-01-02 13:42:56 +0000517 { "nativePutLong", "(JJII)Z",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700518 (void*)nativePutLong },
Ashok Bhat738702d2014-01-02 13:42:56 +0000519 { "nativePutDouble", "(JDII)Z",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700520 (void*)nativePutDouble },
Ashok Bhat738702d2014-01-02 13:42:56 +0000521 { "nativePutNull", "(JII)Z",
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700522 (void*)nativePutNull },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523};
524
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700525#define FIND_CLASS(var, className) \
526 var = env->FindClass(className); \
527 LOG_FATAL_IF(! var, "Unable to find class " className);
528
529#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
530 var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
531 LOG_FATAL_IF(! var, "Unable to find field " fieldName);
532
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533int register_android_database_CursorWindow(JNIEnv * env)
534{
535 jclass clazz;
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700536 FIND_CLASS(clazz, "android/database/CharArrayBuffer");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700538 GET_FIELD_ID(gCharArrayBufferClassInfo.data, clazz,
539 "data", "[C");
540 GET_FIELD_ID(gCharArrayBufferClassInfo.sizeCopied, clazz,
541 "sizeCopied", "I");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542
Jeff Brown3bc6bbc2011-10-06 13:11:04 -0700543 gEmptyString = jstring(env->NewGlobalRef(env->NewStringUTF("")));
544 LOG_FATAL_IF(!gEmptyString, "Unable to create empty string");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800545
546 return AndroidRuntime::registerNativeMethods(env, "android/database/CursorWindow",
547 sMethods, NELEM(sMethods));
548}
549
550} // namespace android