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