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 "ScopedStringChars.h" 27 #include "TimeUtils.h" 28 #include <nativehelper/JNIHelp.h> 29 #include <cutils/tztime.h> 30 31 namespace android { 32 33 static jfieldID g_allDayField = 0; 34 static jfieldID g_secField = 0; 35 static jfieldID g_minField = 0; 36 static jfieldID g_hourField = 0; 37 static jfieldID g_mdayField = 0; 38 static jfieldID g_monField = 0; 39 static jfieldID g_yearField = 0; 40 static jfieldID g_wdayField = 0; 41 static jfieldID g_ydayField = 0; 42 static jfieldID g_isdstField = 0; 43 static jfieldID g_gmtoffField = 0; 44 static jfieldID g_timezoneField = 0; 45 46 static jfieldID g_shortMonthsField = 0; 47 static jfieldID g_longMonthsField = 0; 48 static jfieldID g_longStandaloneMonthsField = 0; 49 static jfieldID g_shortWeekdaysField = 0; 50 static jfieldID g_longWeekdaysField = 0; 51 static jfieldID g_timeOnlyFormatField = 0; 52 static jfieldID g_dateOnlyFormatField = 0; 53 static jfieldID g_dateTimeFormatField = 0; 54 static jfieldID g_amField = 0; 55 static jfieldID g_pmField = 0; 56 static jfieldID g_dateCommandField = 0; 57 static jfieldID g_localeField = 0; 58 59 static jclass g_timeClass = NULL; 60 61 static inline bool java2time(JNIEnv* env, Time* t, jobject o) 62 { 63 t->t.tm_sec = env->GetIntField(o, g_secField); 64 t->t.tm_min = env->GetIntField(o, g_minField); 65 t->t.tm_hour = env->GetIntField(o, g_hourField); 66 t->t.tm_mday = env->GetIntField(o, g_mdayField); 67 t->t.tm_mon = env->GetIntField(o, g_monField); 68 t->t.tm_year = (env->GetIntField(o, g_yearField))-1900; 69 t->t.tm_wday = env->GetIntField(o, g_wdayField); 70 t->t.tm_yday = env->GetIntField(o, g_ydayField); 71 t->t.tm_isdst = env->GetIntField(o, g_isdstField); 72 t->t.tm_gmtoff = env->GetLongField(o, g_gmtoffField); 73 bool allDay = env->GetBooleanField(o, g_allDayField); 74 if (allDay && 75 ((t->t.tm_sec !=0) || (t->t.tm_min != 0) || (t->t.tm_hour != 0))) { 76 jniThrowException(env, "java/lang/IllegalArgumentException", 77 "allDay is true but sec, min, hour are not 0."); 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->GetBooleanField(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 // Calendar.JANUARY == 0. 243 js_mon[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i)); 244 locale.mon[i] = env->GetStringUTFChars(js_mon[i], NULL); 245 } 246 247 ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longMonthsField); 248 for (int i = 0; i < 12; i++) { 249 // Calendar.JANUARY == 0. 250 js_month[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i)); 251 locale.month[i] = env->GetStringUTFChars(js_month[i], NULL); 252 } 253 254 ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longStandaloneMonthsField); 255 for (int i = 0; i < 12; i++) { 256 // Calendar.JANUARY == 0. 257 js_standalone_month[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i)); 258 locale.standalone_month[i] = env->GetStringUTFChars(js_standalone_month[i], NULL); 259 } 260 261 ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortWeekdaysField); 262 for (int i = 0; i < 7; i++) { 263 // Calendar.SUNDAY == 1, and there's an empty string in element 0. 264 js_wday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i + 1)); 265 locale.wday[i] = env->GetStringUTFChars(js_wday[i], NULL); 266 } 267 268 ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longWeekdaysField); 269 for (int i = 0; i < 7; i++) { 270 // Calendar.SUNDAY == 1, and there's an empty string in element 0. 271 js_weekday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i + 1)); 272 locale.weekday[i] = env->GetStringUTFChars(js_weekday[i], NULL); 273 } 274 275 js_X_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField( 276 timeClass, g_timeOnlyFormatField)); 277 locale.X_fmt = env->GetStringUTFChars(js_X_fmt, NULL); 278 279 js_x_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField( 280 timeClass, g_dateOnlyFormatField)); 281 locale.x_fmt = env->GetStringUTFChars(js_x_fmt, NULL); 282 283 js_c_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField( 284 timeClass, g_dateTimeFormatField)); 285 locale.c_fmt = env->GetStringUTFChars(js_c_fmt, NULL); 286 287 js_am = (jstring) env->NewGlobalRef(env->GetStaticObjectField( 288 timeClass, g_amField)); 289 locale.am = env->GetStringUTFChars(js_am, NULL); 290 291 js_pm = (jstring) env->NewGlobalRef(env->GetStaticObjectField( 292 timeClass, g_pmField)); 293 locale.pm = env->GetStringUTFChars(js_pm, NULL); 294 295 js_date_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField( 296 timeClass, g_dateCommandField)); 297 locale.date_fmt = env->GetStringUTFChars(js_date_fmt, NULL); 298 } 299 300 ACQUIRE_TIMEZONE(This, t) 301 302 const char* format = env->GetStringUTFChars(formatObject, NULL); 303 304 String8 r = t.format(format, &locale); 305 306 env->ReleaseStringUTFChars(formatObject, format); 307 RELEASE_TIMEZONE(This, t) 308 309 return env->NewStringUTF(r.string()); 310 } 311 312 313 static jstring android_text_format_Time_toString(JNIEnv* env, jobject This) 314 { 315 Time t; 316 if (!java2time(env, &t, This)) return env->NewStringUTF(""); 317 ACQUIRE_TIMEZONE(This, t) 318 319 String8 r = t.toString(); 320 321 RELEASE_TIMEZONE(This, t) 322 323 return env->NewStringUTF(r.string()); 324 } 325 326 static void android_text_format_Time_setToNow(JNIEnv* env, jobject This) 327 { 328 env->SetBooleanField(This, g_allDayField, JNI_FALSE); 329 Time t; 330 ACQUIRE_TIMEZONE(This, t) 331 332 t.setToNow(); 333 334 time2java(env, This, t); 335 RELEASE_TIMEZONE(This, t) 336 } 337 338 static jlong android_text_format_Time_toMillis(JNIEnv* env, jobject This, 339 jboolean ignoreDst) 340 { 341 Time t; 342 if (!java2time(env, &t, This)) return 0L; 343 ACQUIRE_TIMEZONE(This, t) 344 345 int64_t result = t.toMillis(ignoreDst != 0); 346 347 RELEASE_TIMEZONE(This, t) 348 349 return result; 350 } 351 352 static void android_text_format_Time_set(JNIEnv* env, jobject This, jlong millis) 353 { 354 env->SetBooleanField(This, g_allDayField, JNI_FALSE); 355 Time t; 356 ACQUIRE_TIMEZONE(This, t) 357 358 t.set(millis); 359 360 time2java(env, This, t); 361 RELEASE_TIMEZONE(This, t) 362 } 363 364 365 // ============================================================================ 366 // Just do this here because it's not worth recreating the strings 367 368 static int get_char(JNIEnv* env, const ScopedStringChars& s, int spos, int mul, 369 bool* thrown) 370 { 371 jchar c = s[spos]; 372 if (c >= '0' && c <= '9') { 373 return (c - '0') * mul; 374 } else { 375 if (!*thrown) { 376 jniThrowExceptionFmt(env, "android/util/TimeFormatException", 377 "Parse error at pos=%d", spos); 378 *thrown = true; 379 } 380 return 0; 381 } 382 } 383 384 static bool check_char(JNIEnv* env, const ScopedStringChars& s, int spos, jchar expected) 385 { 386 jchar c = s[spos]; 387 if (c != expected) { 388 jniThrowExceptionFmt(env, "android/util/TimeFormatException", 389 "Unexpected character 0x%02x at pos=%d. Expected %c.", 390 c, spos, expected); 391 return false; 392 } 393 return true; 394 } 395 396 397 static jboolean android_text_format_Time_parse(JNIEnv* env, jobject This, jstring strObj) 398 { 399 jsize len = env->GetStringLength(strObj); 400 if (len < 8) { 401 jniThrowException(env, "android/util/TimeFormatException", 402 "String too short -- expected at least 8 characters."); 403 return false; 404 } 405 406 jboolean inUtc = false; 407 408 ScopedStringChars s(env, strObj); 409 410 // year 411 int n; 412 bool thrown = false; 413 n = get_char(env, s, 0, 1000, &thrown); 414 n += get_char(env, s, 1, 100, &thrown); 415 n += get_char(env, s, 2, 10, &thrown); 416 n += get_char(env, s, 3, 1, &thrown); 417 if (thrown) return false; 418 env->SetIntField(This, g_yearField, n); 419 420 // month 421 n = get_char(env, s, 4, 10, &thrown); 422 n += get_char(env, s, 5, 1, &thrown); 423 n--; 424 if (thrown) return false; 425 env->SetIntField(This, g_monField, n); 426 427 // day of month 428 n = get_char(env, s, 6, 10, &thrown); 429 n += get_char(env, s, 7, 1, &thrown); 430 if (thrown) return false; 431 env->SetIntField(This, g_mdayField, n); 432 433 if (len > 8) { 434 // T 435 if (!check_char(env, s, 8, 'T')) return false; 436 env->SetBooleanField(This, g_allDayField, JNI_FALSE); 437 438 // hour 439 n = get_char(env, s, 9, 10, &thrown); 440 n += get_char(env, s, 10, 1, &thrown); 441 if (thrown) return false; 442 env->SetIntField(This, g_hourField, n); 443 444 // min 445 n = get_char(env, s, 11, 10, &thrown); 446 n += get_char(env, s, 12, 1, &thrown); 447 if (thrown) return false; 448 env->SetIntField(This, g_minField, n); 449 450 // sec 451 n = get_char(env, s, 13, 10, &thrown); 452 n += get_char(env, s, 14, 1, &thrown); 453 if (thrown) return false; 454 env->SetIntField(This, g_secField, n); 455 456 if (len > 15) { 457 // Z 458 if (!check_char(env, s, 15, 'Z')) return false; 459 inUtc = true; 460 } 461 } else { 462 env->SetBooleanField(This, g_allDayField, JNI_TRUE); 463 env->SetIntField(This, g_hourField, 0); 464 env->SetIntField(This, g_minField, 0); 465 env->SetIntField(This, g_secField, 0); 466 } 467 468 env->SetIntField(This, g_wdayField, 0); 469 env->SetIntField(This, g_ydayField, 0); 470 env->SetIntField(This, g_isdstField, -1); 471 env->SetLongField(This, g_gmtoffField, 0); 472 473 return inUtc; 474 } 475 476 static jboolean android_text_format_Time_parse3339(JNIEnv* env, 477 jobject This, 478 jstring strObj) 479 { 480 jsize len = env->GetStringLength(strObj); 481 if (len < 10) { 482 jniThrowException(env, "android/util/TimeFormatException", 483 "String too short --- expected at least 10 characters."); 484 return false; 485 } 486 487 jboolean inUtc = false; 488 489 ScopedStringChars s(env, strObj); 490 491 // year 492 int n; 493 bool thrown = false; 494 n = get_char(env, s, 0, 1000, &thrown); 495 n += get_char(env, s, 1, 100, &thrown); 496 n += get_char(env, s, 2, 10, &thrown); 497 n += get_char(env, s, 3, 1, &thrown); 498 if (thrown) return false; 499 env->SetIntField(This, g_yearField, n); 500 501 // - 502 if (!check_char(env, s, 4, '-')) return false; 503 504 // month 505 n = get_char(env, s, 5, 10, &thrown); 506 n += get_char(env, s, 6, 1, &thrown); 507 --n; 508 if (thrown) return false; 509 env->SetIntField(This, g_monField, n); 510 511 // - 512 if (!check_char(env, s, 7, '-')) return false; 513 514 // day 515 n = get_char(env, s, 8, 10, &thrown); 516 n += get_char(env, s, 9, 1, &thrown); 517 if (thrown) return false; 518 env->SetIntField(This, g_mdayField, n); 519 520 if (len >= 19) { 521 // T 522 if (!check_char(env, s, 10, 'T')) return false; 523 524 env->SetBooleanField(This, g_allDayField, JNI_FALSE); 525 // hour 526 n = get_char(env, s, 11, 10, &thrown); 527 n += get_char(env, s, 12, 1, &thrown); 528 if (thrown) return false; 529 int hour = n; 530 // env->SetIntField(This, g_hourField, n); 531 532 // : 533 if (!check_char(env, s, 13, ':')) return false; 534 535 // minute 536 n = get_char(env, s, 14, 10, &thrown); 537 n += get_char(env, s, 15, 1, &thrown); 538 if (thrown) return false; 539 int minute = n; 540 // env->SetIntField(This, g_minField, n); 541 542 // : 543 if (!check_char(env, s, 16, ':')) return false; 544 545 // second 546 n = get_char(env, s, 17, 10, &thrown); 547 n += get_char(env, s, 18, 1, &thrown); 548 if (thrown) return false; 549 env->SetIntField(This, g_secField, n); 550 551 // skip the '.XYZ' -- we don't care about subsecond precision. 552 int tz_index = 19; 553 if (tz_index < len && s[tz_index] == '.') { 554 do { 555 tz_index++; 556 } while (tz_index < len 557 && s[tz_index] >= '0' 558 && s[tz_index] <= '9'); 559 } 560 561 int offset = 0; 562 if (len > tz_index) { 563 char c = s[tz_index]; 564 565 // NOTE: the offset is meant to be subtracted to get from local time 566 // to UTC. we therefore use 1 for '-' and -1 for '+'. 567 switch (c) { 568 case 'Z': 569 // Zulu time -- UTC 570 offset = 0; 571 break; 572 case '-': 573 offset = 1; 574 break; 575 case '+': 576 offset = -1; 577 break; 578 default: 579 jniThrowExceptionFmt(env, "android/util/TimeFormatException", 580 "Unexpected character 0x%02x at position %d. Expected + or -", 581 c, tz_index); 582 return false; 583 } 584 inUtc = true; 585 586 if (offset != 0) { 587 if (len < tz_index + 6) { 588 jniThrowExceptionFmt(env, "android/util/TimeFormatException", 589 "Unexpected length; should be %d characters", 590 tz_index + 6); 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 return inUtc; 633 } 634 635 // ============================================================================ 636 /* 637 * JNI registration. 638 */ 639 static JNINativeMethod gMethods[] = { 640 /* name, signature, funcPtr */ 641 { "normalize", "(Z)J", (void*)android_text_format_Time_normalize }, 642 { "switchTimezone", "(Ljava/lang/String;)V", (void*)android_text_format_Time_switchTimezone }, 643 { "nativeCompare", "(Landroid/text/format/Time;Landroid/text/format/Time;)I", (void*)android_text_format_Time_compare }, 644 { "format1", "(Ljava/lang/String;)Ljava/lang/String;", (void*)android_text_format_Time_format }, 645 { "format2445", "()Ljava/lang/String;", (void*)android_text_format_Time_format2445 }, 646 { "toString", "()Ljava/lang/String;", (void*)android_text_format_Time_toString }, 647 { "nativeParse", "(Ljava/lang/String;)Z", (void*)android_text_format_Time_parse }, 648 { "nativeParse3339", "(Ljava/lang/String;)Z", (void*)android_text_format_Time_parse3339 }, 649 { "setToNow", "()V", (void*)android_text_format_Time_setToNow }, 650 { "toMillis", "(Z)J", (void*)android_text_format_Time_toMillis }, 651 { "set", "(J)V", (void*)android_text_format_Time_set } 652 }; 653 654 int register_android_text_format_Time(JNIEnv* env) 655 { 656 jclass timeClass = env->FindClass("android/text/format/Time"); 657 658 g_timeClass = (jclass) env->NewGlobalRef(timeClass); 659 660 g_allDayField = env->GetFieldID(timeClass, "allDay", "Z"); 661 g_secField = env->GetFieldID(timeClass, "second", "I"); 662 g_minField = env->GetFieldID(timeClass, "minute", "I"); 663 g_hourField = env->GetFieldID(timeClass, "hour", "I"); 664 g_mdayField = env->GetFieldID(timeClass, "monthDay", "I"); 665 g_monField = env->GetFieldID(timeClass, "month", "I"); 666 g_yearField = env->GetFieldID(timeClass, "year", "I"); 667 g_wdayField = env->GetFieldID(timeClass, "weekDay", "I"); 668 g_ydayField = env->GetFieldID(timeClass, "yearDay", "I"); 669 g_isdstField = env->GetFieldID(timeClass, "isDst", "I"); 670 g_gmtoffField = env->GetFieldID(timeClass, "gmtoff", "J"); 671 g_timezoneField = env->GetFieldID(timeClass, "timezone", "Ljava/lang/String;"); 672 673 g_shortMonthsField = env->GetStaticFieldID(timeClass, "sShortMonths", "[Ljava/lang/String;"); 674 g_longMonthsField = env->GetStaticFieldID(timeClass, "sLongMonths", "[Ljava/lang/String;"); 675 g_longStandaloneMonthsField = env->GetStaticFieldID(timeClass, "sLongStandaloneMonths", "[Ljava/lang/String;"); 676 g_shortWeekdaysField = env->GetStaticFieldID(timeClass, "sShortWeekdays", "[Ljava/lang/String;"); 677 g_longWeekdaysField = env->GetStaticFieldID(timeClass, "sLongWeekdays", "[Ljava/lang/String;"); 678 g_timeOnlyFormatField = env->GetStaticFieldID(timeClass, "sTimeOnlyFormat", "Ljava/lang/String;"); 679 g_dateOnlyFormatField = env->GetStaticFieldID(timeClass, "sDateOnlyFormat", "Ljava/lang/String;"); 680 g_dateTimeFormatField = env->GetStaticFieldID(timeClass, "sDateTimeFormat", "Ljava/lang/String;"); 681 g_amField = env->GetStaticFieldID(timeClass, "sAm", "Ljava/lang/String;"); 682 g_pmField = env->GetStaticFieldID(timeClass, "sPm", "Ljava/lang/String;"); 683 g_dateCommandField = env->GetStaticFieldID(timeClass, "sDateCommand", "Ljava/lang/String;"); 684 g_localeField = env->GetStaticFieldID(timeClass, "sLocale", "Ljava/util/Locale;"); 685 686 return AndroidRuntime::registerNativeMethods(env, "android/text/format/Time", gMethods, NELEM(gMethods)); 687 } 688 689 }; // namespace android 690