1 /* 2 * Copyright (C) 2009 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 #include <assert.h> 18 #include <cutils/log.h> 19 #include <jni.h> 20 #include <string.h> 21 #include <sys/types.h> 22 #include <unistd.h> 23 24 #include "../include/pinyinime.h" 25 #include "../include/sync.h" 26 #include "../include/userdict.h" 27 28 #ifdef __cplusplus 29 extern "C" { 30 #endif 31 32 using namespace ime_pinyin; 33 34 #define RET_BUF_LEN 256 35 36 static char16 retbuf[RET_BUF_LEN]; 37 static char16 (*predict_buf)[kMaxPredictSize + 1] = NULL; 38 static size_t predict_len; 39 40 static Sync sync_worker; 41 42 static struct file_descriptor_offsets_t 43 { 44 jclass mClass; 45 jfieldID mDescriptor; 46 } gFileDescriptorOffsets; 47 48 JNIEXPORT jboolean JNICALL nativeImOpenDecoder(JNIEnv* env, jclass jclazz, 49 jbyteArray fn_sys_dict, 50 jbyteArray fn_usr_dict) { 51 jbyte *fsd = (*env).GetByteArrayElements(fn_sys_dict, 0); 52 jbyte *fud = (*env).GetByteArrayElements(fn_usr_dict, 0); 53 54 jboolean jret = JNI_FALSE; 55 56 if (im_open_decoder((const char*)fsd, (const char*)fud)) 57 jret = JNI_TRUE; 58 59 (*env).ReleaseByteArrayElements(fn_sys_dict, fsd, 0); 60 (*env).ReleaseByteArrayElements(fn_usr_dict, fud, 0); 61 62 return jret; 63 } 64 65 JNIEXPORT jboolean JNICALL nativeImOpenDecoderFd(JNIEnv* env, jclass jclazz, 66 jobject fd_sys_dict, 67 jlong startoffset, 68 jlong length, 69 jbyteArray fn_usr_dict) { 70 jint fd = env->GetIntField(fd_sys_dict, gFileDescriptorOffsets.mDescriptor); 71 jbyte *fud = (*env).GetByteArrayElements(fn_usr_dict, 0); 72 73 jboolean jret = JNI_FALSE; 74 75 int newfd = dup(fd); 76 if (im_open_decoder_fd(newfd, startoffset, length, (const char*)fud)) 77 jret = JNI_TRUE; 78 79 close(newfd); 80 81 (*env).ReleaseByteArrayElements(fn_usr_dict, fud, 0); 82 83 return jret; 84 } 85 86 JNIEXPORT void JNICALL nativeImSetMaxLens(JNIEnv* env, jclass jclazz, 87 jint max_sps_len, 88 jint max_hzs_len) { 89 im_set_max_lens(static_cast<size_t>(max_sps_len), 90 static_cast<size_t>(max_hzs_len)); 91 return; 92 } 93 94 JNIEXPORT jboolean JNICALL nativeImCloseDecoder(JNIEnv* env, jclass jclazz) { 95 im_close_decoder(); 96 return JNI_TRUE; 97 } 98 99 JNIEXPORT jint JNICALL nativeImSearch(JNIEnv* env, jclass jclazz, 100 jbyteArray pybuf, jint pylen) { 101 jbyte *array_body = (*env).GetByteArrayElements(pybuf, 0); 102 103 jint jret = 0; 104 if (NULL != array_body) { 105 jret = im_search((const char*)array_body, pylen); 106 } 107 108 (*env).ReleaseByteArrayElements(pybuf, array_body, 0); 109 110 return jret; 111 } 112 113 JNIEXPORT jint JNICALL nativeImDelSearch(JNIEnv* env, jclass jclazz, jint pos, 114 jboolean is_pos_in_splid, 115 jboolean clear_fixed_this_step) { 116 return im_delsearch(pos, is_pos_in_splid, clear_fixed_this_step); 117 } 118 119 JNIEXPORT void JNICALL nativeImResetSearch(JNIEnv* env, jclass jclazz) { 120 im_reset_search(); 121 return; 122 } 123 124 JNIEXPORT jint JNICALL nativeImAddLetter(JNIEnv *env, jclass clazz, jbyte ch) { 125 return im_add_letter(ch); 126 } 127 128 JNIEXPORT jstring JNICALL nativeImGetPyStr(JNIEnv* env, jclass jclazz, 129 jboolean decoded) { 130 size_t py_len; 131 const char *py = im_get_sps_str(&py_len); // py_len gets decoded length 132 assert(NULL != py); 133 if (!decoded) 134 py_len = strlen(py); 135 136 const unsigned short *spl_start; 137 size_t len; 138 len = im_get_spl_start_pos(spl_start); 139 140 size_t i; 141 for (i = 0; i < py_len; i++) 142 retbuf[i] = py[i]; 143 retbuf[i] = (char16)'\0'; 144 145 jstring retstr = (*env).NewString((unsigned short*)retbuf, i); 146 return retstr; 147 } 148 149 JNIEXPORT jint JNICALL nativeImGetPyStrLen(JNIEnv* env, jclass jclazz, 150 jboolean decoded) { 151 size_t py_len; 152 const char *py = im_get_sps_str(&py_len); // py_len gets decoded length 153 assert(NULL != py); 154 if (!decoded) 155 py_len = strlen(py); 156 return py_len; 157 } 158 159 JNIEXPORT jintArray JNICALL nativeImGetSplStart(JNIEnv* env, jclass jclazz) { 160 const unsigned short *spl_start; 161 size_t len; 162 163 // There will be len + 1 elements in the buffer when len > 0. 164 len = im_get_spl_start_pos(spl_start); 165 166 jintArray arr = (*env).NewIntArray(len + 2); 167 jint *arr_body = (*env).GetIntArrayElements(arr, 0); 168 assert(NULL != arr_body); 169 arr_body[0] = len; // element 0 is used to store the length of buffer. 170 for (size_t i = 0; i <= len; i++) 171 arr_body[i + 1] = spl_start[i]; 172 173 (*env).ReleaseIntArrayElements(arr, arr_body, 0); 174 175 return arr; 176 } 177 178 JNIEXPORT jstring JNICALL nativeImGetChoice(JNIEnv *env, jclass clazz, 179 jint candidateId) { 180 jstring retstr; 181 if(im_get_candidate(candidateId, retbuf, RET_BUF_LEN)) { 182 retstr = (*env).NewString(retbuf, utf16_strlen(retbuf)); 183 return retstr; 184 } else { 185 retstr = (*env).NewString((unsigned short*)retbuf, 0); 186 return retstr; 187 } 188 } 189 190 JNIEXPORT jint JNICALL nativeImChoose(JNIEnv *env, jclass clazz, 191 jint choice_id) { 192 return im_choose(choice_id); 193 } 194 195 JNIEXPORT jint JNICALL nativeImCancelLastChoice(JNIEnv *env, jclass clazz) { 196 return im_cancel_last_choice(); 197 } 198 199 JNIEXPORT jint JNICALL nativeImGetFixedLen(JNIEnv *env, jclass clazz) { 200 return im_get_fixed_len(); 201 } 202 203 JNIEXPORT jboolean JNICALL nativeImCancelInput(JNIEnv *env, jclass clazz) { 204 if (im_cancel_input()) 205 return JNI_TRUE; 206 207 return JNI_FALSE; 208 } 209 210 JNIEXPORT jboolean JNICALL nativeImFlushCache(JNIEnv *env, jclass clazz) { 211 im_flush_cache(); 212 return JNI_TRUE; 213 } 214 215 JNIEXPORT jint JNICALL nativeImGetPredictsNum(JNIEnv *env, jclass clazz, 216 jstring fixed_str) { 217 char16 *fixed_ptr = (char16*)(*env).GetStringChars(fixed_str, false); 218 size_t fixed_len = (size_t)(*env).GetStringLength(fixed_str); 219 220 char16 fixed_buf[kMaxPredictSize + 1]; 221 222 if (fixed_len > kMaxPredictSize) { 223 fixed_ptr += fixed_len - kMaxPredictSize; 224 fixed_len = kMaxPredictSize; 225 } 226 utf16_strncpy(fixed_buf, fixed_ptr, fixed_len); 227 fixed_buf[fixed_len] = (char16)'\0'; 228 229 predict_len = im_get_predicts(fixed_buf, predict_buf); 230 231 (*env).ReleaseStringChars(fixed_str, fixed_ptr); 232 233 return predict_len; 234 } 235 236 JNIEXPORT jstring JNICALL nativeImGetPredictItem(JNIEnv *env, jclass clazz, 237 jint predict_no) { 238 jstring retstr; 239 240 if (predict_no < 0 || (size_t)predict_no >= predict_len) { 241 retstr = (*env).NewString((unsigned short*)predict_buf[0], 0); 242 } else { 243 retstr = (*env).NewString((unsigned short*)predict_buf[predict_no], 244 utf16_strlen(predict_buf[predict_no])); 245 } 246 return retstr; 247 } 248 249 JNIEXPORT jboolean JNICALL nativeSyncBegin(JNIEnv *env, jclass clazz, 250 jbyteArray dict_file) { 251 jbyte *file_name = (*env).GetByteArrayElements(dict_file, 0); 252 253 jboolean jret = JNI_FALSE; 254 if (true == sync_worker.begin((const char *)file_name)) 255 jret = JNI_TRUE; 256 257 (*env).ReleaseByteArrayElements(dict_file, file_name, 0); 258 259 return jret; 260 } 261 262 JNIEXPORT jboolean JNICALL nativeSyncFinish(JNIEnv *env, jclass clazz) { 263 sync_worker.finish(); 264 return JNI_TRUE; 265 } 266 267 JNIEXPORT jint JNICALL nativeSyncGetCapacity(JNIEnv *env, jclass clazz) { 268 return sync_worker.get_capacity(); 269 } 270 271 JNIEXPORT jint JNICALL nativeSyncPutLemmas(JNIEnv *env, jclass clazz, 272 jstring tomerge) { 273 274 char16 *ptr = (char16*)(*env).GetStringChars(tomerge, NULL); 275 int len = (size_t)(*env).GetStringLength(tomerge); 276 277 int added = sync_worker.put_lemmas(ptr, len); 278 279 (*env).ReleaseStringChars(tomerge, ptr); 280 281 return added; 282 } 283 284 JNIEXPORT jstring JNICALL nativeSyncGetLemmas(JNIEnv *env, jclass clazz) { 285 286 int len = sync_worker.get_lemmas(retbuf, RET_BUF_LEN); 287 if (len == 0) 288 return NULL; 289 jstring retstr; 290 retstr = (*env).NewString((unsigned short*)retbuf, len); 291 return retstr; 292 } 293 294 JNIEXPORT jint JNICALL nativeSyncGetLastCount(JNIEnv *env, jclass clazz) { 295 return sync_worker.get_last_got_count(); 296 } 297 298 JNIEXPORT jint JNICALL nativeSyncGetTotalCount(JNIEnv *env, jclass clazz) { 299 return sync_worker.get_total_count(); 300 } 301 302 JNIEXPORT jboolean JNICALL nativeSyncClearLastGot(JNIEnv *env, jclass clazz) { 303 sync_worker.clear_last_got(); 304 return JNI_TRUE; 305 } 306 307 /** 308 * Table of methods associated with a single class. 309 */ 310 static JNINativeMethod gMethods[] = { 311 /* name, signature, funcPtr */ 312 /* ------Functions for Pinyin-to-hanzi decoding begin--------->> */ 313 { "nativeImOpenDecoder", "([B[B)Z", 314 (void*) nativeImOpenDecoder }, 315 { "nativeImOpenDecoderFd", "(Ljava/io/FileDescriptor;JJ[B)Z", 316 (void*) nativeImOpenDecoderFd }, 317 { "nativeImSetMaxLens", "(II)V", 318 (void*) nativeImSetMaxLens }, 319 { "nativeImCloseDecoder", "()Z", 320 (void*) nativeImCloseDecoder }, 321 { "nativeImSearch", "([BI)I", 322 (void*) nativeImSearch }, 323 { "nativeImDelSearch", "(IZZ)I", 324 (void*) nativeImDelSearch }, 325 { "nativeImResetSearch", "()V", 326 (void*) nativeImResetSearch }, 327 { "nativeImAddLetter", "(B)I", 328 (void*) nativeImAddLetter }, 329 { "nativeImGetPyStr", "(Z)Ljava/lang/String;", 330 (void*) nativeImGetPyStr }, 331 { "nativeImGetPyStrLen", "(Z)I", 332 (void*) nativeImGetPyStrLen }, 333 { "nativeImGetSplStart", "()[I", 334 (void*) nativeImGetSplStart }, 335 { "nativeImGetChoice", "(I)Ljava/lang/String;", 336 (void*) nativeImGetChoice }, 337 { "nativeImChoose", "(I)I", 338 (void*) nativeImChoose }, 339 { "nativeImCancelLastChoice", "()I", 340 (void*) nativeImCancelLastChoice }, 341 { "nativeImGetFixedLen", "()I", 342 (void*) nativeImGetFixedLen }, 343 { "nativeImGetPredictsNum", "(Ljava/lang/String;)I", 344 (void*) nativeImGetPredictsNum }, 345 { "nativeImGetPredictItem", "(I)Ljava/lang/String;", 346 (void*) nativeImGetPredictItem }, 347 { "nativeImCancelInput", "()Z", 348 (void*) nativeImCancelInput }, 349 { "nativeImFlushCache", "()Z", 350 (void*) nativeImFlushCache }, 351 /* <<----Functions for Pinyin-to-hanzi decoding end------------- */ 352 353 /* ------Functions for sync begin----------------------------->> */ 354 { "nativeSyncBegin", "([B)Z", 355 (void*) nativeSyncBegin }, 356 { "nativeSyncFinish", "()Z", 357 (void*) nativeSyncFinish }, 358 { "nativeSyncPutLemmas", "(Ljava/lang/String;)I", 359 (void*) nativeSyncPutLemmas }, 360 { "nativeSyncGetLemmas", "()Ljava/lang/String;", 361 (void*) nativeSyncGetLemmas }, 362 { "nativeSyncGetLastCount", "()I", 363 (void*) nativeSyncGetLastCount }, 364 { "nativeSyncGetTotalCount", "()I", 365 (void*) nativeSyncGetTotalCount }, 366 { "nativeSyncClearLastGot", "()Z", 367 (void*) nativeSyncClearLastGot }, 368 { "nativeSyncGetCapacity", "()I", 369 (void*) nativeSyncGetCapacity }, 370 /* <<----Functions for sync end--------------------------------- */ 371 }; 372 373 374 /* 375 * Register several native methods for one class. 376 */ 377 static int registerNativeMethods(JNIEnv* env, const char* className, 378 JNINativeMethod* gMethods, int numMethods) 379 { 380 jclass clazz; 381 382 clazz = (*env).FindClass(className); 383 if (clazz == NULL) { 384 return JNI_FALSE; 385 } 386 if ((*env).RegisterNatives(clazz, gMethods, numMethods) < 0) { 387 return JNI_FALSE; 388 } 389 390 clazz = env->FindClass("java/io/FileDescriptor"); 391 LOG_FATAL_IF(clazz == NULL, "Unable to find Java class java.io.FileDescriptor"); 392 gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); 393 gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I"); 394 LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL, 395 "Unable to find descriptor field in java.io.FileDescriptor"); 396 397 return JNI_TRUE; 398 } 399 400 /* 401 * Register native methods for all classes we know about. 402 */ 403 static int registerNatives(JNIEnv* env) 404 { 405 if (!registerNativeMethods(env, 406 "com/android/inputmethod/pinyin/PinyinDecoderService", 407 gMethods, sizeof(gMethods) / sizeof(gMethods[0]))) 408 return JNI_FALSE; 409 410 return JNI_TRUE; 411 } 412 413 /* 414 * Set some test stuff up. 415 * 416 * Returns the JNI version on success, -1 on failure. 417 */ 418 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) 419 { 420 JNIEnv* env = NULL; 421 jint result = -1; 422 423 if ((*vm).GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { 424 goto bail; 425 } 426 assert(env != NULL); 427 428 if (!registerNatives(env)) { 429 goto bail; 430 } 431 432 /* success -- return valid version number */ 433 result = JNI_VERSION_1_4; 434 435 bail: 436 return result; 437 } 438 439 #ifdef __cplusplus 440 } 441 #endif 442