xref: /aosp_15_r20/external/cronet/base/i18n/icu_util.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker // Copyright 2012 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker 
5*6777b538SAndroid Build Coastguard Worker #include "base/i18n/icu_util.h"
6*6777b538SAndroid Build Coastguard Worker 
7*6777b538SAndroid Build Coastguard Worker #include "build/build_config.h"
8*6777b538SAndroid Build Coastguard Worker 
9*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
10*6777b538SAndroid Build Coastguard Worker #include <windows.h>
11*6777b538SAndroid Build Coastguard Worker #endif
12*6777b538SAndroid Build Coastguard Worker 
13*6777b538SAndroid Build Coastguard Worker #include <string.h>
14*6777b538SAndroid Build Coastguard Worker 
15*6777b538SAndroid Build Coastguard Worker #include <memory>
16*6777b538SAndroid Build Coastguard Worker #include <string>
17*6777b538SAndroid Build Coastguard Worker 
18*6777b538SAndroid Build Coastguard Worker #include "base/debug/alias.h"
19*6777b538SAndroid Build Coastguard Worker #include "base/environment.h"
20*6777b538SAndroid Build Coastguard Worker #include "base/files/file_path.h"
21*6777b538SAndroid Build Coastguard Worker #include "base/files/file_util.h"
22*6777b538SAndroid Build Coastguard Worker #include "base/files/memory_mapped_file.h"
23*6777b538SAndroid Build Coastguard Worker #include "base/logging.h"
24*6777b538SAndroid Build Coastguard Worker #include "base/metrics/histogram_functions.h"
25*6777b538SAndroid Build Coastguard Worker #include "base/metrics/metrics_hashes.h"
26*6777b538SAndroid Build Coastguard Worker #include "base/path_service.h"
27*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_util.h"
28*6777b538SAndroid Build Coastguard Worker #include "build/chromecast_buildflags.h"
29*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/common/unicode/putil.h"
30*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/common/unicode/uclean.h"
31*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/common/unicode/udata.h"
32*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/common/unicode/utrace.h"
33*6777b538SAndroid Build Coastguard Worker 
34*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_ANDROID)
35*6777b538SAndroid Build Coastguard Worker #include "base/android/apk_assets.h"
36*6777b538SAndroid Build Coastguard Worker #include "base/android/timezone_utils.h"
37*6777b538SAndroid Build Coastguard Worker #endif
38*6777b538SAndroid Build Coastguard Worker 
39*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_IOS)
40*6777b538SAndroid Build Coastguard Worker #include "base/ios/ios_util.h"
41*6777b538SAndroid Build Coastguard Worker #endif
42*6777b538SAndroid Build Coastguard Worker 
43*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_APPLE)
44*6777b538SAndroid Build Coastguard Worker #include "base/apple/foundation_util.h"
45*6777b538SAndroid Build Coastguard Worker #endif
46*6777b538SAndroid Build Coastguard Worker 
47*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_FUCHSIA)
48*6777b538SAndroid Build Coastguard Worker #include "base/fuchsia/intl_profile_watcher.h"
49*6777b538SAndroid Build Coastguard Worker #endif
50*6777b538SAndroid Build Coastguard Worker 
51*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
52*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/common/unicode/unistr.h"
53*6777b538SAndroid Build Coastguard Worker #endif
54*6777b538SAndroid Build Coastguard Worker 
55*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA) || \
56*6777b538SAndroid Build Coastguard Worker     BUILDFLAG(IS_CHROMEOS) || (BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS))
57*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/i18n/unicode/timezone.h"
58*6777b538SAndroid Build Coastguard Worker #endif
59*6777b538SAndroid Build Coastguard Worker 
60*6777b538SAndroid Build Coastguard Worker namespace base::i18n {
61*6777b538SAndroid Build Coastguard Worker 
62*6777b538SAndroid Build Coastguard Worker #if !BUILDFLAG(IS_NACL)
63*6777b538SAndroid Build Coastguard Worker namespace {
64*6777b538SAndroid Build Coastguard Worker 
65*6777b538SAndroid Build Coastguard Worker #if DCHECK_IS_ON()
66*6777b538SAndroid Build Coastguard Worker // Assert that we are not called more than once.  Even though calling this
67*6777b538SAndroid Build Coastguard Worker // function isn't harmful (ICU can handle it), being called twice probably
68*6777b538SAndroid Build Coastguard Worker // indicates a programming error.
69*6777b538SAndroid Build Coastguard Worker bool g_check_called_once = true;
70*6777b538SAndroid Build Coastguard Worker bool g_called_once = false;
71*6777b538SAndroid Build Coastguard Worker #endif  // DCHECK_IS_ON()
72*6777b538SAndroid Build Coastguard Worker 
73*6777b538SAndroid Build Coastguard Worker #if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
74*6777b538SAndroid Build Coastguard Worker 
75*6777b538SAndroid Build Coastguard Worker // To debug http://crbug.com/445616.
76*6777b538SAndroid Build Coastguard Worker int g_debug_icu_last_error;
77*6777b538SAndroid Build Coastguard Worker int g_debug_icu_load;
78*6777b538SAndroid Build Coastguard Worker int g_debug_icu_pf_error_details;
79*6777b538SAndroid Build Coastguard Worker int g_debug_icu_pf_last_error;
80*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
81*6777b538SAndroid Build Coastguard Worker wchar_t g_debug_icu_pf_filename[_MAX_PATH];
82*6777b538SAndroid Build Coastguard Worker #endif  // BUILDFLAG(IS_WIN)
83*6777b538SAndroid Build Coastguard Worker // Use an unversioned file name to simplify a icu version update down the road.
84*6777b538SAndroid Build Coastguard Worker // No need to change the filename in multiple places (gyp files, windows
85*6777b538SAndroid Build Coastguard Worker // build pkg configurations, etc). 'l' stands for Little Endian.
86*6777b538SAndroid Build Coastguard Worker // This variable is exported through the header file.
87*6777b538SAndroid Build Coastguard Worker const char kIcuDataFileName[] = "icudtl.dat";
88*6777b538SAndroid Build Coastguard Worker 
89*6777b538SAndroid Build Coastguard Worker // Time zone data loading.
90*6777b538SAndroid Build Coastguard Worker // For now, only Fuchsia has a meaningful use case for this feature, so it is
91*6777b538SAndroid Build Coastguard Worker // only implemented for OS_FUCHSIA.
92*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_FUCHSIA)
93*6777b538SAndroid Build Coastguard Worker // The environment variable used to point the ICU data loader to the directory
94*6777b538SAndroid Build Coastguard Worker // containing time zone data. This is available from ICU version 54. The env
95*6777b538SAndroid Build Coastguard Worker // variable approach is antiquated by today's standards (2019), but is the
96*6777b538SAndroid Build Coastguard Worker // recommended way to configure ICU.
97*6777b538SAndroid Build Coastguard Worker //
98*6777b538SAndroid Build Coastguard Worker // See for details: http://userguide.icu-project.org/datetime/timezone
99*6777b538SAndroid Build Coastguard Worker const char kIcuTimeZoneEnvVariable[] = "ICU_TIMEZONE_FILES_DIR";
100*6777b538SAndroid Build Coastguard Worker 
101*6777b538SAndroid Build Coastguard Worker // Up-to-date time zone data MUST be provided by the system as a
102*6777b538SAndroid Build Coastguard Worker // directory offered to Chromium components at /config/tzdata.  Chromium
103*6777b538SAndroid Build Coastguard Worker // components "use" the `tzdata` directory capability, specifying the
104*6777b538SAndroid Build Coastguard Worker // "/config/tzdata" path. Chromium components will crash if this capability
105*6777b538SAndroid Build Coastguard Worker // is not available.
106*6777b538SAndroid Build Coastguard Worker //
107*6777b538SAndroid Build Coastguard Worker // TimeZoneDataTest.* tests verify that external timezone data is correctly
108*6777b538SAndroid Build Coastguard Worker // loaded from the system, to alert developers if the platform and Chromium
109*6777b538SAndroid Build Coastguard Worker // versions are no longer compatible versions.
110*6777b538SAndroid Build Coastguard Worker // LINT.IfChange(icu_time_zone_data_path)
111*6777b538SAndroid Build Coastguard Worker const char kIcuTimeZoneDataDir[] = "/config/tzdata/icu/44/le";
112*6777b538SAndroid Build Coastguard Worker // LINT.ThenChange(//sandbox/policy.fuchsia/sandbox_policy_fuchsia.cc:icu_time_zone_data_path)
113*6777b538SAndroid Build Coastguard Worker #endif  // BUILDFLAG(IS_FUCHSIA)
114*6777b538SAndroid Build Coastguard Worker 
115*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_ANDROID)
116*6777b538SAndroid Build Coastguard Worker const char kAndroidAssetsIcuDataFileName[] = "assets/icudtl.dat";
117*6777b538SAndroid Build Coastguard Worker #endif  // BUILDFLAG(IS_ANDROID)
118*6777b538SAndroid Build Coastguard Worker 
119*6777b538SAndroid Build Coastguard Worker // File handle intentionally never closed. Not using File here because its
120*6777b538SAndroid Build Coastguard Worker // Windows implementation guards against two instances owning the same
121*6777b538SAndroid Build Coastguard Worker // PlatformFile (which we allow since we know it is never freed).
122*6777b538SAndroid Build Coastguard Worker PlatformFile g_icudtl_pf = kInvalidPlatformFile;
123*6777b538SAndroid Build Coastguard Worker IcuDataFile* g_icudtl_mapped_file = nullptr;
124*6777b538SAndroid Build Coastguard Worker MemoryMappedFile::Region g_icudtl_region;
125*6777b538SAndroid Build Coastguard Worker 
126*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_FUCHSIA)
127*6777b538SAndroid Build Coastguard Worker // The directory from which the ICU data loader will be configured to load time
128*6777b538SAndroid Build Coastguard Worker // zone data. It is only changed by SetIcuTimeZoneDataDirForTesting().
129*6777b538SAndroid Build Coastguard Worker const char* g_icu_time_zone_data_dir = kIcuTimeZoneDataDir;
130*6777b538SAndroid Build Coastguard Worker #endif  // BUILDFLAG(IS_FUCHSIA)
131*6777b538SAndroid Build Coastguard Worker 
LazyInitIcuDataFile()132*6777b538SAndroid Build Coastguard Worker void LazyInitIcuDataFile() {
133*6777b538SAndroid Build Coastguard Worker   if (g_icudtl_pf != kInvalidPlatformFile) {
134*6777b538SAndroid Build Coastguard Worker     return;
135*6777b538SAndroid Build Coastguard Worker   }
136*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_ANDROID)
137*6777b538SAndroid Build Coastguard Worker   int fd =
138*6777b538SAndroid Build Coastguard Worker       android::OpenApkAsset(kAndroidAssetsIcuDataFileName, &g_icudtl_region);
139*6777b538SAndroid Build Coastguard Worker   g_icudtl_pf = fd;
140*6777b538SAndroid Build Coastguard Worker   if (fd != -1) {
141*6777b538SAndroid Build Coastguard Worker     return;
142*6777b538SAndroid Build Coastguard Worker   }
143*6777b538SAndroid Build Coastguard Worker #endif  // BUILDFLAG(IS_ANDROID)
144*6777b538SAndroid Build Coastguard Worker   // For unit tests, data file is located on disk, so try there as a fallback.
145*6777b538SAndroid Build Coastguard Worker #if !BUILDFLAG(IS_APPLE)
146*6777b538SAndroid Build Coastguard Worker   FilePath data_path;
147*6777b538SAndroid Build Coastguard Worker   if (!PathService::Get(DIR_ASSETS, &data_path)) {
148*6777b538SAndroid Build Coastguard Worker     LOG(ERROR) << "Can't find " << kIcuDataFileName;
149*6777b538SAndroid Build Coastguard Worker     return;
150*6777b538SAndroid Build Coastguard Worker   }
151*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
152*6777b538SAndroid Build Coastguard Worker   // TODO(brucedawson): http://crbug.com/445616
153*6777b538SAndroid Build Coastguard Worker   wchar_t tmp_buffer[_MAX_PATH] = {0};
154*6777b538SAndroid Build Coastguard Worker   wcscpy_s(tmp_buffer, data_path.value().c_str());
155*6777b538SAndroid Build Coastguard Worker   debug::Alias(tmp_buffer);
156*6777b538SAndroid Build Coastguard Worker #endif
157*6777b538SAndroid Build Coastguard Worker   data_path = data_path.AppendASCII(kIcuDataFileName);
158*6777b538SAndroid Build Coastguard Worker 
159*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
160*6777b538SAndroid Build Coastguard Worker   // TODO(brucedawson): http://crbug.com/445616
161*6777b538SAndroid Build Coastguard Worker   wchar_t tmp_buffer2[_MAX_PATH] = {0};
162*6777b538SAndroid Build Coastguard Worker   wcscpy_s(tmp_buffer2, data_path.value().c_str());
163*6777b538SAndroid Build Coastguard Worker   debug::Alias(tmp_buffer2);
164*6777b538SAndroid Build Coastguard Worker #endif
165*6777b538SAndroid Build Coastguard Worker 
166*6777b538SAndroid Build Coastguard Worker #else  // !BUILDFLAG(IS_APPLE)
167*6777b538SAndroid Build Coastguard Worker   // Assume it is in the framework bundle's Resources directory.
168*6777b538SAndroid Build Coastguard Worker   FilePath data_path = apple::PathForFrameworkBundleResource(kIcuDataFileName);
169*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_IOS)
170*6777b538SAndroid Build Coastguard Worker   FilePath override_data_path = ios::FilePathOfEmbeddedICU();
171*6777b538SAndroid Build Coastguard Worker   if (!override_data_path.empty()) {
172*6777b538SAndroid Build Coastguard Worker     data_path = override_data_path;
173*6777b538SAndroid Build Coastguard Worker   }
174*6777b538SAndroid Build Coastguard Worker #endif  // !BUILDFLAG(IS_IOS)
175*6777b538SAndroid Build Coastguard Worker   if (data_path.empty()) {
176*6777b538SAndroid Build Coastguard Worker     LOG(ERROR) << kIcuDataFileName << " not found in bundle";
177*6777b538SAndroid Build Coastguard Worker     return;
178*6777b538SAndroid Build Coastguard Worker   }
179*6777b538SAndroid Build Coastguard Worker #endif  // !BUILDFLAG(IS_APPLE)
180*6777b538SAndroid Build Coastguard Worker   File file(data_path, File::FLAG_OPEN | File::FLAG_READ);
181*6777b538SAndroid Build Coastguard Worker   if (file.IsValid()) {
182*6777b538SAndroid Build Coastguard Worker     // TODO(brucedawson): http://crbug.com/445616.
183*6777b538SAndroid Build Coastguard Worker     g_debug_icu_pf_last_error = 0;
184*6777b538SAndroid Build Coastguard Worker     g_debug_icu_pf_error_details = 0;
185*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
186*6777b538SAndroid Build Coastguard Worker     g_debug_icu_pf_filename[0] = 0;
187*6777b538SAndroid Build Coastguard Worker #endif  // BUILDFLAG(IS_WIN)
188*6777b538SAndroid Build Coastguard Worker 
189*6777b538SAndroid Build Coastguard Worker     g_icudtl_pf = file.TakePlatformFile();
190*6777b538SAndroid Build Coastguard Worker     g_icudtl_region = MemoryMappedFile::Region::kWholeFile;
191*6777b538SAndroid Build Coastguard Worker   }
192*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
193*6777b538SAndroid Build Coastguard Worker   else {
194*6777b538SAndroid Build Coastguard Worker     // TODO(brucedawson): http://crbug.com/445616.
195*6777b538SAndroid Build Coastguard Worker     g_debug_icu_pf_last_error = ::GetLastError();
196*6777b538SAndroid Build Coastguard Worker     g_debug_icu_pf_error_details = file.error_details();
197*6777b538SAndroid Build Coastguard Worker     wcscpy_s(g_debug_icu_pf_filename, data_path.value().c_str());
198*6777b538SAndroid Build Coastguard Worker   }
199*6777b538SAndroid Build Coastguard Worker #endif  // BUILDFLAG(IS_WIN)
200*6777b538SAndroid Build Coastguard Worker }
201*6777b538SAndroid Build Coastguard Worker 
202*6777b538SAndroid Build Coastguard Worker // Configures ICU to load external time zone data, if appropriate.
InitializeExternalTimeZoneData()203*6777b538SAndroid Build Coastguard Worker void InitializeExternalTimeZoneData() {
204*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_FUCHSIA)
205*6777b538SAndroid Build Coastguard Worker   // Set the environment variable to override the location used by ICU.
206*6777b538SAndroid Build Coastguard Worker   // Loading can still fail if the directory is empty or its data is invalid.
207*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<base::Environment> env = base::Environment::Create();
208*6777b538SAndroid Build Coastguard Worker   if (!base::DirectoryExists(base::FilePath(g_icu_time_zone_data_dir))) {
209*6777b538SAndroid Build Coastguard Worker     PLOG(FATAL) << "Could not open directory: '" << g_icu_time_zone_data_dir
210*6777b538SAndroid Build Coastguard Worker                 << "'";
211*6777b538SAndroid Build Coastguard Worker   }
212*6777b538SAndroid Build Coastguard Worker   env->SetVar(kIcuTimeZoneEnvVariable, g_icu_time_zone_data_dir);
213*6777b538SAndroid Build Coastguard Worker #endif  // BUILDFLAG(IS_FUCHSIA)
214*6777b538SAndroid Build Coastguard Worker }
215*6777b538SAndroid Build Coastguard Worker 
LoadIcuData(PlatformFile data_fd,const MemoryMappedFile::Region & data_region,std::unique_ptr<IcuDataFile> * out_mapped_data_file,UErrorCode * out_error_code)216*6777b538SAndroid Build Coastguard Worker int LoadIcuData(PlatformFile data_fd,
217*6777b538SAndroid Build Coastguard Worker                 const MemoryMappedFile::Region& data_region,
218*6777b538SAndroid Build Coastguard Worker                 std::unique_ptr<IcuDataFile>* out_mapped_data_file,
219*6777b538SAndroid Build Coastguard Worker                 UErrorCode* out_error_code) {
220*6777b538SAndroid Build Coastguard Worker   InitializeExternalTimeZoneData();
221*6777b538SAndroid Build Coastguard Worker 
222*6777b538SAndroid Build Coastguard Worker   if (data_fd == kInvalidPlatformFile) {
223*6777b538SAndroid Build Coastguard Worker     LOG(ERROR) << "Invalid file descriptor to ICU data received.";
224*6777b538SAndroid Build Coastguard Worker     return 1;  // To debug http://crbug.com/445616.
225*6777b538SAndroid Build Coastguard Worker   }
226*6777b538SAndroid Build Coastguard Worker 
227*6777b538SAndroid Build Coastguard Worker   *out_mapped_data_file = std::make_unique<IcuDataFile>();
228*6777b538SAndroid Build Coastguard Worker   if (!(*out_mapped_data_file)->Initialize(File(data_fd), data_region)) {
229*6777b538SAndroid Build Coastguard Worker     LOG(ERROR) << "Couldn't mmap icu data file";
230*6777b538SAndroid Build Coastguard Worker     return 2;  // To debug http://crbug.com/445616.
231*6777b538SAndroid Build Coastguard Worker   }
232*6777b538SAndroid Build Coastguard Worker 
233*6777b538SAndroid Build Coastguard Worker   (*out_error_code) = U_ZERO_ERROR;
234*6777b538SAndroid Build Coastguard Worker   udata_setCommonData(const_cast<uint8_t*>((*out_mapped_data_file)->data()),
235*6777b538SAndroid Build Coastguard Worker                       out_error_code);
236*6777b538SAndroid Build Coastguard Worker   if (U_FAILURE(*out_error_code)) {
237*6777b538SAndroid Build Coastguard Worker     LOG(ERROR) << "Failed to initialize ICU with data file: "
238*6777b538SAndroid Build Coastguard Worker                << u_errorName(*out_error_code);
239*6777b538SAndroid Build Coastguard Worker     return 3;  // To debug http://crbug.com/445616.
240*6777b538SAndroid Build Coastguard Worker   }
241*6777b538SAndroid Build Coastguard Worker 
242*6777b538SAndroid Build Coastguard Worker   return 0;
243*6777b538SAndroid Build Coastguard Worker }
244*6777b538SAndroid Build Coastguard Worker 
InitializeICUWithFileDescriptorInternal(PlatformFile data_fd,const MemoryMappedFile::Region & data_region)245*6777b538SAndroid Build Coastguard Worker bool InitializeICUWithFileDescriptorInternal(
246*6777b538SAndroid Build Coastguard Worker     PlatformFile data_fd,
247*6777b538SAndroid Build Coastguard Worker     const MemoryMappedFile::Region& data_region) {
248*6777b538SAndroid Build Coastguard Worker   // This can be called multiple times in tests.
249*6777b538SAndroid Build Coastguard Worker   if (g_icudtl_mapped_file) {
250*6777b538SAndroid Build Coastguard Worker     g_debug_icu_load = 0;  // To debug http://crbug.com/445616.
251*6777b538SAndroid Build Coastguard Worker     return true;
252*6777b538SAndroid Build Coastguard Worker   }
253*6777b538SAndroid Build Coastguard Worker 
254*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<IcuDataFile> mapped_file;
255*6777b538SAndroid Build Coastguard Worker   UErrorCode err;
256*6777b538SAndroid Build Coastguard Worker   g_debug_icu_load = LoadIcuData(data_fd, data_region, &mapped_file, &err);
257*6777b538SAndroid Build Coastguard Worker   if (g_debug_icu_load == 1 || g_debug_icu_load == 2) {
258*6777b538SAndroid Build Coastguard Worker     return false;
259*6777b538SAndroid Build Coastguard Worker   }
260*6777b538SAndroid Build Coastguard Worker   g_icudtl_mapped_file = mapped_file.release();
261*6777b538SAndroid Build Coastguard Worker 
262*6777b538SAndroid Build Coastguard Worker   if (g_debug_icu_load == 3) {
263*6777b538SAndroid Build Coastguard Worker     g_debug_icu_last_error = err;
264*6777b538SAndroid Build Coastguard Worker   }
265*6777b538SAndroid Build Coastguard Worker 
266*6777b538SAndroid Build Coastguard Worker   // Never try to load ICU data from files.
267*6777b538SAndroid Build Coastguard Worker   udata_setFileAccess(UDATA_ONLY_PACKAGES, &err);
268*6777b538SAndroid Build Coastguard Worker   return U_SUCCESS(err);
269*6777b538SAndroid Build Coastguard Worker }
270*6777b538SAndroid Build Coastguard Worker 
InitializeICUFromDataFile()271*6777b538SAndroid Build Coastguard Worker bool InitializeICUFromDataFile() {
272*6777b538SAndroid Build Coastguard Worker   // If the ICU data directory is set, ICU won't actually load the data until
273*6777b538SAndroid Build Coastguard Worker   // it is needed.  This can fail if the process is sandboxed at that time.
274*6777b538SAndroid Build Coastguard Worker   // Instead, we map the file in and hand off the data so the sandbox won't
275*6777b538SAndroid Build Coastguard Worker   // cause any problems.
276*6777b538SAndroid Build Coastguard Worker   LazyInitIcuDataFile();
277*6777b538SAndroid Build Coastguard Worker   bool result =
278*6777b538SAndroid Build Coastguard Worker       InitializeICUWithFileDescriptorInternal(g_icudtl_pf, g_icudtl_region);
279*6777b538SAndroid Build Coastguard Worker 
280*6777b538SAndroid Build Coastguard Worker   int debug_icu_load = g_debug_icu_load;
281*6777b538SAndroid Build Coastguard Worker   debug::Alias(&debug_icu_load);
282*6777b538SAndroid Build Coastguard Worker   int debug_icu_last_error = g_debug_icu_last_error;
283*6777b538SAndroid Build Coastguard Worker   debug::Alias(&debug_icu_last_error);
284*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
285*6777b538SAndroid Build Coastguard Worker   int debug_icu_pf_last_error = g_debug_icu_pf_last_error;
286*6777b538SAndroid Build Coastguard Worker   debug::Alias(&debug_icu_pf_last_error);
287*6777b538SAndroid Build Coastguard Worker   int debug_icu_pf_error_details = g_debug_icu_pf_error_details;
288*6777b538SAndroid Build Coastguard Worker   debug::Alias(&debug_icu_pf_error_details);
289*6777b538SAndroid Build Coastguard Worker   wchar_t debug_icu_pf_filename[_MAX_PATH] = {0};
290*6777b538SAndroid Build Coastguard Worker   wcscpy_s(debug_icu_pf_filename, g_debug_icu_pf_filename);
291*6777b538SAndroid Build Coastguard Worker   debug::Alias(&debug_icu_pf_filename);
292*6777b538SAndroid Build Coastguard Worker #endif            // BUILDFLAG(IS_WIN)
293*6777b538SAndroid Build Coastguard Worker   // Excluding Chrome OS from this CHECK due to b/289684640.
294*6777b538SAndroid Build Coastguard Worker #if !BUILDFLAG(IS_CHROMEOS)
295*6777b538SAndroid Build Coastguard Worker   // https://crbug.com/445616
296*6777b538SAndroid Build Coastguard Worker   // https://crbug.com/1449816
297*6777b538SAndroid Build Coastguard Worker   CHECK(result);
298*6777b538SAndroid Build Coastguard Worker #endif
299*6777b538SAndroid Build Coastguard Worker 
300*6777b538SAndroid Build Coastguard Worker   return result;
301*6777b538SAndroid Build Coastguard Worker }
302*6777b538SAndroid Build Coastguard Worker #endif  // (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
303*6777b538SAndroid Build Coastguard Worker 
304*6777b538SAndroid Build Coastguard Worker // Explicitly initialize ICU's time zone if necessary.
305*6777b538SAndroid Build Coastguard Worker // On some platforms, the time zone must be explicitly initialized zone rather
306*6777b538SAndroid Build Coastguard Worker // than relying on ICU's internal initialization.
InitializeIcuTimeZone()307*6777b538SAndroid Build Coastguard Worker void InitializeIcuTimeZone() {
308*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_ANDROID)
309*6777b538SAndroid Build Coastguard Worker   // On Android, we can't leave it up to ICU to set the default time zone
310*6777b538SAndroid Build Coastguard Worker   // because ICU's time zone detection does not work in many time zones (e.g.
311*6777b538SAndroid Build Coastguard Worker   // Australia/Sydney, Asia/Seoul, Europe/Paris ). Use JNI to detect the host
312*6777b538SAndroid Build Coastguard Worker   // time zone and set the ICU default time zone accordingly in advance of
313*6777b538SAndroid Build Coastguard Worker   // actual use. See crbug.com/722821 and
314*6777b538SAndroid Build Coastguard Worker   // https://ssl.icu-project.org/trac/ticket/13208 .
315*6777b538SAndroid Build Coastguard Worker   std::u16string zone_id = android::GetDefaultTimeZoneId();
316*6777b538SAndroid Build Coastguard Worker   icu::TimeZone::adoptDefault(icu::TimeZone::createTimeZone(
317*6777b538SAndroid Build Coastguard Worker       icu::UnicodeString(false, zone_id.data(), zone_id.length())));
318*6777b538SAndroid Build Coastguard Worker #elif BUILDFLAG(IS_FUCHSIA)
319*6777b538SAndroid Build Coastguard Worker   // The platform-specific mechanisms used by ICU's detectHostTimeZone() to
320*6777b538SAndroid Build Coastguard Worker   // determine the default time zone will not work on Fuchsia. Therefore,
321*6777b538SAndroid Build Coastguard Worker   // proactively set the default system.
322*6777b538SAndroid Build Coastguard Worker   // This is also required by TimeZoneMonitorFuchsia::ProfileMayHaveChanged(),
323*6777b538SAndroid Build Coastguard Worker   // which uses the current default to detect whether the time zone changed in
324*6777b538SAndroid Build Coastguard Worker   // the new profile.
325*6777b538SAndroid Build Coastguard Worker   // If the system time zone cannot be obtained or is not understood by ICU,
326*6777b538SAndroid Build Coastguard Worker   // the "unknown" time zone will be returned by createTimeZone() and used.
327*6777b538SAndroid Build Coastguard Worker   std::string zone_id =
328*6777b538SAndroid Build Coastguard Worker       FuchsiaIntlProfileWatcher::GetPrimaryTimeZoneIdForIcuInitialization();
329*6777b538SAndroid Build Coastguard Worker   icu::TimeZone::adoptDefault(
330*6777b538SAndroid Build Coastguard Worker       icu::TimeZone::createTimeZone(icu::UnicodeString::fromUTF8(zone_id)));
331*6777b538SAndroid Build Coastguard Worker #elif BUILDFLAG(IS_CHROMEOS) || (BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS))
332*6777b538SAndroid Build Coastguard Worker   // To respond to the time zone change properly, the default time zone
333*6777b538SAndroid Build Coastguard Worker   // cache in ICU has to be populated on starting up.
334*6777b538SAndroid Build Coastguard Worker   // See TimeZoneMonitorLinux::NotifyClientsFromImpl().
335*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault());
336*6777b538SAndroid Build Coastguard Worker #endif  // BUILDFLAG(IS_ANDROID)
337*6777b538SAndroid Build Coastguard Worker }
338*6777b538SAndroid Build Coastguard Worker 
339*6777b538SAndroid Build Coastguard Worker enum class ICUCreateInstance {
340*6777b538SAndroid Build Coastguard Worker   kCharacterBreakIterator = 0,
341*6777b538SAndroid Build Coastguard Worker   kWordBreakIterator = 1,
342*6777b538SAndroid Build Coastguard Worker   kLineBreakIterator = 2,
343*6777b538SAndroid Build Coastguard Worker   kLineBreakIteratorTypeLoose = 3,
344*6777b538SAndroid Build Coastguard Worker   kLineBreakIteratorTypeNormal = 4,
345*6777b538SAndroid Build Coastguard Worker   kLineBreakIteratorTypeStrict = 5,
346*6777b538SAndroid Build Coastguard Worker   kSentenceBreakIterator = 6,
347*6777b538SAndroid Build Coastguard Worker   kTitleBreakIterator = 7,
348*6777b538SAndroid Build Coastguard Worker   kThaiBreakEngine = 8,
349*6777b538SAndroid Build Coastguard Worker   kLaoBreakEngine = 9,
350*6777b538SAndroid Build Coastguard Worker   kBurmeseBreakEngine = 10,
351*6777b538SAndroid Build Coastguard Worker   kKhmerBreakEngine = 11,
352*6777b538SAndroid Build Coastguard Worker   kChineseJapaneseBreakEngine = 12,
353*6777b538SAndroid Build Coastguard Worker 
354*6777b538SAndroid Build Coastguard Worker   kMaxValue = kChineseJapaneseBreakEngine
355*6777b538SAndroid Build Coastguard Worker };
356*6777b538SAndroid Build Coastguard Worker 
357*6777b538SAndroid Build Coastguard Worker // Common initialization to run regardless of how ICU is initialized.
358*6777b538SAndroid Build Coastguard Worker // There are multiple exposed InitializeIcu* functions. This should be called
359*6777b538SAndroid Build Coastguard Worker // as at the end of (the last functions in the sequence of) these functions.
DoCommonInitialization()360*6777b538SAndroid Build Coastguard Worker bool DoCommonInitialization() {
361*6777b538SAndroid Build Coastguard Worker   // TODO(jungshik): Some callers do not care about tz at all. If necessary,
362*6777b538SAndroid Build Coastguard Worker   // add a boolean argument to this function to init the default tz only
363*6777b538SAndroid Build Coastguard Worker   // when requested.
364*6777b538SAndroid Build Coastguard Worker   InitializeIcuTimeZone();
365*6777b538SAndroid Build Coastguard Worker 
366*6777b538SAndroid Build Coastguard Worker   utrace_setLevel(UTRACE_VERBOSE);
367*6777b538SAndroid Build Coastguard Worker   return true;
368*6777b538SAndroid Build Coastguard Worker }
369*6777b538SAndroid Build Coastguard Worker 
370*6777b538SAndroid Build Coastguard Worker }  // namespace
371*6777b538SAndroid Build Coastguard Worker 
372*6777b538SAndroid Build Coastguard Worker #if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
InitializeICUWithFileDescriptor(PlatformFile data_fd,const MemoryMappedFile::Region & data_region)373*6777b538SAndroid Build Coastguard Worker bool InitializeICUWithFileDescriptor(
374*6777b538SAndroid Build Coastguard Worker     PlatformFile data_fd,
375*6777b538SAndroid Build Coastguard Worker     const MemoryMappedFile::Region& data_region) {
376*6777b538SAndroid Build Coastguard Worker #if DCHECK_IS_ON()
377*6777b538SAndroid Build Coastguard Worker   DCHECK(!g_check_called_once || !g_called_once);
378*6777b538SAndroid Build Coastguard Worker   g_called_once = true;
379*6777b538SAndroid Build Coastguard Worker #endif
380*6777b538SAndroid Build Coastguard Worker   if (!InitializeICUWithFileDescriptorInternal(data_fd, data_region))
381*6777b538SAndroid Build Coastguard Worker     return false;
382*6777b538SAndroid Build Coastguard Worker 
383*6777b538SAndroid Build Coastguard Worker   return DoCommonInitialization();
384*6777b538SAndroid Build Coastguard Worker }
385*6777b538SAndroid Build Coastguard Worker 
GetIcuDataFileHandle(MemoryMappedFile::Region * out_region)386*6777b538SAndroid Build Coastguard Worker PlatformFile GetIcuDataFileHandle(MemoryMappedFile::Region* out_region) {
387*6777b538SAndroid Build Coastguard Worker   CHECK_NE(g_icudtl_pf, kInvalidPlatformFile);
388*6777b538SAndroid Build Coastguard Worker   *out_region = g_icudtl_region;
389*6777b538SAndroid Build Coastguard Worker   return g_icudtl_pf;
390*6777b538SAndroid Build Coastguard Worker }
391*6777b538SAndroid Build Coastguard Worker 
ResetGlobalsForTesting()392*6777b538SAndroid Build Coastguard Worker void ResetGlobalsForTesting() {
393*6777b538SAndroid Build Coastguard Worker   // Reset ICU library internal state before tearing-down the mapped data
394*6777b538SAndroid Build Coastguard Worker   // file, or handle.
395*6777b538SAndroid Build Coastguard Worker   u_cleanup();
396*6777b538SAndroid Build Coastguard Worker 
397*6777b538SAndroid Build Coastguard Worker   // `g_icudtl_pf` does not actually own the FD once ICU is initialized, so
398*6777b538SAndroid Build Coastguard Worker   // don't try to close it here.
399*6777b538SAndroid Build Coastguard Worker   g_icudtl_pf = kInvalidPlatformFile;
400*6777b538SAndroid Build Coastguard Worker   delete std::exchange(g_icudtl_mapped_file, nullptr);
401*6777b538SAndroid Build Coastguard Worker 
402*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_FUCHSIA)
403*6777b538SAndroid Build Coastguard Worker   g_icu_time_zone_data_dir = kIcuTimeZoneDataDir;
404*6777b538SAndroid Build Coastguard Worker #endif  // BUILDFLAG(IS_FUCHSIA)
405*6777b538SAndroid Build Coastguard Worker }
406*6777b538SAndroid Build Coastguard Worker 
407*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_FUCHSIA)
408*6777b538SAndroid Build Coastguard Worker // |dir| must remain valid until ResetGlobalsForTesting() is called.
SetIcuTimeZoneDataDirForTesting(const char * dir)409*6777b538SAndroid Build Coastguard Worker void SetIcuTimeZoneDataDirForTesting(const char* dir) {
410*6777b538SAndroid Build Coastguard Worker   g_icu_time_zone_data_dir = dir;
411*6777b538SAndroid Build Coastguard Worker }
412*6777b538SAndroid Build Coastguard Worker #endif  // BUILDFLAG(IS_FUCHSIA)
413*6777b538SAndroid Build Coastguard Worker #endif  // (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
414*6777b538SAndroid Build Coastguard Worker 
InitializeICU()415*6777b538SAndroid Build Coastguard Worker bool InitializeICU() {
416*6777b538SAndroid Build Coastguard Worker #if DCHECK_IS_ON()
417*6777b538SAndroid Build Coastguard Worker   DCHECK(!g_check_called_once || !g_called_once);
418*6777b538SAndroid Build Coastguard Worker   g_called_once = true;
419*6777b538SAndroid Build Coastguard Worker #endif
420*6777b538SAndroid Build Coastguard Worker 
421*6777b538SAndroid Build Coastguard Worker #if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC)
422*6777b538SAndroid Build Coastguard Worker   // The ICU data is statically linked.
423*6777b538SAndroid Build Coastguard Worker #elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
424*6777b538SAndroid Build Coastguard Worker   if (!InitializeICUFromDataFile())
425*6777b538SAndroid Build Coastguard Worker     return false;
426*6777b538SAndroid Build Coastguard Worker #else
427*6777b538SAndroid Build Coastguard Worker #error Unsupported ICU_UTIL_DATA_IMPL value
428*6777b538SAndroid Build Coastguard Worker #endif  // (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC)
429*6777b538SAndroid Build Coastguard Worker 
430*6777b538SAndroid Build Coastguard Worker   return DoCommonInitialization();
431*6777b538SAndroid Build Coastguard Worker }
432*6777b538SAndroid Build Coastguard Worker 
AllowMultipleInitializeCallsForTesting()433*6777b538SAndroid Build Coastguard Worker void AllowMultipleInitializeCallsForTesting() {
434*6777b538SAndroid Build Coastguard Worker #if DCHECK_IS_ON()
435*6777b538SAndroid Build Coastguard Worker   g_check_called_once = false;
436*6777b538SAndroid Build Coastguard Worker #endif
437*6777b538SAndroid Build Coastguard Worker }
438*6777b538SAndroid Build Coastguard Worker 
439*6777b538SAndroid Build Coastguard Worker #endif  // !BUILDFLAG(IS_NACL)
440*6777b538SAndroid Build Coastguard Worker 
441*6777b538SAndroid Build Coastguard Worker }  // namespace base::i18n
442