1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "IcuRegistration.h"
18
19 #include <sys/mman.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #include <unicode/putil.h>
28 #include <unicode/udata.h>
29 #include <unicode/utypes.h>
30
31 #if !defined(__ANDROID__) || defined(NO_ANDROID_LIBLOG)
PriorityToLevel(char priority)32 static int PriorityToLevel(char priority) {
33 // Priority is just the array index of priority in kPriorities.
34 static const char* kPriorities = "VDIWEF";
35 static const int kLogSuppress = sizeof(kPriorities);
36 const char* matching_priority = strchr(kPriorities, toupper(priority));
37 return (matching_priority != nullptr) ? matching_priority - kPriorities : kLogSuppress;
38 }
39
GetHostLogLevel()40 static int GetHostLogLevel() {
41 const char* log_tags = getenv("ANDROID_LOG_TAGS");
42 if (log_tags == nullptr) {
43 return 0;
44 }
45 // Find the wildcard prefix if present in ANDROID_LOG_TAGS.
46 static constexpr const char kLogWildcardPrefix[] = "*:";
47 static constexpr size_t kLogWildcardPrefixLength = sizeof(kLogWildcardPrefix) - 1;
48 const char* wildcard_start = strstr(log_tags, kLogWildcardPrefix);
49 if (wildcard_start == nullptr) {
50 return 0;
51 }
52 // Priority is based on the character after the wildcard prefix.
53 char priority = *(wildcard_start + kLogWildcardPrefixLength);
54 return PriorityToLevel(priority);
55 }
56
AIcuHostShouldLog(char priority)57 bool AIcuHostShouldLog(char priority) {
58 static int g_LogLevel = GetHostLogLevel();
59 return PriorityToLevel(priority) >= g_LogLevel;
60 }
61 #endif // !defined(__ANDROID__) || defined(NO_ANDROID_LIBLOG)
62
63 namespace androidicuinit {
64 namespace impl {
65
66 #if !defined(__ANDROID__) || defined(NO_ANDROID_LIBLOG)
67 // http://b/171371690 Avoid dependency on liblog and libbase on host
68 // Simplified version of android::base::unique_fd for host.
69 class simple_unique_fd final {
70 public:
simple_unique_fd(int fd)71 simple_unique_fd(int fd) {
72 reset(fd);
73 }
~simple_unique_fd()74 ~simple_unique_fd() {
75 reset();
76 }
get()77 int get() {
78 return fd_;
79 }
80
81 private:
82 int fd_ = -1;
reset(int new_fd=-1)83 void reset(int new_fd = -1) {
84 if (fd_ != -1) {
85 close(fd_);
86 }
87 fd_ = new_fd;
88 }
89 // Disable copy constructor and assignment operator
90 simple_unique_fd(const simple_unique_fd&) = delete;
91 void operator=(const simple_unique_fd&) = delete;
92
93 };
94
95 // A copy of TEMP_FAILURE_RETRY from android-base/macros.h
96 // bionic and glibc both have TEMP_FAILURE_RETRY, but eg Mac OS' libc doesn't.
97 #ifndef TEMP_FAILURE_RETRY
98 #define TEMP_FAILURE_RETRY(exp) \
99 ({ \
100 decltype(exp) _rc; \
101 do { \
102 _rc = (exp); \
103 } while (_rc == -1 && errno == EINTR); \
104 _rc; \
105 })
106 #endif
107 #endif // !defined(__ANDROID__) || defined(NO_ANDROID_LIBLOG)
108
109 // http://b/171371690 Avoid dependency on liblog and libbase on host
110 #if defined(__ANDROID__) && !defined(NO_ANDROID_LIBLOG)
111 typedef android::base::unique_fd aicu_unique_fd;
112 #else
113 typedef simple_unique_fd aicu_unique_fd;
114 #endif
115
116 // Map in ICU data at the path, returning null to print error if it failed.
Create(const std::string & path)117 std::unique_ptr<IcuDataMap> IcuDataMap::Create(const std::string& path) {
118 std::unique_ptr<IcuDataMap> map(new IcuDataMap(path));
119
120 if (!map->TryMap()) {
121 // madvise or ICU could fail but mmap still succeeds.
122 // Destructor will take care of cleaning up a partial init.
123 return nullptr;
124 }
125
126 return map;
127 }
128
129 // Unmap the ICU data.
~IcuDataMap()130 IcuDataMap::~IcuDataMap() { TryUnmap(); }
131
TryMap()132 bool IcuDataMap::TryMap() {
133 // Open the file and get its length.
134 aicu_unique_fd fd(TEMP_FAILURE_RETRY(open(path_.c_str(), O_RDONLY)));
135
136 if (fd.get() == -1) {
137 AICU_LOGE("Couldn't open '%s': %s", path_.c_str(), strerror(errno));
138 return false;
139 }
140
141 struct stat sb;
142 if (fstat(fd.get(), &sb) == -1) {
143 AICU_LOGE("Couldn't stat '%s': %s", path_.c_str(), strerror(errno));
144 return false;
145 }
146
147 data_length_ = sb.st_size;
148
149 // Map it.
150 data_ =
151 mmap(NULL, data_length_, PROT_READ, MAP_SHARED, fd.get(), 0 /* offset */);
152 if (data_ == MAP_FAILED) {
153 AICU_LOGE("Couldn't mmap '%s': %s", path_.c_str(), strerror(errno));
154 return false;
155 }
156
157 // Tell the kernel that accesses are likely to be random rather than
158 // sequential.
159 if (madvise(data_, data_length_, MADV_RANDOM) == -1) {
160 AICU_LOGE("Couldn't madvise(MADV_RANDOM) '%s': %s", path_.c_str(),
161 strerror(errno));
162 return false;
163 }
164
165 UErrorCode status = U_ZERO_ERROR;
166
167 // Tell ICU to use our memory-mapped data.
168 udata_setCommonData(data_, &status);
169 if (status != U_ZERO_ERROR) {
170 AICU_LOGE("Couldn't initialize ICU (udata_setCommonData): %s (%s)",
171 u_errorName(status), path_.c_str());
172 return false;
173 }
174
175 return true;
176 }
177
TryUnmap()178 bool IcuDataMap::TryUnmap() {
179 // Don't need to do opposite of udata_setCommonData,
180 // u_cleanup (performed in IcuRegistration::~IcuRegistration()) takes care of
181 // it.
182
183 // Don't need to opposite of madvise, munmap will take care of it.
184
185 if (data_ != nullptr && data_ != MAP_FAILED) {
186 if (munmap(data_, data_length_) == -1) {
187 AICU_LOGE("Couldn't munmap '%s': %s", path_.c_str(), strerror(errno));
188 return false;
189 }
190 }
191
192 // Don't need to close the file, it was closed automatically during TryMap.
193 return true;
194 }
195
196 } // namespace impl
197
198 // A pointer to the instance used by Register and Deregister. Since this code
199 // is currently included in a static library this doesn't prevent duplicate
200 // initialization calls.
201 static std::unique_ptr<IcuRegistration> gIcuRegistration;
202
Register()203 void IcuRegistration::Register() {
204 CHECK(gIcuRegistration.get() == nullptr);
205
206 gIcuRegistration.reset(new IcuRegistration());
207 }
208
Deregister()209 void IcuRegistration::Deregister() {
210 gIcuRegistration.reset();
211 }
212
213 // Init ICU, configuring it and loading the data files.
IcuRegistration()214 IcuRegistration::IcuRegistration() {
215 // Check the timezone override file exists from a mounted APEX file.
216 // If it does, map it so we use its data in preference to later ones.
217 // However, I18N apex is not expected to have the time zone data resources.
218 // http://b/171542040
219 std::string tzIcuDataPath = getTimeZoneModulePath();
220 if (pathExists(tzIcuDataPath)) {
221 UErrorCode status = U_ZERO_ERROR;
222 u_setTimeZoneFilesDirectory(tzIcuDataPath.c_str(), &status);
223 if (U_SUCCESS(status)) {
224 AICU_LOGD("u_setTimeZoneFilesDirectory(\"%s\") succeeded. ", tzIcuDataPath.c_str());
225 } else {
226 AICU_LOGE("u_setTimeZoneFilesDirectory(\"%s\") failed: %s",
227 tzIcuDataPath.c_str(), u_errorName(status));
228 }
229 } else {
230 AICU_LOGE("IcuRegistration: no time zone files were found. %s does not exist.",
231 tzIcuDataPath.c_str());
232 }
233
234 // Use the ICU data files that shipped with the i18n module for everything
235 // else.
236 std::string i18nModulePath = getI18nModulePath();
237 icu_datamap_from_i18n_module_ = impl::IcuDataMap::Create(i18nModulePath);
238 if (icu_datamap_from_i18n_module_ == nullptr) {
239 // IcuDataMap::Create() will log on error so there is no need to log here.
240 abort();
241 }
242 AICU_LOGD("I18n APEX ICU file found: %s", i18nModulePath.c_str());
243 }
244
245 // De-init ICU, unloading the data files. Do the opposite of the above function.
~IcuRegistration()246 IcuRegistration::~IcuRegistration() {
247 // Unmap ICU data files.
248 icu_datamap_from_i18n_module_.reset();
249 icu_datamap_from_tz_module_.reset();
250 }
251
pathExists(const std::string & path)252 bool IcuRegistration::pathExists(const std::string& path) {
253 struct stat sb;
254 return stat(path.c_str(), &sb) == 0;
255 }
256
257 // Identical to TzDataSetVersion#CURRENT_MAJOR_FORMAT_VERSION.
258 // LINT.IfChange
259 static const std::string CURRENT_MAJOR_FORMAT_VERSION = "8";
260 // LINT.ThenChange(external/icu/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TzDataSetVersion.java)
261
getTimeZoneModulePath()262 std::string IcuRegistration::getTimeZoneModulePath() {
263 const char* tzdataModulePathPrefix = getenv("ANDROID_TZDATA_ROOT");
264 if (tzdataModulePathPrefix == NULL) {
265 AICU_LOGE("ANDROID_TZDATA_ROOT environment variable not set");
266 abort();
267 }
268
269 std::string tzdataModulePath;
270 tzdataModulePath = tzdataModulePathPrefix;
271 tzdataModulePath += "/etc/tz/versioned/" + CURRENT_MAJOR_FORMAT_VERSION + "/icu";
272 return tzdataModulePath;
273 }
274
getI18nModulePath()275 std::string IcuRegistration::getI18nModulePath() {
276 const char* i18nModulePathPrefix = getenv("ANDROID_I18N_ROOT");
277 if (i18nModulePathPrefix == NULL) {
278 AICU_LOGE("ANDROID_I18N_ROOT environment variable not set");
279 abort();
280 }
281
282 std::string i18nModulePath;
283 i18nModulePath = i18nModulePathPrefix;
284 i18nModulePath += "/etc/icu/" U_ICUDATA_NAME ".dat";
285 return i18nModulePath;
286 }
287
288 } // namespace androidicuinit
289
android_icu_register()290 void android_icu_register() {
291 androidicuinit::IcuRegistration::Register();
292 }
293
android_icu_deregister()294 void android_icu_deregister() {
295 androidicuinit::IcuRegistration::Deregister();
296 }
297
android_icu_is_registered()298 bool android_icu_is_registered() {
299 return androidicuinit::gIcuRegistration.get() != nullptr;
300 }
301