1 /*
2 * Copyright (C) 2008 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_NDEBUG 1
18 #define LOG_TAG "ICU"
19
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <string>
25 #include <sys/mman.h>
26 #include <sys/stat.h>
27 #include <sys/time.h>
28 #include <sys/types.h>
29 #include <time.h>
30 #include <unistd.h>
31
32 #include <memory>
33 #include <vector>
34
35 #include <android-base/unique_fd.h>
36 #include <log/log.h>
37 #include <nativehelper/JNIHelp.h>
38 #include <nativehelper/ScopedLocalRef.h>
39 #include <nativehelper/ScopedUtfChars.h>
40 #include <nativehelper/jni_macros.h>
41 #include <nativehelper/toStringArray.h>
42
43 #include "IcuUtilities.h"
44 #include "JniConstants.h"
45 #include "JniException.h"
46 #include "ScopedIcuLocale.h"
47 #include "ScopedJavaUnicodeString.h"
48 #include "unicode/brkiter.h"
49 #include "unicode/calendar.h"
50 #include "unicode/datefmt.h"
51 #include "unicode/dcfmtsym.h"
52 #include "unicode/decimfmt.h"
53 #include "unicode/dtfmtsym.h"
54 #include "unicode/dtptngen.h"
55 #include "unicode/gregocal.h"
56 #include "unicode/locid.h"
57 #include "unicode/numfmt.h"
58 #include "unicode/strenum.h"
59 #include "unicode/timezone.h"
60 #include "unicode/ubrk.h"
61 #include "unicode/ucal.h"
62 #include "unicode/ucasemap.h"
63 #include "unicode/uclean.h"
64 #include "unicode/ucol.h"
65 #include "unicode/ucurr.h"
66 #include "unicode/udat.h"
67 #include "unicode/uloc.h"
68 #include "unicode/ulocdata.h"
69 #include "unicode/ures.h"
70 #include "unicode/ustring.h"
71 #include "ureslocs.h"
72 #include "valueOf.h"
73
74 class ScopedResourceBundle {
75 public:
ScopedResourceBundle(UResourceBundle * bundle)76 explicit ScopedResourceBundle(UResourceBundle* bundle) : bundle_(bundle) {
77 }
78
~ScopedResourceBundle()79 ~ScopedResourceBundle() {
80 if (bundle_ != NULL) {
81 ures_close(bundle_);
82 }
83 }
84
get()85 UResourceBundle* get() {
86 return bundle_;
87 }
88
hasKey(const char * key)89 bool hasKey(const char* key) {
90 UErrorCode status = U_ZERO_ERROR;
91 ures_getStringByKey(bundle_, key, NULL, &status);
92 return U_SUCCESS(status);
93 }
94
95 private:
96 UResourceBundle* bundle_;
97 DISALLOW_COPY_AND_ASSIGN(ScopedResourceBundle);
98 };
99
ICU_addLikelySubtags(JNIEnv * env,jclass,jstring javaLocaleName)100 static jstring ICU_addLikelySubtags(JNIEnv* env, jclass, jstring javaLocaleName) {
101 UErrorCode status = U_ZERO_ERROR;
102 ScopedUtfChars localeID(env, javaLocaleName);
103 char maximizedLocaleID[ULOC_FULLNAME_CAPACITY];
104 uloc_addLikelySubtags(localeID.c_str(), maximizedLocaleID, sizeof(maximizedLocaleID), &status);
105 if (U_FAILURE(status)) {
106 return javaLocaleName;
107 }
108 return env->NewStringUTF(maximizedLocaleID);
109 }
110
ICU_getScript(JNIEnv * env,jclass,jstring javaLocaleName)111 static jstring ICU_getScript(JNIEnv* env, jclass, jstring javaLocaleName) {
112 ScopedIcuLocale icuLocale(env, javaLocaleName);
113 if (!icuLocale.valid()) {
114 return NULL;
115 }
116 return env->NewStringUTF(icuLocale.locale().getScript());
117 }
118
ICU_getCurrencyFractionDigits(JNIEnv * env,jclass,jstring javaCurrencyCode)119 static jint ICU_getCurrencyFractionDigits(JNIEnv* env, jclass, jstring javaCurrencyCode) {
120 ScopedJavaUnicodeString currencyCode(env, javaCurrencyCode);
121 if (!currencyCode.valid()) {
122 return 0;
123 }
124 icu::UnicodeString icuCurrencyCode(currencyCode.unicodeString());
125 UErrorCode status = U_ZERO_ERROR;
126 return ucurr_getDefaultFractionDigits(icuCurrencyCode.getTerminatedBuffer(), &status);
127 }
128
ICU_getCurrencyNumericCode(JNIEnv * env,jclass,jstring javaCurrencyCode)129 static jint ICU_getCurrencyNumericCode(JNIEnv* env, jclass, jstring javaCurrencyCode) {
130 ScopedJavaUnicodeString currencyCode(env, javaCurrencyCode);
131 if (!currencyCode.valid()) {
132 return 0;
133 }
134 icu::UnicodeString icuCurrencyCode(currencyCode.unicodeString());
135 return ucurr_getNumericCode(icuCurrencyCode.getTerminatedBuffer());
136 }
137
138 // TODO: rewrite this with int32_t ucurr_forLocale(const char* locale, UChar* buff, int32_t buffCapacity, UErrorCode* ec)...
ICU_getCurrencyCode(JNIEnv * env,jclass,jstring javaCountryCode)139 static jstring ICU_getCurrencyCode(JNIEnv* env, jclass, jstring javaCountryCode) {
140 UErrorCode status = U_ZERO_ERROR;
141 ScopedResourceBundle supplData(ures_openDirect(U_ICUDATA_CURR, "supplementalData", &status));
142 if (U_FAILURE(status)) {
143 return NULL;
144 }
145
146 ScopedResourceBundle currencyMap(ures_getByKey(supplData.get(), "CurrencyMap", NULL, &status));
147 if (U_FAILURE(status)) {
148 return NULL;
149 }
150
151 ScopedUtfChars countryCode(env, javaCountryCode);
152 ScopedResourceBundle currency(ures_getByKey(currencyMap.get(), countryCode.c_str(), NULL, &status));
153 if (U_FAILURE(status)) {
154 return NULL;
155 }
156
157 ScopedResourceBundle currencyElem(ures_getByIndex(currency.get(), 0, NULL, &status));
158 if (U_FAILURE(status)) {
159 return env->NewStringUTF("XXX");
160 }
161
162 // Check if there's a 'to' date. If there is, the currency isn't used anymore.
163 ScopedResourceBundle currencyTo(ures_getByKey(currencyElem.get(), "to", NULL, &status));
164 if (!U_FAILURE(status)) {
165 return NULL;
166 }
167 // Ignore the failure to find a 'to' date.
168 status = U_ZERO_ERROR;
169
170 ScopedResourceBundle currencyId(ures_getByKey(currencyElem.get(), "id", NULL, &status));
171 if (U_FAILURE(status)) {
172 // No id defined for this country
173 return env->NewStringUTF("XXX");
174 }
175
176 int32_t charCount;
177 const UChar* chars = ures_getString(currencyId.get(), &charCount, &status);
178 return (charCount == 0) ? env->NewStringUTF("XXX") : jniCreateString(env, chars, charCount);
179 }
180
getCurrencyName(JNIEnv * env,jstring javaLanguageTag,jstring javaCurrencyCode,UCurrNameStyle nameStyle)181 static jstring getCurrencyName(JNIEnv* env, jstring javaLanguageTag, jstring javaCurrencyCode, UCurrNameStyle nameStyle) {
182 ScopedUtfChars languageTag(env, javaLanguageTag);
183 if (languageTag.c_str() == NULL) {
184 return NULL;
185 }
186 ScopedJavaUnicodeString currencyCode(env, javaCurrencyCode);
187 if (!currencyCode.valid()) {
188 return NULL;
189 }
190 icu::UnicodeString icuCurrencyCode(currencyCode.unicodeString());
191 UErrorCode status = U_ZERO_ERROR;
192 UBool isChoiceFormat = false;
193 int32_t charCount;
194 const UChar* chars = ucurr_getName(icuCurrencyCode.getTerminatedBuffer(), languageTag.c_str(),
195 nameStyle, &isChoiceFormat, &charCount, &status);
196 if (status == U_USING_DEFAULT_WARNING) {
197 if (nameStyle == UCURR_SYMBOL_NAME) {
198 // ICU doesn't distinguish between falling back to the root locale and meeting a genuinely
199 // unknown currency. The Currency class does.
200 if (!ucurr_isAvailable(icuCurrencyCode.getTerminatedBuffer(), U_DATE_MIN, U_DATE_MAX, &status)) {
201 return NULL;
202 }
203 }
204 if (nameStyle == UCURR_LONG_NAME) {
205 // ICU's default is English. We want the ISO 4217 currency code instead.
206 chars = icuCurrencyCode.getBuffer();
207 charCount = icuCurrencyCode.length();
208 }
209 }
210 return (charCount == 0) ? NULL : jniCreateString(env, chars, charCount);
211 }
212
ICU_getCurrencyDisplayName(JNIEnv * env,jclass,jstring javaLanguageTag,jstring javaCurrencyCode)213 static jstring ICU_getCurrencyDisplayName(JNIEnv* env, jclass, jstring javaLanguageTag, jstring javaCurrencyCode) {
214 return getCurrencyName(env, javaLanguageTag, javaCurrencyCode, UCURR_LONG_NAME);
215 }
216
ICU_getCurrencySymbol(JNIEnv * env,jclass,jstring javaLanguageTag,jstring javaCurrencyCode)217 static jstring ICU_getCurrencySymbol(JNIEnv* env, jclass, jstring javaLanguageTag, jstring javaCurrencyCode) {
218 return getCurrencyName(env, javaLanguageTag, javaCurrencyCode, UCURR_SYMBOL_NAME);
219 }
220
ICU_getDisplayCountryNative(JNIEnv * env,jclass,jstring javaTargetLanguageTag,jstring javaLanguageTag)221 static jstring ICU_getDisplayCountryNative(JNIEnv* env, jclass, jstring javaTargetLanguageTag, jstring javaLanguageTag) {
222 ScopedIcuLocale icuLocale(env, javaLanguageTag);
223 if (!icuLocale.valid()) {
224 return NULL;
225 }
226 ScopedIcuLocale icuTargetLocale(env, javaTargetLanguageTag);
227 if (!icuTargetLocale.valid()) {
228 return NULL;
229 }
230
231 icu::UnicodeString str;
232 icuTargetLocale.locale().getDisplayCountry(icuLocale.locale(), str);
233 return jniCreateString(env, str.getBuffer(), str.length());
234 }
235
ICU_getDisplayLanguageNative(JNIEnv * env,jclass,jstring javaTargetLanguageTag,jstring javaLanguageTag)236 static jstring ICU_getDisplayLanguageNative(JNIEnv* env, jclass, jstring javaTargetLanguageTag, jstring javaLanguageTag) {
237 ScopedIcuLocale icuLocale(env, javaLanguageTag);
238 if (!icuLocale.valid()) {
239 return NULL;
240 }
241 ScopedIcuLocale icuTargetLocale(env, javaTargetLanguageTag);
242 if (!icuTargetLocale.valid()) {
243 return NULL;
244 }
245
246 icu::UnicodeString str;
247 icuTargetLocale.locale().getDisplayLanguage(icuLocale.locale(), str);
248 return jniCreateString(env, str.getBuffer(), str.length());
249 }
250
ICU_getDisplayScriptNative(JNIEnv * env,jclass,jstring javaTargetLanguageTag,jstring javaLanguageTag)251 static jstring ICU_getDisplayScriptNative(JNIEnv* env, jclass, jstring javaTargetLanguageTag, jstring javaLanguageTag) {
252 ScopedIcuLocale icuLocale(env, javaLanguageTag);
253 if (!icuLocale.valid()) {
254 return NULL;
255 }
256 ScopedIcuLocale icuTargetLocale(env, javaTargetLanguageTag);
257 if (!icuTargetLocale.valid()) {
258 return NULL;
259 }
260
261 icu::UnicodeString str;
262 icuTargetLocale.locale().getDisplayScript(icuLocale.locale(), str);
263 return jniCreateString(env, str.getBuffer(), str.length());
264 }
265
ICU_getDisplayVariantNative(JNIEnv * env,jclass,jstring javaTargetLanguageTag,jstring javaLanguageTag)266 static jstring ICU_getDisplayVariantNative(JNIEnv* env, jclass, jstring javaTargetLanguageTag, jstring javaLanguageTag) {
267 ScopedIcuLocale icuLocale(env, javaLanguageTag);
268 if (!icuLocale.valid()) {
269 return NULL;
270 }
271 ScopedIcuLocale icuTargetLocale(env, javaTargetLanguageTag);
272 if (!icuTargetLocale.valid()) {
273 return NULL;
274 }
275
276 icu::UnicodeString str;
277 icuTargetLocale.locale().getDisplayVariant(icuLocale.locale(), str);
278 return jniCreateString(env, str.getBuffer(), str.length());
279 }
280
ICU_getISO3Country(JNIEnv * env,jclass,jstring javaLanguageTag)281 static jstring ICU_getISO3Country(JNIEnv* env, jclass, jstring javaLanguageTag) {
282 ScopedIcuLocale icuLocale(env, javaLanguageTag);
283 if (!icuLocale.valid()) {
284 return NULL;
285 }
286 return env->NewStringUTF(icuLocale.locale().getISO3Country());
287 }
288
ICU_getISO3Language(JNIEnv * env,jclass,jstring javaLanguageTag)289 static jstring ICU_getISO3Language(JNIEnv* env, jclass, jstring javaLanguageTag) {
290 ScopedIcuLocale icuLocale(env, javaLanguageTag);
291 if (!icuLocale.valid()) {
292 return NULL;
293 }
294 return env->NewStringUTF(icuLocale.locale().getISO3Language());
295 }
296
ICU_getISOCountriesNative(JNIEnv * env,jclass)297 static jobjectArray ICU_getISOCountriesNative(JNIEnv* env, jclass) {
298 return toStringArray(env, icu::Locale::getISOCountries());
299 }
300
ICU_getISOLanguagesNative(JNIEnv * env,jclass)301 static jobjectArray ICU_getISOLanguagesNative(JNIEnv* env, jclass) {
302 return toStringArray(env, icu::Locale::getISOLanguages());
303 }
304
ICU_getAvailableLocalesNative(JNIEnv * env,jclass)305 static jobjectArray ICU_getAvailableLocalesNative(JNIEnv* env, jclass) {
306 return toStringArray(env, uloc_countAvailable, uloc_getAvailable);
307 }
308
ICU_getAvailableBreakIteratorLocalesNative(JNIEnv * env,jclass)309 static jobjectArray ICU_getAvailableBreakIteratorLocalesNative(JNIEnv* env, jclass) {
310 return toStringArray(env, ubrk_countAvailable, ubrk_getAvailable);
311 }
312
ICU_getAvailableCalendarLocalesNative(JNIEnv * env,jclass)313 static jobjectArray ICU_getAvailableCalendarLocalesNative(JNIEnv* env, jclass) {
314 return toStringArray(env, ucal_countAvailable, ucal_getAvailable);
315 }
316
ICU_getAvailableCollatorLocalesNative(JNIEnv * env,jclass)317 static jobjectArray ICU_getAvailableCollatorLocalesNative(JNIEnv* env, jclass) {
318 return toStringArray(env, ucol_countAvailable, ucol_getAvailable);
319 }
320
ICU_getAvailableDateFormatLocalesNative(JNIEnv * env,jclass)321 static jobjectArray ICU_getAvailableDateFormatLocalesNative(JNIEnv* env, jclass) {
322 return toStringArray(env, udat_countAvailable, udat_getAvailable);
323 }
324
ICU_getAvailableNumberFormatLocalesNative(JNIEnv * env,jclass)325 static jobjectArray ICU_getAvailableNumberFormatLocalesNative(JNIEnv* env, jclass) {
326 return toStringArray(env, unum_countAvailable, unum_getAvailable);
327 }
328
setIntegerField(JNIEnv * env,jobject obj,const char * fieldName,int value)329 static bool setIntegerField(JNIEnv* env, jobject obj, const char* fieldName, int value) {
330 ScopedLocalRef<jobject> integerValue(env, integerValueOf(env, value));
331 if (integerValue.get() == NULL) return false;
332 jfieldID fid = env->GetFieldID(JniConstants::GetLocaleDataClass(env), fieldName, "Ljava/lang/Integer;");
333 env->SetObjectField(obj, fid, integerValue.get());
334 return true;
335 }
336
setStringField(JNIEnv * env,jobject obj,const char * fieldName,jstring value)337 static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, jstring value) {
338 jfieldID fid = env->GetFieldID(JniConstants::GetLocaleDataClass(env), fieldName, "Ljava/lang/String;");
339 env->SetObjectField(obj, fid, value);
340 env->DeleteLocalRef(value);
341 }
342
setStringArrayField(JNIEnv * env,jobject obj,const char * fieldName,jobjectArray value)343 static void setStringArrayField(JNIEnv* env, jobject obj, const char* fieldName, jobjectArray value) {
344 jfieldID fid = env->GetFieldID(JniConstants::GetLocaleDataClass(env), fieldName, "[Ljava/lang/String;");
345 env->SetObjectField(obj, fid, value);
346 }
347
setStringArrayField(JNIEnv * env,jobject obj,const char * fieldName,const icu::UnicodeString * valueArray,int32_t size)348 static void setStringArrayField(JNIEnv* env, jobject obj, const char* fieldName, const icu::UnicodeString* valueArray, int32_t size) {
349 ScopedLocalRef<jobjectArray> result(env, env->NewObjectArray(size, JniConstants::GetStringClass(env), NULL));
350 for (int32_t i = 0; i < size ; i++) {
351 ScopedLocalRef<jstring> s(env, jniCreateString(env, valueArray[i].getBuffer(),valueArray[i].length()));
352 if (env->ExceptionCheck()) {
353 return;
354 }
355 env->SetObjectArrayElement(result.get(), i, s.get());
356 if (env->ExceptionCheck()) {
357 return;
358 }
359 }
360 setStringArrayField(env, obj, fieldName, result.get());
361 }
362
setStringField(JNIEnv * env,jobject obj,const char * fieldName,UResourceBundle * bundle,int index)363 static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, UResourceBundle* bundle, int index) {
364 UErrorCode status = U_ZERO_ERROR;
365 int charCount;
366 const UChar* chars;
367 UResourceBundle* currentBundle = ures_getByIndex(bundle, index, NULL, &status);
368 switch (ures_getType(currentBundle)) {
369 case URES_STRING:
370 chars = ures_getString(currentBundle, &charCount, &status);
371 break;
372 case URES_ARRAY:
373 // In case there is an array, Android currently only cares about the
374 // first string of that array, the rest of the array is used by ICU
375 // for additional data ignored by Android.
376 chars = ures_getStringByIndex(currentBundle, 0, &charCount, &status);
377 break;
378 default:
379 status = U_INVALID_FORMAT_ERROR;
380 }
381 ures_close(currentBundle);
382 if (U_SUCCESS(status)) {
383 setStringField(env, obj, fieldName, jniCreateString(env, chars, charCount));
384 } else {
385 ALOGE("Error setting String field %s from ICU resource (index %d): %s", fieldName, index, u_errorName(status));
386 }
387 }
388
setCharField(JNIEnv * env,jobject obj,const char * fieldName,const icu::UnicodeString & value)389 static void setCharField(JNIEnv* env, jobject obj, const char* fieldName, const icu::UnicodeString& value) {
390 if (value.length() == 0) {
391 return;
392 }
393 jfieldID fid = env->GetFieldID(JniConstants::GetLocaleDataClass(env), fieldName, "C");
394 env->SetCharField(obj, fid, value.charAt(0));
395 }
396
setStringField(JNIEnv * env,jobject obj,const char * fieldName,const icu::UnicodeString & value)397 static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, const icu::UnicodeString& value) {
398 const UChar* chars = value.getBuffer();
399 setStringField(env, obj, fieldName, jniCreateString(env, chars, value.length()));
400 }
401
setNumberPatterns(JNIEnv * env,jobject obj,icu::Locale & locale)402 static void setNumberPatterns(JNIEnv* env, jobject obj, icu::Locale& locale) {
403 UErrorCode status = U_ZERO_ERROR;
404
405 icu::UnicodeString pattern;
406 std::unique_ptr<icu::DecimalFormat> fmt(static_cast<icu::DecimalFormat*>(icu::NumberFormat::createInstance(locale, UNUM_CURRENCY, status)));
407 pattern = fmt->toPattern(pattern.remove());
408 setStringField(env, obj, "currencyPattern", pattern);
409
410 fmt.reset(static_cast<icu::DecimalFormat*>(icu::NumberFormat::createInstance(locale, UNUM_DECIMAL, status)));
411 pattern = fmt->toPattern(pattern.remove());
412 setStringField(env, obj, "numberPattern", pattern);
413
414 fmt.reset(static_cast<icu::DecimalFormat*>(icu::NumberFormat::createInstance(locale, UNUM_PERCENT, status)));
415 pattern = fmt->toPattern(pattern.remove());
416 setStringField(env, obj, "percentPattern", pattern);
417 }
418
setDecimalFormatSymbolsData(JNIEnv * env,jobject obj,icu::Locale & locale)419 static void setDecimalFormatSymbolsData(JNIEnv* env, jobject obj, icu::Locale& locale) {
420 UErrorCode status = U_ZERO_ERROR;
421 icu::DecimalFormatSymbols dfs(locale, status);
422
423 setCharField(env, obj, "decimalSeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kDecimalSeparatorSymbol));
424 setCharField(env, obj, "groupingSeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kGroupingSeparatorSymbol));
425 setCharField(env, obj, "patternSeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kPatternSeparatorSymbol));
426 setStringField(env, obj, "percent", dfs.getSymbol(icu::DecimalFormatSymbols::kPercentSymbol));
427 setStringField(env, obj, "perMill", dfs.getSymbol(icu::DecimalFormatSymbols::kPerMillSymbol));
428 setCharField(env, obj, "monetarySeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kMonetarySeparatorSymbol));
429 setStringField(env, obj, "minusSign", dfs.getSymbol(icu::DecimalFormatSymbols:: kMinusSignSymbol));
430 setStringField(env, obj, "exponentSeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kExponentialSymbol));
431 setStringField(env, obj, "infinity", dfs.getSymbol(icu::DecimalFormatSymbols::kInfinitySymbol));
432 setStringField(env, obj, "NaN", dfs.getSymbol(icu::DecimalFormatSymbols::kNaNSymbol));
433 setCharField(env, obj, "zeroDigit", dfs.getSymbol(icu::DecimalFormatSymbols::kZeroDigitSymbol));
434 }
435
436
437 // Iterates up through the locale hierarchy. So "en_US" would return "en_US", "en", "".
438 class LocaleNameIterator {
439 public:
LocaleNameIterator(const char * locale_name,UErrorCode & status)440 LocaleNameIterator(const char* locale_name, UErrorCode& status) : status_(status), has_next_(true) {
441 strcpy(locale_name_, locale_name);
442 locale_name_length_ = strlen(locale_name_);
443 }
444
Get()445 const char* Get() {
446 return locale_name_;
447 }
448
HasNext()449 bool HasNext() {
450 return has_next_;
451 }
452
Up()453 void Up() {
454 if (locale_name_length_ == 0) {
455 has_next_ = false;
456 } else {
457 locale_name_length_ = uloc_getParent(locale_name_, locale_name_, sizeof(locale_name_), &status_);
458 }
459 }
460
461 private:
462 UErrorCode& status_;
463 bool has_next_;
464 char locale_name_[ULOC_FULLNAME_CAPACITY];
465 int32_t locale_name_length_;
466
467 DISALLOW_COPY_AND_ASSIGN(LocaleNameIterator);
468 };
469
getAmPmMarkersNarrow(JNIEnv * env,jobject localeData,const char * locale_name)470 static bool getAmPmMarkersNarrow(JNIEnv* env, jobject localeData, const char* locale_name) {
471 UErrorCode status = U_ZERO_ERROR;
472 ScopedResourceBundle root(ures_open(NULL, locale_name, &status));
473 if (U_FAILURE(status)) {
474 return false;
475 }
476 ScopedResourceBundle calendar(ures_getByKey(root.get(), "calendar", NULL, &status));
477 if (U_FAILURE(status)) {
478 return false;
479 }
480 ScopedResourceBundle gregorian(ures_getByKey(calendar.get(), "gregorian", NULL, &status));
481 if (U_FAILURE(status)) {
482 return false;
483 }
484 ScopedResourceBundle amPmMarkersNarrow(ures_getByKey(gregorian.get(), "AmPmMarkersNarrow", NULL, &status));
485 if (U_FAILURE(status)) {
486 return false;
487 }
488 setStringField(env, localeData, "narrowAm", amPmMarkersNarrow.get(), 0);
489 setStringField(env, localeData, "narrowPm", amPmMarkersNarrow.get(), 1);
490 return true;
491 }
492
getDateTimePatterns(JNIEnv * env,jobject localeData,const char * locale_name)493 static bool getDateTimePatterns(JNIEnv* env, jobject localeData, const char* locale_name) {
494 UErrorCode status = U_ZERO_ERROR;
495 ScopedResourceBundle root(ures_open(NULL, locale_name, &status));
496 if (U_FAILURE(status)) {
497 return false;
498 }
499 ScopedResourceBundle calendar(ures_getByKey(root.get(), "calendar", NULL, &status));
500 if (U_FAILURE(status)) {
501 return false;
502 }
503 ScopedResourceBundle gregorian(ures_getByKey(calendar.get(), "gregorian", NULL, &status));
504 if (U_FAILURE(status)) {
505 return false;
506 }
507 ScopedResourceBundle dateTimePatterns(ures_getByKey(gregorian.get(), "DateTimePatterns", NULL, &status));
508 if (U_FAILURE(status)) {
509 return false;
510 }
511 setStringField(env, localeData, "fullTimeFormat", dateTimePatterns.get(), 0);
512 setStringField(env, localeData, "longTimeFormat", dateTimePatterns.get(), 1);
513 setStringField(env, localeData, "mediumTimeFormat", dateTimePatterns.get(), 2);
514 setStringField(env, localeData, "shortTimeFormat", dateTimePatterns.get(), 3);
515 setStringField(env, localeData, "fullDateFormat", dateTimePatterns.get(), 4);
516 setStringField(env, localeData, "longDateFormat", dateTimePatterns.get(), 5);
517 setStringField(env, localeData, "mediumDateFormat", dateTimePatterns.get(), 6);
518 setStringField(env, localeData, "shortDateFormat", dateTimePatterns.get(), 7);
519 return true;
520 }
521
getYesterdayTodayAndTomorrow(JNIEnv * env,jobject localeData,const icu::Locale & locale,const char * locale_name)522 static bool getYesterdayTodayAndTomorrow(JNIEnv* env, jobject localeData, const icu::Locale& locale, const char* locale_name) {
523 UErrorCode status = U_ZERO_ERROR;
524 ScopedResourceBundle root(ures_open(NULL, locale_name, &status));
525 ScopedResourceBundle fields(ures_getByKey(root.get(), "fields", NULL, &status));
526 ScopedResourceBundle day(ures_getByKey(fields.get(), "day", NULL, &status));
527 ScopedResourceBundle relative(ures_getByKey(day.get(), "relative", NULL, &status));
528 if (U_FAILURE(status)) {
529 return false;
530 }
531
532 icu::UnicodeString yesterday(icu::ures_getUnicodeStringByKey(relative.get(), "-1", &status));
533 icu::UnicodeString today(icu::ures_getUnicodeStringByKey(relative.get(), "0", &status));
534 icu::UnicodeString tomorrow(icu::ures_getUnicodeStringByKey(relative.get(), "1", &status));
535 if (U_FAILURE(status)) {
536 ALOGE("Error getting yesterday/today/tomorrow for %s: %s", locale_name, u_errorName(status));
537 return false;
538 }
539
540 // We title-case the strings so they have consistent capitalization (http://b/14493853).
541 std::unique_ptr<icu::BreakIterator> brk(icu::BreakIterator::createSentenceInstance(locale, status));
542 if (U_FAILURE(status)) {
543 ALOGE("Error getting yesterday/today/tomorrow break iterator for %s: %s", locale_name, u_errorName(status));
544 return false;
545 }
546 yesterday.toTitle(brk.get(), locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
547 today.toTitle(brk.get(), locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
548 tomorrow.toTitle(brk.get(), locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
549
550 setStringField(env, localeData, "yesterday", yesterday);
551 setStringField(env, localeData, "today", today);
552 setStringField(env, localeData, "tomorrow", tomorrow);
553 return true;
554 }
555
ICU_initLocaleDataNative(JNIEnv * env,jclass,jstring javaLanguageTag,jobject localeData)556 static jboolean ICU_initLocaleDataNative(JNIEnv* env, jclass, jstring javaLanguageTag, jobject localeData) {
557 ScopedUtfChars languageTag(env, javaLanguageTag);
558 if (languageTag.c_str() == NULL) {
559 return JNI_FALSE;
560 }
561 if (languageTag.size() >= ULOC_FULLNAME_CAPACITY) {
562 return JNI_FALSE; // ICU has a fixed-length limit.
563 }
564
565 ScopedIcuLocale icuLocale(env, javaLanguageTag);
566 if (!icuLocale.valid()) {
567 return JNI_FALSE;
568 }
569
570 // Get the DateTimePatterns.
571 UErrorCode status = U_ZERO_ERROR;
572 bool foundDateTimePatterns = false;
573 for (LocaleNameIterator it(icuLocale.locale().getBaseName(), status); it.HasNext(); it.Up()) {
574 if (getDateTimePatterns(env, localeData, it.Get())) {
575 foundDateTimePatterns = true;
576 break;
577 }
578 }
579 if (!foundDateTimePatterns) {
580 ALOGE("Couldn't find ICU DateTimePatterns for %s", languageTag.c_str());
581 return JNI_FALSE;
582 }
583
584 // Get the "Yesterday", "Today", and "Tomorrow" strings.
585 bool foundYesterdayTodayAndTomorrow = false;
586 for (LocaleNameIterator it(icuLocale.locale().getBaseName(), status); it.HasNext(); it.Up()) {
587 if (getYesterdayTodayAndTomorrow(env, localeData, icuLocale.locale(), it.Get())) {
588 foundYesterdayTodayAndTomorrow = true;
589 break;
590 }
591 }
592 if (!foundYesterdayTodayAndTomorrow) {
593 ALOGE("Couldn't find ICU yesterday/today/tomorrow for %s", languageTag.c_str());
594 return JNI_FALSE;
595 }
596
597 // Get the narrow "AM" and "PM" strings.
598 bool foundAmPmMarkersNarrow = false;
599 for (LocaleNameIterator it(icuLocale.locale().getBaseName(), status); it.HasNext(); it.Up()) {
600 if (getAmPmMarkersNarrow(env, localeData, it.Get())) {
601 foundAmPmMarkersNarrow = true;
602 break;
603 }
604 }
605 if (!foundAmPmMarkersNarrow) {
606 ALOGE("Couldn't find ICU AmPmMarkersNarrow for %s", languageTag.c_str());
607 return JNI_FALSE;
608 }
609
610 status = U_ZERO_ERROR;
611 std::unique_ptr<icu::Calendar> cal(icu::Calendar::createInstance(icuLocale.locale(), status));
612 if (U_FAILURE(status)) {
613 return JNI_FALSE;
614 }
615 if (!setIntegerField(env, localeData, "firstDayOfWeek", cal->getFirstDayOfWeek())) {
616 return JNI_FALSE;
617 }
618 if (!setIntegerField(env, localeData, "minimalDaysInFirstWeek", cal->getMinimalDaysInFirstWeek())) {
619 return JNI_FALSE;
620 }
621
622 // Get DateFormatSymbols.
623 status = U_ZERO_ERROR;
624 icu::DateFormatSymbols dateFormatSym(icuLocale.locale(), status);
625 if (U_FAILURE(status)) {
626 return JNI_FALSE;
627 }
628
629 // Get AM/PM and BC/AD.
630 int32_t count = 0;
631 const icu::UnicodeString* amPmStrs = dateFormatSym.getAmPmStrings(count);
632 setStringArrayField(env, localeData, "amPm", amPmStrs, count);
633 const icu::UnicodeString* erasStrs = dateFormatSym.getEras(count);
634 setStringArrayField(env, localeData, "eras", erasStrs, count);
635
636 const icu::UnicodeString* longMonthNames =
637 dateFormatSym.getMonths(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::WIDE);
638 setStringArrayField(env, localeData, "longMonthNames", longMonthNames, count);
639 const icu::UnicodeString* shortMonthNames =
640 dateFormatSym.getMonths(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::ABBREVIATED);
641 setStringArrayField(env, localeData, "shortMonthNames", shortMonthNames, count);
642 const icu::UnicodeString* tinyMonthNames =
643 dateFormatSym.getMonths(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::NARROW);
644 setStringArrayField(env, localeData, "tinyMonthNames", tinyMonthNames, count);
645 const icu::UnicodeString* longWeekdayNames =
646 dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::WIDE);
647 setStringArrayField(env, localeData, "longWeekdayNames", longWeekdayNames, count);
648 const icu::UnicodeString* shortWeekdayNames =
649 dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::ABBREVIATED);
650 setStringArrayField(env, localeData, "shortWeekdayNames", shortWeekdayNames, count);
651 const icu::UnicodeString* tinyWeekdayNames =
652 dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::NARROW);
653 setStringArrayField(env, localeData, "tinyWeekdayNames", tinyWeekdayNames, count);
654
655 const icu::UnicodeString* longStandAloneMonthNames =
656 dateFormatSym.getMonths(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::WIDE);
657 setStringArrayField(env, localeData, "longStandAloneMonthNames", longStandAloneMonthNames, count);
658 const icu::UnicodeString* shortStandAloneMonthNames =
659 dateFormatSym.getMonths(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::ABBREVIATED);
660 setStringArrayField(env, localeData, "shortStandAloneMonthNames", shortStandAloneMonthNames, count);
661 const icu::UnicodeString* tinyStandAloneMonthNames =
662 dateFormatSym.getMonths(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::NARROW);
663 setStringArrayField(env, localeData, "tinyStandAloneMonthNames", tinyStandAloneMonthNames, count);
664 const icu::UnicodeString* longStandAloneWeekdayNames =
665 dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::WIDE);
666 setStringArrayField(env, localeData, "longStandAloneWeekdayNames", longStandAloneWeekdayNames, count);
667 const icu::UnicodeString* shortStandAloneWeekdayNames =
668 dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::ABBREVIATED);
669 setStringArrayField(env, localeData, "shortStandAloneWeekdayNames", shortStandAloneWeekdayNames, count);
670 const icu::UnicodeString* tinyStandAloneWeekdayNames =
671 dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::NARROW);
672 setStringArrayField(env, localeData, "tinyStandAloneWeekdayNames", tinyStandAloneWeekdayNames, count);
673
674 status = U_ZERO_ERROR;
675
676 // For numberPatterns and symbols.
677 setNumberPatterns(env, localeData, icuLocale.locale());
678 setDecimalFormatSymbolsData(env, localeData, icuLocale.locale());
679
680 jstring countryCode = env->NewStringUTF(icuLocale.locale().getCountry());
681 jstring internationalCurrencySymbol = ICU_getCurrencyCode(env, NULL, countryCode);
682 env->DeleteLocalRef(countryCode);
683 countryCode = NULL;
684
685 jstring currencySymbol = NULL;
686 if (internationalCurrencySymbol != NULL) {
687 currencySymbol = ICU_getCurrencySymbol(env, NULL, javaLanguageTag, internationalCurrencySymbol);
688 } else {
689 internationalCurrencySymbol = env->NewStringUTF("XXX");
690 }
691 if (currencySymbol == NULL) {
692 // This is the UTF-8 encoding of U+00A4 (CURRENCY SIGN).
693 currencySymbol = env->NewStringUTF("\xc2\xa4");
694 }
695 setStringField(env, localeData, "currencySymbol", currencySymbol);
696 setStringField(env, localeData, "internationalCurrencySymbol", internationalCurrencySymbol);
697
698 return JNI_TRUE;
699 }
700
ICU_toLowerCase(JNIEnv * env,jclass,jstring javaString,jstring javaLanguageTag)701 static jstring ICU_toLowerCase(JNIEnv* env, jclass, jstring javaString, jstring javaLanguageTag) {
702 ScopedJavaUnicodeString scopedString(env, javaString);
703 if (!scopedString.valid()) {
704 return NULL;
705 }
706 ScopedIcuLocale icuLocale(env, javaLanguageTag);
707 if (!icuLocale.valid()) {
708 return NULL;
709 }
710 icu::UnicodeString& s(scopedString.unicodeString());
711 icu::UnicodeString original(s);
712 s.toLower(icuLocale.locale());
713 return s == original ? javaString : jniCreateString(env, s.getBuffer(), s.length());
714 }
715
ICU_toUpperCase(JNIEnv * env,jclass,jstring javaString,jstring javaLanguageTag)716 static jstring ICU_toUpperCase(JNIEnv* env, jclass, jstring javaString, jstring javaLanguageTag) {
717 ScopedJavaUnicodeString scopedString(env, javaString);
718 if (!scopedString.valid()) {
719 return NULL;
720 }
721 ScopedIcuLocale icuLocale(env, javaLanguageTag);
722 if (!icuLocale.valid()) {
723 return NULL;
724 }
725 icu::UnicodeString& s(scopedString.unicodeString());
726 icu::UnicodeString original(s);
727 s.toUpper(icuLocale.locale());
728 return s == original ? javaString : jniCreateString(env, s.getBuffer(), s.length());
729 }
730
versionString(JNIEnv * env,const UVersionInfo & version)731 static jstring versionString(JNIEnv* env, const UVersionInfo& version) {
732 char versionString[U_MAX_VERSION_STRING_LENGTH];
733 u_versionToString(const_cast<UVersionInfo&>(version), &versionString[0]);
734 return env->NewStringUTF(versionString);
735 }
736
ICU_getCldrVersion(JNIEnv * env,jclass)737 static jstring ICU_getCldrVersion(JNIEnv* env, jclass) {
738 UErrorCode status = U_ZERO_ERROR;
739 UVersionInfo cldrVersion;
740 ulocdata_getCLDRVersion(cldrVersion, &status);
741 return versionString(env, cldrVersion);
742 }
743
ICU_getIcuVersion(JNIEnv * env,jclass)744 static jstring ICU_getIcuVersion(JNIEnv* env, jclass) {
745 UVersionInfo icuVersion;
746 u_getVersion(icuVersion);
747 return versionString(env, icuVersion);
748 }
749
ICU_getUnicodeVersion(JNIEnv * env,jclass)750 static jstring ICU_getUnicodeVersion(JNIEnv* env, jclass) {
751 UVersionInfo unicodeVersion;
752 u_getUnicodeVersion(unicodeVersion);
753 return versionString(env, unicodeVersion);
754 }
755
ICU_getTZDataVersion(JNIEnv * env,jclass)756 static jstring ICU_getTZDataVersion(JNIEnv* env, jclass) {
757 UErrorCode status = U_ZERO_ERROR;
758 const char* version = icu::TimeZone::getTZDataVersion(status);
759 if (maybeThrowIcuException(env, "icu::TimeZone::getTZDataVersion", status)) {
760 return NULL;
761 }
762 return env->NewStringUTF(version);
763 }
764
ICU_getAvailableCurrencyCodes(JNIEnv * env,jclass)765 static jobjectArray ICU_getAvailableCurrencyCodes(JNIEnv* env, jclass) {
766 UErrorCode status = U_ZERO_ERROR;
767 icu::UStringEnumeration e(ucurr_openISOCurrencies(UCURR_COMMON|UCURR_NON_DEPRECATED, &status));
768 return fromStringEnumeration(env, status, "ucurr_openISOCurrencies", &e);
769 }
770
ICU_getBestDateTimePatternNative(JNIEnv * env,jclass,jstring javaSkeleton,jstring javaLanguageTag)771 static jstring ICU_getBestDateTimePatternNative(JNIEnv* env, jclass, jstring javaSkeleton, jstring javaLanguageTag) {
772 ScopedIcuLocale icuLocale(env, javaLanguageTag);
773 if (!icuLocale.valid()) {
774 return NULL;
775 }
776
777 UErrorCode status = U_ZERO_ERROR;
778 std::unique_ptr<icu::DateTimePatternGenerator> generator(icu::DateTimePatternGenerator::createInstance(icuLocale.locale(), status));
779 if (maybeThrowIcuException(env, "DateTimePatternGenerator::createInstance", status)) {
780 return NULL;
781 }
782
783 ScopedJavaUnicodeString skeletonHolder(env, javaSkeleton);
784 if (!skeletonHolder.valid()) {
785 return NULL;
786 }
787 icu::UnicodeString result(generator->getBestPattern(skeletonHolder.unicodeString(), status));
788 if (maybeThrowIcuException(env, "DateTimePatternGenerator::getBestPattern", status)) {
789 return NULL;
790 }
791
792 return jniCreateString(env, result.getBuffer(), result.length());
793 }
794
ICU_setDefaultLocale(JNIEnv * env,jclass,jstring javaLanguageTag)795 static void ICU_setDefaultLocale(JNIEnv* env, jclass, jstring javaLanguageTag) {
796 ScopedIcuLocale icuLocale(env, javaLanguageTag);
797 if (!icuLocale.valid()) {
798 return;
799 }
800
801 UErrorCode status = U_ZERO_ERROR;
802 icu::Locale::setDefault(icuLocale.locale(), status);
803 maybeThrowIcuException(env, "Locale::setDefault", status);
804 }
805
ICU_getDefaultLocale(JNIEnv * env,jclass)806 static jstring ICU_getDefaultLocale(JNIEnv* env, jclass) {
807 return env->NewStringUTF(icu::Locale::getDefault().getName());
808 }
809
810 static JNINativeMethod gMethods[] = {
811 NATIVE_METHOD(ICU, addLikelySubtags, "(Ljava/lang/String;)Ljava/lang/String;"),
812 NATIVE_METHOD(ICU, getAvailableBreakIteratorLocalesNative, "()[Ljava/lang/String;"),
813 NATIVE_METHOD(ICU, getAvailableCalendarLocalesNative, "()[Ljava/lang/String;"),
814 NATIVE_METHOD(ICU, getAvailableCollatorLocalesNative, "()[Ljava/lang/String;"),
815 NATIVE_METHOD(ICU, getAvailableCurrencyCodes, "()[Ljava/lang/String;"),
816 NATIVE_METHOD(ICU, getAvailableDateFormatLocalesNative, "()[Ljava/lang/String;"),
817 NATIVE_METHOD(ICU, getAvailableLocalesNative, "()[Ljava/lang/String;"),
818 NATIVE_METHOD(ICU, getAvailableNumberFormatLocalesNative, "()[Ljava/lang/String;"),
819 NATIVE_METHOD(ICU, getBestDateTimePatternNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
820 NATIVE_METHOD(ICU, getCldrVersion, "()Ljava/lang/String;"),
821 NATIVE_METHOD(ICU, getCurrencyCode, "(Ljava/lang/String;)Ljava/lang/String;"),
822 NATIVE_METHOD(ICU, getCurrencyDisplayName, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
823 NATIVE_METHOD(ICU, getCurrencyFractionDigits, "(Ljava/lang/String;)I"),
824 NATIVE_METHOD(ICU, getCurrencyNumericCode, "(Ljava/lang/String;)I"),
825 NATIVE_METHOD(ICU, getCurrencySymbol, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
826 NATIVE_METHOD(ICU, getDefaultLocale, "()Ljava/lang/String;"),
827 NATIVE_METHOD(ICU, getDisplayCountryNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
828 NATIVE_METHOD(ICU, getDisplayLanguageNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
829 NATIVE_METHOD(ICU, getDisplayScriptNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
830 NATIVE_METHOD(ICU, getDisplayVariantNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
831 NATIVE_METHOD(ICU, getISO3Country, "(Ljava/lang/String;)Ljava/lang/String;"),
832 NATIVE_METHOD(ICU, getISO3Language, "(Ljava/lang/String;)Ljava/lang/String;"),
833 NATIVE_METHOD(ICU, getISOCountriesNative, "()[Ljava/lang/String;"),
834 NATIVE_METHOD(ICU, getISOLanguagesNative, "()[Ljava/lang/String;"),
835 NATIVE_METHOD(ICU, getIcuVersion, "()Ljava/lang/String;"),
836 NATIVE_METHOD(ICU, getScript, "(Ljava/lang/String;)Ljava/lang/String;"),
837 NATIVE_METHOD(ICU, getTZDataVersion, "()Ljava/lang/String;"),
838 NATIVE_METHOD(ICU, getUnicodeVersion, "()Ljava/lang/String;"),
839 NATIVE_METHOD(ICU, initLocaleDataNative, "(Ljava/lang/String;Llibcore/icu/LocaleData;)Z"),
840 NATIVE_METHOD(ICU, setDefaultLocale, "(Ljava/lang/String;)V"),
841 NATIVE_METHOD(ICU, toLowerCase, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
842 NATIVE_METHOD(ICU, toUpperCase, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
843 };
844
845 //
846 // Global initialization & Teardown for ICU Setup
847 // - Contains handlers for JNI_OnLoad and JNI_OnUnload
848 //
849
850 #define FAIL_WITH_STRERROR(s) \
851 ALOGE("Couldn't " s " '%s': %s", path_.c_str(), strerror(errno)); \
852 return FALSE;
853
854 #define MAYBE_FAIL_WITH_ICU_ERROR(s) \
855 if (status != U_ZERO_ERROR) {\
856 ALOGE("Couldn't initialize ICU (" s "): %s (%s)", u_errorName(status), path_.c_str()); \
857 return FALSE; \
858 }
859
860 // Contain the memory map for ICU data files.
861 // Automatically adds the data file to ICU's list of data files upon constructing.
862 //
863 // - Automatically unmaps in the destructor.
864 struct IcuDataMap {
865 // Map in ICU data at the path, returning null if it failed (prints error to ALOGE).
CreateIcuDataMap866 static std::unique_ptr<IcuDataMap> Create(const std::string& path) {
867 std::unique_ptr<IcuDataMap> map(new IcuDataMap(path));
868
869 if (!map->TryMap()) {
870 // madvise or ICU could fail but mmap still succeeds.
871 // Destructor will take care of cleaning up a partial init.
872 return nullptr;
873 }
874
875 return map;
876 }
877
878 // Unmap the ICU data.
~IcuDataMapIcuDataMap879 ~IcuDataMap() {
880 TryUnmap();
881 }
882
883 private:
IcuDataMapIcuDataMap884 IcuDataMap(const std::string& path)
885 : path_(path),
886 data_(MAP_FAILED),
887 data_length_(0)
888 {}
889
TryMapIcuDataMap890 bool TryMap() {
891 // Open the file and get its length.
892 android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path_.c_str(), O_RDONLY)));
893
894 if (fd.get() == -1) {
895 FAIL_WITH_STRERROR("open");
896 }
897
898 struct stat sb;
899 if (fstat(fd.get(), &sb) == -1) {
900 FAIL_WITH_STRERROR("stat");
901 }
902
903 data_length_ = sb.st_size;
904
905 // Map it.
906 data_ = mmap(NULL, data_length_, PROT_READ, MAP_SHARED, fd.get(), 0 /* offset */);
907 if (data_ == MAP_FAILED) {
908 FAIL_WITH_STRERROR("mmap");
909 }
910
911 // Tell the kernel that accesses are likely to be random rather than sequential.
912 if (madvise(data_, data_length_, MADV_RANDOM) == -1) {
913 FAIL_WITH_STRERROR("madvise(MADV_RANDOM)");
914 }
915
916 UErrorCode status = U_ZERO_ERROR;
917
918 // Tell ICU to use our memory-mapped data.
919 udata_setCommonData(data_, &status);
920 MAYBE_FAIL_WITH_ICU_ERROR("udata_setCommonData");
921
922 return true;
923 }
924
TryUnmapIcuDataMap925 bool TryUnmap() {
926 // Don't need to do opposite of udata_setCommonData,
927 // u_cleanup (performed in unregister_libcore_icu_ICU) takes care of it.
928
929 // Don't need to opposite of madvise, munmap will take care of it.
930
931 if (data_ != MAP_FAILED) {
932 if (munmap(data_, data_length_) == -1) {
933 FAIL_WITH_STRERROR("munmap");
934 }
935 }
936
937 // Don't need to close the file, it was closed automatically during TryMap.
938 return true;
939 }
940
941 std::string path_; // Save for error messages.
942 void* data_; // Save for munmap.
943 size_t data_length_; // Save for munmap.
944 };
945
946 struct ICURegistration {
947 // Init ICU, configuring it and loading the data files.
ICURegistrationICURegistration948 ICURegistration(JNIEnv* env) {
949 UErrorCode status = U_ZERO_ERROR;
950 // Tell ICU it can *only* use our memory-mapped data.
951 udata_setFileAccess(UDATA_NO_FILES, &status);
952 if (status != U_ZERO_ERROR) {
953 ALOGE("Couldn't initialize ICU (s_setFileAccess): %s", u_errorName(status));
954 abort();
955 }
956
957 // Check the timezone /data override file exists from the "Time zone update via APK" feature.
958 // https://source.android.com/devices/tech/config/timezone-rules
959 // If it does, map it first so we use its data in preference to later ones.
960 std::string dataPath = getDataTimeZonePath();
961 if (pathExists(dataPath)) {
962 ALOGD("Time zone override file found: %s", dataPath.c_str());
963 if ((icu_datamap_from_data_ = IcuDataMap::Create(dataPath)) == nullptr) {
964 ALOGW("TZ override /data file %s exists but could not be loaded. Skipping.",
965 dataPath.c_str());
966 }
967 } else {
968 ALOGV("No timezone override /data file found: %s", dataPath.c_str());
969 }
970
971 // Check the timezone override file exists from a mounted APEX file.
972 // If it does, map it next so we use its data in preference to later ones.
973 std::string tzModulePath = getTimeZoneModulePath();
974 if (pathExists(tzModulePath)) {
975 ALOGD("Time zone APEX file found: %s", tzModulePath.c_str());
976 if ((icu_datamap_from_tz_module_ = IcuDataMap::Create(tzModulePath)) == nullptr) {
977 ALOGW("TZ module override file %s exists but could not be loaded. Skipping.",
978 tzModulePath.c_str());
979 }
980 } else {
981 ALOGV("No time zone module override file found: %s", tzModulePath.c_str());
982 }
983
984 // Use the ICU data files that shipped with the runtime module for everything else.
985 icu_datamap_from_runtime_module_ = IcuDataMap::Create(getRuntimeModulePath());
986 if (icu_datamap_from_runtime_module_ == nullptr) {
987 abort();
988 }
989
990 // Failures to find the ICU data tend to be somewhat obscure because ICU loads its data on first
991 // use, which can be anywhere. Force initialization up front so we can report a nice clear error
992 // and bail.
993 u_init(&status);
994 if (status != U_ZERO_ERROR) {\
995 ALOGE("Couldn't initialize ICU (u_init): %s", u_errorName(status));
996 abort();
997 }
998
999 jniRegisterNativeMethods(env, "libcore/icu/ICU", gMethods, NELEM(gMethods));
1000 }
1001
1002 // De-init ICU, unloading the data files. Do the opposite of the above function.
~ICURegistrationICURegistration1003 ~ICURegistration() {
1004 // Skip unregistering JNI methods explicitly, class unloading takes care of it.
1005
1006 // Reset libicu state to before it was loaded.
1007 u_cleanup();
1008
1009 // Unmap ICU data files from the runtime module.
1010 icu_datamap_from_runtime_module_.reset();
1011
1012 // Unmap optional TZ module files from /apex.
1013 icu_datamap_from_tz_module_.reset();
1014
1015 // Unmap optional TZ /data file.
1016 icu_datamap_from_data_.reset();
1017
1018 // We don't need to call udata_setFileAccess because u_cleanup takes care of it.
1019 }
1020
pathExistsICURegistration1021 static bool pathExists(const std::string path) {
1022 struct stat sb;
1023 return stat(path.c_str(), &sb) == 0;
1024 }
1025
1026 // Returns a string containing the expected path of the (optional) /data tz data file
getDataTimeZonePathICURegistration1027 static std::string getDataTimeZonePath() {
1028 const char* dataPathPrefix = getenv("ANDROID_DATA");
1029 if (dataPathPrefix == NULL) {
1030 ALOGE("ANDROID_DATA environment variable not set"); \
1031 abort();
1032 }
1033 std::string dataPath;
1034 dataPath = dataPathPrefix;
1035 dataPath += "/misc/zoneinfo/current/icu/icu_tzdata.dat";
1036
1037 return dataPath;
1038 }
1039
1040 // Returns a string containing the expected path of the (optional) /apex tz module data file
getTimeZoneModulePathICURegistration1041 static std::string getTimeZoneModulePath() {
1042 const char* tzdataModulePathPrefix = getenv("ANDROID_TZDATA_ROOT");
1043 if (tzdataModulePathPrefix == NULL) {
1044 ALOGE("ANDROID_TZDATA_ROOT environment variable not set"); \
1045 abort();
1046 }
1047
1048 std::string tzdataModulePath;
1049 tzdataModulePath = tzdataModulePathPrefix;
1050 tzdataModulePath += "/etc/icu/icu_tzdata.dat";
1051 return tzdataModulePath;
1052 }
1053
getRuntimeModulePathICURegistration1054 static std::string getRuntimeModulePath() {
1055 const char* runtimeModulePathPrefix = getenv("ANDROID_RUNTIME_ROOT");
1056 if (runtimeModulePathPrefix == NULL) {
1057 ALOGE("ANDROID_RUNTIME_ROOT environment variable not set"); \
1058 abort();
1059 }
1060
1061 std::string runtimeModulePath;
1062 runtimeModulePath = runtimeModulePathPrefix;
1063 runtimeModulePath += "/etc/icu/";
1064 runtimeModulePath += U_ICUDATA_NAME;
1065 runtimeModulePath += ".dat";
1066 return runtimeModulePath;
1067 }
1068
1069 std::unique_ptr<IcuDataMap> icu_datamap_from_data_;
1070 std::unique_ptr<IcuDataMap> icu_datamap_from_tz_module_;
1071 std::unique_ptr<IcuDataMap> icu_datamap_from_runtime_module_;
1072 };
1073
1074 // Use RAII-style initialization/teardown so that we can get unregistered
1075 // when dlclose is called (even if JNI_OnUnload is not).
1076 static std::unique_ptr<ICURegistration> sIcuRegistration;
1077
1078 // Init ICU, configuring it and loading the data files.
register_libcore_icu_ICU(JNIEnv * env)1079 void register_libcore_icu_ICU(JNIEnv* env) {
1080 sIcuRegistration.reset(new ICURegistration(env));
1081 }
1082
1083 // De-init ICU, unloading the data files. Do the opposite of the above function.
unregister_libcore_icu_ICU()1084 void unregister_libcore_icu_ICU() {
1085 // Explicitly calling this is optional. Dlclose will take care of it as well.
1086 sIcuRegistration.reset();
1087 }
1088