1 /* 2 ** Copyright 2006, 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 #define LOG_TAG "Log_println" 18 19 #include <utils/Log.h> 20 #include <utils/String8.h> 21 #include <assert.h> 22 23 #include "jni.h" 24 #include "utils/misc.h" 25 #include "android_runtime/AndroidRuntime.h" 26 #include "TimeUtils.h" 27 #include <nativehelper/JNIHelp.h> 28 #include <cutils/tztime.h> 29 30 namespace android { 31 32 static jfieldID g_allDayField = 0; 33 static jfieldID g_secField = 0; 34 static jfieldID g_minField = 0; 35 static jfieldID g_hourField = 0; 36 static jfieldID g_mdayField = 0; 37 static jfieldID g_monField = 0; 38 static jfieldID g_yearField = 0; 39 static jfieldID g_wdayField = 0; 40 static jfieldID g_ydayField = 0; 41 static jfieldID g_isdstField = 0; 42 static jfieldID g_gmtoffField = 0; 43 static jfieldID g_timezoneField = 0; 44 45 static jfieldID g_shortMonthsField = 0; 46 static jfieldID g_longMonthsField = 0; 47 static jfieldID g_longStandaloneMonthsField = 0; 48 static jfieldID g_shortWeekdaysField = 0; 49 static jfieldID g_longWeekdaysField = 0; 50 static jfieldID g_timeOnlyFormatField = 0; 51 static jfieldID g_dateOnlyFormatField = 0; 52 static jfieldID g_dateTimeFormatField = 0; 53 static jfieldID g_amField = 0; 54 static jfieldID g_pmField = 0; 55 static jfieldID g_dateCommandField = 0; 56 static jfieldID g_localeField = 0; 57 58 static jclass g_timeClass = NULL; 59 60 static inline bool java2time(JNIEnv* env, Time* t, jobject o) 61 { 62 t->t.tm_sec = env->GetIntField(o, g_secField); 63 t->t.tm_min = env->GetIntField(o, g_minField); 64 t->t.tm_hour = env->GetIntField(o, g_hourField); 65 t->t.tm_mday = env->GetIntField(o, g_mdayField); 66 t->t.tm_mon = env->GetIntField(o, g_monField); 67 t->t.tm_year = (env->GetIntField(o, g_yearField))-1900; 68 t->t.tm_wday = env->GetIntField(o, g_wdayField); 69 t->t.tm_yday = env->GetIntField(o, g_ydayField); 70 t->t.tm_isdst = env->GetIntField(o, g_isdstField); 71 t->t.tm_gmtoff = env->GetLongField(o, g_gmtoffField); 72 bool allDay = env->GetIntField(o, g_allDayField); 73 if (allDay && 74 ((t->t.tm_sec !=0) || (t->t.tm_min != 0) || (t->t.tm_hour != 0))) { 75 char msg[100]; 76 sprintf(msg, "allDay is true but sec, min, hour are not 0."); 77 jniThrowException(env, "java/lang/IllegalArgumentException", msg); 78 return false; 79 } 80 return true; 81 } 82 83 static inline void time2java(JNIEnv* env, jobject o, const Time &t) 84 { 85 env->SetIntField(o, g_secField, t.t.tm_sec); 86 env->SetIntField(o, g_minField, t.t.tm_min); 87 env->SetIntField(o, g_hourField, t.t.tm_hour); 88 env->SetIntField(o, g_mdayField, t.t.tm_mday); 89 env->SetIntField(o, g_monField, t.t.tm_mon); 90 env->SetIntField(o, g_yearField, t.t.tm_year+1900); 91 env->SetIntField(o, g_wdayField, t.t.tm_wday); 92 env->SetIntField(o, g_ydayField, t.t.tm_yday); 93 env->SetIntField(o, g_isdstField, t.t.tm_isdst); 94 env->SetLongField(o, g_gmtoffField, t.t.tm_gmtoff); 95 } 96 97 #define ACQUIRE_TIMEZONE(This, t) \ 98 jstring timezoneString_##This \ 99 = (jstring) env->GetObjectField(This, g_timezoneField); \ 100 t.timezone = env->GetStringUTFChars(timezoneString_##This, NULL); 101 102 #define RELEASE_TIMEZONE(This, t) \ 103 env->ReleaseStringUTFChars(timezoneString_##This, t.timezone); 104 105 106 // ============================================================================ 107 108 static jlong android_text_format_Time_normalize(JNIEnv* env, jobject This, 109 jboolean ignoreDst) 110 { 111 Time t; 112 if (!java2time(env, &t, This)) return 0L; 113 ACQUIRE_TIMEZONE(This, t) 114 115 int64_t result = t.toMillis(ignoreDst != 0); 116 117 time2java(env, This, t); 118 RELEASE_TIMEZONE(This, t) 119 120 return result; 121 } 122 123 static void android_text_format_Time_switchTimezone(JNIEnv* env, jobject This, 124 jstring timezoneObject) 125 { 126 Time t; 127 if (!java2time(env, &t, This)) return; 128 ACQUIRE_TIMEZONE(This, t) 129 130 const char* timezone = env->GetStringUTFChars(timezoneObject, NULL); 131 132 t.switchTimezone(timezone); 133 134 time2java(env, This, t); 135 env->ReleaseStringUTFChars(timezoneObject, timezone); 136 RELEASE_TIMEZONE(This, t) 137 138 // we do this here because there's no point in reallocating the string 139 env->SetObjectField(This, g_timezoneField, timezoneObject); 140 } 141 142 static jint android_text_format_Time_compare(JNIEnv* env, jobject clazz, 143 jobject aObject, jobject bObject) 144 { 145 Time a, b; 146 147 if (!java2time(env, &a, aObject)) return 0; 148 ACQUIRE_TIMEZONE(aObject, a) 149 150 if (!java2time(env, &b, bObject)) return 0; 151 ACQUIRE_TIMEZONE(bObject, b) 152 153 int result = Time::compare(a, b); 154 155 RELEASE_TIMEZONE(aObject, a) 156 RELEASE_TIMEZONE(bObject, b) 157 158 return result; 159 } 160 161 static jstring android_text_format_Time_format2445(JNIEnv* env, jobject This) 162 { 163 Time t; 164 if (!java2time(env, &t, This)) return env->NewStringUTF(""); 165 bool allDay = env->GetIntField(This, g_allDayField); 166 167 if (!allDay) { 168 ACQUIRE_TIMEZONE(This, t) 169 bool inUtc = strcmp("UTC", t.timezone) == 0; 170 short buf[16]; 171 t.format2445(buf, true); 172 RELEASE_TIMEZONE(This, t) 173 if (inUtc) { 174 // The letter 'Z' is appended to the end so allow for one 175 // more character in the buffer. 176 return env->NewString((jchar*)buf, 16); 177 } else { 178 return env->NewString((jchar*)buf, 15); 179 } 180 } else { 181 short buf[8]; 182 t.format2445(buf, false); 183 return env->NewString((jchar*)buf, 8); 184 } 185 } 186 187 static jstring android_text_format_Time_format(JNIEnv* env, jobject This, 188 jstring formatObject) 189 { 190 // We only teardown and setup our 'locale' struct and other state 191 // when the Java-side locale changed. This is safe to do here 192 // without locking because we're always called from Java code 193 // synchronized on the class instance. 194 static jobject js_locale_previous = NULL; 195 static struct strftime_locale locale; 196 static jstring js_mon[12], js_month[12], js_wday[7], js_weekday[7]; 197 static jstring js_standalone_month[12]; 198 static jstring js_X_fmt, js_x_fmt, js_c_fmt, js_am, js_pm, js_date_fmt; 199 200 Time t; 201 if (!java2time(env, &t, This)) return env->NewStringUTF(""); 202 203 jclass timeClass = g_timeClass; 204 jobject js_locale = (jobject) env->GetStaticObjectField(timeClass, g_localeField); 205 if (js_locale_previous != js_locale) { 206 if (js_locale_previous != NULL) { 207 // Free the old one. 208 for (int i = 0; i < 12; i++) { 209 env->ReleaseStringUTFChars(js_mon[i], locale.mon[i]); 210 env->ReleaseStringUTFChars(js_month[i], locale.month[i]); 211 env->ReleaseStringUTFChars(js_standalone_month[i], locale.standalone_month[i]); 212 env->DeleteGlobalRef(js_mon[i]); 213 env->DeleteGlobalRef(js_month[i]); 214 env->DeleteGlobalRef(js_standalone_month[i]); 215 } 216 217 for (int i = 0; i < 7; i++) { 218 env->ReleaseStringUTFChars(js_wday[i], locale.wday[i]); 219 env->ReleaseStringUTFChars(js_weekday[i], locale.weekday[i]); 220 env->DeleteGlobalRef(js_wday[i]); 221 env->DeleteGlobalRef(js_weekday[i]); 222 } 223 224 env->ReleaseStringUTFChars(js_X_fmt, locale.X_fmt); 225 env->ReleaseStringUTFChars(js_x_fmt, locale.x_fmt); 226 env->ReleaseStringUTFChars(js_c_fmt, locale.c_fmt); 227 env->ReleaseStringUTFChars(js_am, locale.am); 228 env->ReleaseStringUTFChars(js_pm, locale.pm); 229 env->ReleaseStringUTFChars(js_date_fmt, locale.date_fmt); 230 env->DeleteGlobalRef(js_X_fmt); 231 env->DeleteGlobalRef(js_x_fmt); 232 env->DeleteGlobalRef(js_c_fmt); 233 env->DeleteGlobalRef(js_am); 234 env->DeleteGlobalRef(js_pm); 235 env->DeleteGlobalRef(js_date_fmt); 236 } 237 js_locale_previous = js_locale; 238 239 jobjectArray ja; 240 ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortMonthsField); 241 for (int i = 0; i < 12; i++) { 242 js_mon[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i)); 243 locale.mon[i] = env->GetStringUTFChars(js_mon[i], NULL); 244 } 245 246 ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longMonthsField); 247 for (int i = 0; i < 12; i++) { 248 js_month[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i)); 249 locale.month[i] = env->GetStringUTFChars(js_month[i], NULL); 250 } 251 252 ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longStandaloneMonthsField); 253 for (int i = 0; i < 12; i++) { 254 js_standalone_month[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i)); 255 locale.standalone_month[i] = env->GetStringUTFChars(js_standalone_month[i], NULL); 256 } 257 258 ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortWeekdaysField); 259 for (int i = 0; i < 7; i++) { 260 js_wday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i)); 261 locale.wday[i] = env->GetStringUTFChars(js_wday[i], NULL); 262 } 263 264 ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longWeekdaysField); 265 for (int i = 0; i < 7; i++) { 266 js_weekday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i)); 267 locale.weekday[i] = env->GetStringUTFChars(js_weekday[i], NULL); 268 } 269 270 js_X_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField( 271 timeClass, g_timeOnlyFormatField)); 272 locale.X_fmt = env->GetStringUTFChars(js_X_fmt, NULL); 273 274 js_x_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField( 275 timeClass, g_dateOnlyFormatField)); 276 locale.x_fmt = env->GetStringUTFChars(js_x_fmt, NULL); 277 278 js_c_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField( 279 timeClass, g_dateTimeFormatField)); 280 locale.c_fmt = env->GetStringUTFChars(js_c_fmt, NULL); 281 282 js_am = (jstring) env->NewGlobalRef(env->GetStaticObjectField( 283 timeClass, g_amField)); 284 locale.am = env->GetStringUTFChars(js_am, NULL); 285 286 js_pm = (jstring) env->NewGlobalRef(env->GetStaticObjectField( 287 timeClass, g_pmField)); 288 locale.pm = env->GetStringUTFChars(js_pm, NULL); 289 290 js_date_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField( 291 timeClass, g_dateCommandField)); 292 locale.date_fmt = env->GetStringUTFChars(js_date_fmt, NULL); 293 } 294 295 ACQUIRE_TIMEZONE(This, t) 296 297 const char* format = env->GetStringUTFChars(formatObject, NULL); 298 299 String8 r = t.format(format, &locale); 300 301 env->ReleaseStringUTFChars(formatObject, format); 302 RELEASE_TIMEZONE(This, t) 303 304 return env->NewStringUTF(r.string()); 305 } 306 307 308 static jstring android_text_format_Time_toString(JNIEnv* env, jobject This) 309 { 310 Time t; 311 if (!java2time(env, &t, This)) return env->NewStringUTF("");; 312 ACQUIRE_TIMEZONE(This, t) 313 314 String8 r = t.toString(); 315 316 RELEASE_TIMEZONE(This, t) 317 318 return env->NewStringUTF(r.string()); 319 } 320 321 static void android_text_format_Time_setToNow(JNIEnv* env, jobject This) 322 { 323 env->SetBooleanField(This, g_allDayField, JNI_FALSE); 324 Time t; 325 ACQUIRE_TIMEZONE(This, t) 326 327 t.setToNow(); 328 329 time2java(env, This, t); 330 RELEASE_TIMEZONE(This, t) 331 } 332 333 static jlong android_text_format_Time_toMillis(JNIEnv* env, jobject This, 334 jboolean ignoreDst) 335 { 336 Time t; 337 if (!java2time(env, &t, This)) return 0L; 338 ACQUIRE_TIMEZONE(This, t) 339 340 int64_t result = t.toMillis(ignoreDst != 0); 341 342 RELEASE_TIMEZONE(This, t) 343 344 return result; 345 } 346 347 static void android_text_format_Time_set(JNIEnv* env, jobject This, jlong millis) 348 { 349 env->SetBooleanField(This, g_allDayField, JNI_FALSE); 350 Time t; 351 ACQUIRE_TIMEZONE(This, t) 352 353 t.set(millis); 354 355 time2java(env, This, t); 356 RELEASE_TIMEZONE(This, t) 357 } 358 359 360 // ============================================================================ 361 // Just do this here because it's not worth recreating the strings 362 363 static int get_char(JNIEnv* env, const jchar *s, int spos, int mul, 364 bool *thrown) 365 { 366 jchar c = s[spos]; 367 if (c >= '0' && c <= '9') { 368 return (c - '0') * mul; 369 } else { 370 if (!*thrown) { 371 char msg[100]; 372 sprintf(msg, "Parse error at pos=%d", spos); 373 jniThrowException(env, "android/util/TimeFormatException", msg); 374 *thrown = true; 375 } 376 return 0; 377 } 378 } 379 380 static bool check_char(JNIEnv* env, const jchar *s, int spos, jchar expected) 381 { 382 jchar c = s[spos]; 383 if (c != expected) { 384 char msg[100]; 385 sprintf(msg, "Unexpected character 0x%02x at pos=%d. Expected %c.", c, spos, 386 expected); 387 jniThrowException(env, "android/util/TimeFormatException", msg); 388 return false; 389 } 390 return true; 391 } 392 393 394 static jboolean android_text_format_Time_parse(JNIEnv* env, jobject This, jstring strObj) 395 { 396 jsize len = env->GetStringLength(strObj); 397 const jchar *s = env->GetStringChars(strObj, NULL); 398 399 bool thrown = false; 400 int n; 401 jboolean inUtc = false; 402 403 if (len < 8) { 404 char msg[100]; 405 sprintf(msg, "String too short -- expected at least 8 characters."); 406 jniThrowException(env, "android/util/TimeFormatException", msg); 407 return false; 408 } 409 410 // year 411 n = get_char(env, s, 0, 1000, &thrown); 412 n += get_char(env, s, 1, 100, &thrown); 413 n += get_char(env, s, 2, 10, &thrown); 414 n += get_char(env, s, 3, 1, &thrown); 415 if (thrown) return false; 416 env->SetIntField(This, g_yearField, n); 417 418 // month 419 n = get_char(env, s, 4, 10, &thrown); 420 n += get_char(env, s, 5, 1, &thrown); 421 n--; 422 if (thrown) return false; 423 env->SetIntField(This, g_monField, n); 424 425 // day of month 426 n = get_char(env, s, 6, 10, &thrown); 427 n += get_char(env, s, 7, 1, &thrown); 428 if (thrown) return false; 429 env->SetIntField(This, g_mdayField, n); 430 431 if (len > 8) { 432 // T 433 if (!check_char(env, s, 8, 'T')) return false; 434 env->SetBooleanField(This, g_allDayField, JNI_FALSE); 435 436 // hour 437 n = get_char(env, s, 9, 10, &thrown); 438 n += get_char(env, s, 10, 1, &thrown); 439 if (thrown) return false; 440 env->SetIntField(This, g_hourField, n); 441 442 // min 443 n = get_char(env, s, 11, 10, &thrown); 444 n += get_char(env, s, 12, 1, &thrown); 445 if (thrown) return false; 446 env->SetIntField(This, g_minField, n); 447 448 // sec 449 n = get_char(env, s, 13, 10, &thrown); 450 n += get_char(env, s, 14, 1, &thrown); 451 if (thrown) return false; 452 env->SetIntField(This, g_secField, n); 453 454 if (len > 15) { 455 // Z 456 if (!check_char(env, s, 15, 'Z')) return false; 457 inUtc = true; 458 } 459 } else { 460 env->SetBooleanField(This, g_allDayField, JNI_TRUE); 461 env->SetIntField(This, g_hourField, 0); 462 env->SetIntField(This, g_minField, 0); 463 env->SetIntField(This, g_secField, 0); 464 } 465 466 env->SetIntField(This, g_wdayField, 0); 467 env->SetIntField(This, g_ydayField, 0); 468 env->SetIntField(This, g_isdstField, -1); 469 env->SetLongField(This, g_gmtoffField, 0); 470 471 env->ReleaseStringChars(strObj, s); 472 return inUtc; 473 } 474 475 static jboolean android_text_format_Time_parse3339(JNIEnv* env, 476 jobject This, 477 jstring strObj) 478 { 479 jsize len = env->GetStringLength(strObj); 480 const jchar *s = env->GetStringChars(strObj, NULL); 481 482 bool thrown = false; 483 int n; 484 jboolean inUtc = false; 485 486 if (len < 10) { 487 jniThrowException(env, "android/util/TimeFormatException", 488 "Time input is too short; must be at least 10 characters"); 489 return false; 490 } 491 492 // year 493 n = get_char(env, s, 0, 1000, &thrown); 494 n += get_char(env, s, 1, 100, &thrown); 495 n += get_char(env, s, 2, 10, &thrown); 496 n += get_char(env, s, 3, 1, &thrown); 497 if (thrown) return false; 498 env->SetIntField(This, g_yearField, n); 499 500 // - 501 if (!check_char(env, s, 4, '-')) return false; 502 503 // month 504 n = get_char(env, s, 5, 10, &thrown); 505 n += get_char(env, s, 6, 1, &thrown); 506 --n; 507 if (thrown) return false; 508 env->SetIntField(This, g_monField, n); 509 510 // - 511 if (!check_char(env, s, 7, '-')) return false; 512 513 // day 514 n = get_char(env, s, 8, 10, &thrown); 515 n += get_char(env, s, 9, 1, &thrown); 516 if (thrown) return false; 517 env->SetIntField(This, g_mdayField, n); 518 519 if (len >= 19) { 520 // T 521 if (!check_char(env, s, 10, 'T')) return false; 522 523 env->SetBooleanField(This, g_allDayField, JNI_FALSE); 524 // hour 525 n = get_char(env, s, 11, 10, &thrown); 526 n += get_char(env, s, 12, 1, &thrown); 527 if (thrown) return false; 528 int hour = n; 529 // env->SetIntField(This, g_hourField, n); 530 531 // : 532 if (!check_char(env, s, 13, ':')) return false; 533 534 // minute 535 n = get_char(env, s, 14, 10, &thrown); 536 n += get_char(env, s, 15, 1, &thrown); 537 if (thrown) return false; 538 int minute = n; 539 // env->SetIntField(This, g_minField, n); 540 541 // : 542 if (!check_char(env, s, 16, ':')) return false; 543 544 // second 545 n = get_char(env, s, 17, 10, &thrown); 546 n += get_char(env, s, 18, 1, &thrown); 547 if (thrown) return false; 548 env->SetIntField(This, g_secField, n); 549 550 // skip the '.XYZ' -- we don't care about subsecond precision. 551 int tz_index = 19; 552 if (tz_index < len && s[tz_index] == '.') { 553 do { 554 tz_index++; 555 } while (tz_index < len 556 && s[tz_index] >= '0' 557 && s[tz_index] <= '9'); 558 } 559 560 int offset = 0; 561 if (len > tz_index) { 562 char c = s[tz_index]; 563 564 // NOTE: the offset is meant to be subtracted to get from local time 565 // to UTC. we therefore use 1 for '-' and -1 for '+'. 566 switch (c) { 567 case 'Z': 568 // Zulu time -- UTC 569 offset = 0; 570 break; 571 case '-': 572 offset = 1; 573 break; 574 case '+': 575 offset = -1; 576 break; 577 default: 578 char msg[100]; 579 sprintf(msg, "Unexpected character 0x%02x at position %d. Expected + or -", 580 c, tz_index); 581 jniThrowException(env, "android/util/TimeFormatException", msg); 582 return false; 583 } 584 inUtc = true; 585 586 if (offset != 0) { 587 if (len < tz_index + 6) { 588 char msg[100]; 589 sprintf(msg, "Unexpected length; should be %d characters", tz_index + 6); 590 jniThrowException(env, "android/util/TimeFormatException", msg); 591 return false; 592 } 593 594 // hour 595 n = get_char(env, s, tz_index + 1, 10, &thrown); 596 n += get_char(env, s, tz_index + 2, 1, &thrown); 597 if (thrown) return false; 598 n *= offset; 599 hour += n; 600 601 // : 602 if (!check_char(env, s, tz_index + 3, ':')) return false; 603 604 // minute 605 n = get_char(env, s, tz_index + 4, 10, &thrown); 606 n += get_char(env, s, tz_index + 5, 1, &thrown); 607 if (thrown) return false; 608 n *= offset; 609 minute += n; 610 } 611 } 612 env->SetIntField(This, g_hourField, hour); 613 env->SetIntField(This, g_minField, minute); 614 615 if (offset != 0) { 616 // we need to normalize after applying the hour and minute offsets 617 android_text_format_Time_normalize(env, This, false /* use isdst */); 618 // The timezone is set to UTC in the calling Java code. 619 } 620 } else { 621 env->SetBooleanField(This, g_allDayField, JNI_TRUE); 622 env->SetIntField(This, g_hourField, 0); 623 env->SetIntField(This, g_minField, 0); 624 env->SetIntField(This, g_secField, 0); 625 } 626 627 env->SetIntField(This, g_wdayField, 0); 628 env->SetIntField(This, g_ydayField, 0); 629 env->SetIntField(This, g_isdstField, -1); 630 env->SetLongField(This, g_gmtoffField, 0); 631 632 env->ReleaseStringChars(strObj, s); 633 return inUtc; 634 } 635 636 // ============================================================================ 637 /* 638 * JNI registration. 639 */ 640 static JNINativeMethod gMethods[] = { 641 /* name, signature, funcPtr */ 642 { "normalize", "(Z)J", (void*)android_text_format_Time_normalize }, 643 { "switchTimezone", "(Ljava/lang/String;)V", (void*)android_text_format_Time_switchTimezone }, 644 { "compare", "(Landroid/text/format/Time;Landroid/text/format/Time;)I", (void*)android_text_format_Time_compare }, 645 { "format1", "(Ljava/lang/String;)Ljava/lang/String;", (void*)android_text_format_Time_format }, 646 { "format2445", "()Ljava/lang/String;", (void*)android_text_format_Time_format2445 }, 647 { "toString", "()Ljava/lang/String;", (void*)android_text_format_Time_toString }, 648 { "nativeParse", "(Ljava/lang/String;)Z", (void*)android_text_format_Time_parse }, 649 { "nativeParse3339", "(Ljava/lang/String;)Z", (void*)android_text_format_Time_parse3339 }, 650 { "setToNow", "()V", (void*)android_text_format_Time_setToNow }, 651 { "toMillis", "(Z)J", (void*)android_text_format_Time_toMillis }, 652 { "set", "(J)V", (void*)android_text_format_Time_set } 653 }; 654 655 int register_android_text_format_Time(JNIEnv* env) 656 { 657 jclass timeClass = env->FindClass("android/text/format/Time"); 658 659 g_timeClass = (jclass) env->NewGlobalRef(timeClass); 660 661 g_allDayField = env->GetFieldID(timeClass, "allDay", "Z"); 662 g_secField = env->GetFieldID(timeClass, "second", "I"); 663 g_minField = env->GetFieldID(timeClass, "minute", "I"); 664 g_hourField = env->GetFieldID(timeClass, "hour", "I"); 665 g_mdayField = env->GetFieldID(timeClass, "monthDay", "I"); 666 g_monField = env->GetFieldID(timeClass, "month", "I"); 667 g_yearField = env->GetFieldID(timeClass, "year", "I"); 668 g_wdayField = env->GetFieldID(timeClass, "weekDay", "I"); 669 g_ydayField = env->GetFieldID(timeClass, "yearDay", "I"); 670 g_isdstField = env->GetFieldID(timeClass, "isDst", "I"); 671 g_gmtoffField = env->GetFieldID(timeClass, "gmtoff", "J"); 672 g_timezoneField = env->GetFieldID(timeClass, "timezone", "Ljava/lang/String;"); 673 674 g_shortMonthsField = env->GetStaticFieldID(timeClass, "sShortMonths", "[Ljava/lang/String;"); 675 g_longMonthsField = env->GetStaticFieldID(timeClass, "sLongMonths", "[Ljava/lang/String;"); 676 g_longStandaloneMonthsField = env->GetStaticFieldID(timeClass, "sLongStandaloneMonths", "[Ljava/lang/String;"); 677 g_shortWeekdaysField = env->GetStaticFieldID(timeClass, "sShortWeekdays", "[Ljava/lang/String;"); 678 g_longWeekdaysField = env->GetStaticFieldID(timeClass, "sLongWeekdays", "[Ljava/lang/String;"); 679 g_timeOnlyFormatField = env->GetStaticFieldID(timeClass, "sTimeOnlyFormat", "Ljava/lang/String;"); 680 g_dateOnlyFormatField = env->GetStaticFieldID(timeClass, "sDateOnlyFormat", "Ljava/lang/String;"); 681 g_dateTimeFormatField = env->GetStaticFieldID(timeClass, "sDateTimeFormat", "Ljava/lang/String;"); 682 g_amField = env->GetStaticFieldID(timeClass, "sAm", "Ljava/lang/String;"); 683 g_pmField = env->GetStaticFieldID(timeClass, "sPm", "Ljava/lang/String;"); 684 g_dateCommandField = env->GetStaticFieldID(timeClass, "sDateCommand", "Ljava/lang/String;"); 685 g_localeField = env->GetStaticFieldID(timeClass, "sLocale", "Ljava/util/Locale;"); 686 687 return AndroidRuntime::registerNativeMethods(env, "android/text/format/Time", gMethods, NELEM(gMethods)); 688 } 689 690 }; // namespace android 691