• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "phone_number_format.h"
16 #include <cctype>
17 #include <dlfcn.h>
18 #include <regex>
19 #include "unicode/localebuilder.h"
20 #include "locale_config.h"
21 #include "unicode/locid.h"
22 #include "i18n_hilog.h"
23 #include "map"
24 #include "new"
25 #include "set"
26 #include "securec.h"
27 #include "string"
28 #include "taboo_utils.h"
29 #include "utility"
30 #include "utils.h"
31 
32 namespace OHOS {
33 namespace Global {
34 namespace I18n {
35 const int RECV_CHAR_LEN = 128;
36 using i18n::phonenumbers::PhoneNumberUtil;
37 void* PhoneNumberFormat::dynamicHandler = nullptr;
38 std::mutex PhoneNumberFormat::phoneMutex;
39 std::mutex PhoneNumberFormat::AS_YOU_TYPE_FORMAT_MUTEX;
40 size_t PhoneNumberFormat::MAX_NUMBER_LENGTH = 30;
41 std::map<char, char> PhoneNumberFormat::VALID_PHONE_NUMBER_CHARS {
42     { '+', '+' },
43     { ' ', ' ' },
44     { '*', '*' },
45     { '-', '-' },
46     { '#', '#' },
47     { '(', '(' },
48     { ')', ')' },
49     { ';', ';' },
50     { ',', ',' },
51     { 'A', '2' },
52     { 'B', '2' },
53     { 'C', '2' },
54     { 'D', '3' },
55     { 'E', '3' },
56     { 'F', '3' },
57     { 'G', '4' },
58     { 'H', '4' },
59     { 'I', '4' },
60     { 'J', '5' },
61     { 'K', '5' },
62     { 'L', '5' },
63     { 'M', '6' },
64     { 'N', '6' },
65     { 'O', '6' },
66     { 'P', '7' },
67     { 'Q', '7' },
68     { 'R', '7' },
69     { 'S', '7' },
70     { 'T', '8' },
71     { 'U', '8' },
72     { 'V', '8' },
73     { 'W', '9' },
74     { 'X', '9' },
75     { 'Y', '9' },
76     { 'Z', '9' },
77     { 'a', '2' },
78     { 'b', '2' },
79     { 'c', '2' },
80     { 'd', '3' },
81     { 'e', '3' },
82     { 'f', '3' },
83     { 'g', '4' },
84     { 'h', '4' },
85     { 'i', '4' },
86     { 'j', '5' },
87     { 'k', '5' },
88     { 'l', '5' },
89     { 'm', '6' },
90     { 'n', '6' },
91     { 'o', '6' },
92     { 'p', '7' },
93     { 'q', '7' },
94     { 'r', '7' },
95     { 's', '7' },
96     { 't', '8' },
97     { 'u', '8' },
98     { 'v', '8' },
99     { 'w', '9' },
100     { 'x', '9' },
101     { 'y', '9' },
102     { 'z', '9' },
103 };
104 
PhoneNumberFormat(const std::string & countryTag,const std::map<std::string,std::string> & options)105 PhoneNumberFormat::PhoneNumberFormat(const std::string &countryTag,
106                                      const std::map<std::string, std::string> &options)
107 {
108     util = PhoneNumberUtil::GetInstance();
109     if (LocaleConfig::IsValidRegion(countryTag)) {
110         country = countryTag;
111     } else {
112         icu::Locale locale = icu::Locale::createFromName(countryTag.c_str());
113         country = locale.getCountry();
114     }
115     std::string type = "";
116     auto search = options.find("type");
117     if (search != options.end()) {
118         type = search->second;
119     }
120 
121     std::map<std::string, PhoneNumberUtil::PhoneNumberFormat> type2PhoneNumberFormat = {
122         {"E164", PhoneNumberUtil::PhoneNumberFormat::E164},
123         {"RFC3966", PhoneNumberUtil::PhoneNumberFormat::RFC3966},
124         {"INTERNATIONAL", PhoneNumberUtil::PhoneNumberFormat::INTERNATIONAL},
125         {"NATIONAL", PhoneNumberUtil::PhoneNumberFormat::NATIONAL}
126     };
127 
128     std::set<std::string> validType = {"E164", "RFC3966", "INTERNATIONAL", "NATIONAL"};
129     if (validType.find(type) != validType.end()) {
130         withOptions = true;
131         phoneNumberFormat = type2PhoneNumberFormat[type];
132     } else {
133         phoneNumberFormat = PhoneNumberUtil::PhoneNumberFormat::NATIONAL;
134     }
135     if (type.compare("TYPING") == 0 && util != nullptr) {
136         formatter = std::unique_ptr<AsYouTypeFormatter>(util->GetAsYouTypeFormatter(country));
137     }
138 }
139 
~PhoneNumberFormat()140 PhoneNumberFormat::~PhoneNumberFormat()
141 {
142 }
143 
CloseDynamicHandler()144 void PhoneNumberFormat::CloseDynamicHandler()
145 {
146     std::lock_guard<std::mutex> phoneLock(phoneMutex);
147     if (dynamicHandler != nullptr) {
148         dlclose(dynamicHandler);
149     }
150     dynamicHandler = nullptr;
151 }
152 
CreateInstance(const std::string & countryTag,const std::map<std::string,std::string> & options)153 std::unique_ptr<PhoneNumberFormat> PhoneNumberFormat::CreateInstance(const std::string &countryTag,
154     const std::map<std::string, std::string> &options)
155 {
156     std::unique_ptr<PhoneNumberFormat> phoneNumberFormat = std::make_unique<PhoneNumberFormat>(countryTag, options);
157     if (phoneNumberFormat == nullptr || phoneNumberFormat->GetPhoneNumberUtil() == nullptr) {
158         return nullptr;
159     }
160     return phoneNumberFormat;
161 }
162 
GetPhoneNumberUtil()163 PhoneNumberUtil* PhoneNumberFormat::GetPhoneNumberUtil()
164 {
165     return util;
166 }
167 
isValidPhoneNumber(const std::string & number) const168 bool PhoneNumberFormat::isValidPhoneNumber(const std::string &number) const
169 {
170     if (util == nullptr) {
171         return false;
172     }
173     i18n::phonenumbers::PhoneNumber phoneNumber;
174     PhoneNumberUtil::ErrorType type = util->Parse(number, country, &phoneNumber);
175     if (type != PhoneNumberUtil::ErrorType::NO_PARSING_ERROR) {
176         return false;
177     }
178     return util->IsValidNumber(phoneNumber);
179 }
180 
format(const std::string & number)181 std::string PhoneNumberFormat::format(const std::string &number)
182 {
183     std::string formattedNumber;
184     if (formatter != nullptr) {
185         formattedNumber = GetAsYouTypeFormatResult(number);
186         return PseudoLocalizationProcessor(formattedNumber);
187     }
188     i18n::phonenumbers::PhoneNumber phoneNumber;
189     if (util == nullptr) {
190         HILOG_ERROR_I18N("PhoneNumberFormat::format: util is nullptr.");
191         return PseudoLocalizationProcessor("");
192     }
193     PhoneNumberUtil::ErrorType type = util->ParseAndKeepRawInput(number, country, &phoneNumber);
194     if (type != PhoneNumberUtil::ErrorType::NO_PARSING_ERROR) {
195         return PseudoLocalizationProcessor("");
196     }
197     const std::string prefix = "106";
198     if (number.compare(0, prefix.length(), prefix) == 0) {
199         util->FormatInOriginalFormat(phoneNumber, country, &formattedNumber);
200     } else {
201         util->Format(phoneNumber, phoneNumberFormat, &formattedNumber);
202     }
203     std::string usingNumberingSystem;
204     LocaleConfig::GetUsingNumberingSystem(usingNumberingSystem);
205     icu::UnicodeString replaceDigit = LocaleConfig::GetNumberingSystemDigit(usingNumberingSystem).c_str();
206     if (replaceDigit.isEmpty() || usingNumberingSystem.compare("latn") == 0) {
207         return PseudoLocalizationProcessor(formattedNumber);
208     }
209     icu::UnicodeString replaceNumber;
210     for (auto& number : formattedNumber) {
211         if (number <= '9' && number >= '0') {
212             replaceNumber += replaceDigit.char32At(number - '0');
213             continue;
214         } else {
215             replaceNumber += number;
216         }
217     }
218     if (usingNumberingSystem.compare("arabext") == 0 || usingNumberingSystem.compare("arab") == 0) {
219         replaceNumber =  "\u202d" + replaceNumber + "\u202c";
220     }
221     std::string result;
222     replaceNumber.toUTF8String(result);
223     return PseudoLocalizationProcessor(result);
224 }
225 
GetAsYouTypeFormatResult(const std::string & number)226 std::string PhoneNumberFormat::GetAsYouTypeFormatResult(const std::string &number)
227 {
228     std::lock_guard<std::mutex> formatLock(AS_YOU_TYPE_FORMAT_MUTEX);
229     if (formatter == nullptr || number.length() > MAX_NUMBER_LENGTH) {
230         return number;
231     }
232     std::regex pattern("[^\\d]");
233     std::string phoneNumber = std::regex_replace(number, pattern, "");
234     if (lastFormatNumber.length() > 0 && phoneNumber.length() == lastFormatNumber.length() + 1) {
235         if (phoneNumber.compare(0, lastFormatNumber.length(), lastFormatNumber) != 0) {
236             return FormatAllInputNumber(number, phoneNumber);
237         }
238         char lastChar = *(phoneNumber.rbegin());
239         std::string result;
240         this->lastFormatNumber = phoneNumber;
241         return formatter->InputDigit(lastChar, &result);
242     }
243 
244     return FormatAllInputNumber(number, phoneNumber);
245 }
246 
FormatAllInputNumber(const std::string & originalNumber,std::string & replacedNumber)247 std::string PhoneNumberFormat::FormatAllInputNumber(const std::string &originalNumber,
248                                                     std::string &replacedNumber)
249 {
250     if (formatter == nullptr) {
251         HILOG_ERROR_I18N("PhoneNumberFormat::FormatAllInputNumber: formatter is nullptr.");
252         return "";
253     }
254     formatter->Clear();
255     this->lastFormatNumber = replacedNumber;
256     std::string result;
257     std::string formattedNumber;
258     for (size_t i = 0; i < originalNumber.length(); i++) {
259         char c = originalNumber.at(i);
260         auto iter = VALID_PHONE_NUMBER_CHARS.find(c);
261         if (isdigit(c)) {
262             formattedNumber = formatter->InputDigit(c, &result);
263         } else if (iter != VALID_PHONE_NUMBER_CHARS.end()) {
264             char replacedChar = VALID_PHONE_NUMBER_CHARS[c];
265             formattedNumber = formatter->InputDigit(replacedChar, &result);
266         }
267     }
268     return formattedNumber;
269 }
270 
getLocationName(const std::string & number,const std::string & locale)271 std::string PhoneNumberFormat::getLocationName(
272     const std::string &number, const std::string &locale)
273 {
274     if (util == nullptr) {
275         return "";
276     }
277     // Combine country and locale parameters
278     UErrorCode status = U_ZERO_ERROR;
279     icu::Locale displayLocale = icu::Locale::createFromName(locale.c_str());
280     displayLocale.addLikelySubtags(status);
281     if (U_FAILURE(status)) {
282         HILOG_ERROR_I18N("PhoneNumberFormat::getLocationName: Add likely subtag failed.");
283         return "";
284     }
285     icu::LocaleBuilder builder = icu::LocaleBuilder().setRegion(country).setLanguage(displayLocale.getLanguage()).
286         setScript(displayLocale.getScript());
287     icu::Locale phoneLocale = builder.build(status);
288     if (U_FAILURE(status)) {
289         HILOG_ERROR_I18N("PhoneNumberFormat::getLocationName: Build phone locale failed.");
290         return "";
291     }
292     i18n::phonenumbers::PhoneNumber phoneNumber;
293     PhoneNumberUtil::ErrorType type = util->Parse(number, phoneLocale.getCountry(), &phoneNumber);
294     if (type != PhoneNumberUtil::ErrorType::NO_PARSING_ERROR) {
295         return "";
296     }
297     std::string regionCode;
298     util->GetRegionCodeForNumber(phoneNumber, &regionCode);
299 
300     std::string locName = getPhoneLocationName(number, phoneLocale.getName(), locale);
301 
302     icu::LocaleBuilder regionbuilder = icu::LocaleBuilder().setRegion(regionCode);
303     icu::Locale regionLocale = regionbuilder.build(status);
304     icu::UnicodeString displayRegion;
305     regionLocale.getDisplayCountry(displayLocale, displayRegion);
306     std::string displayCountryName;
307     displayRegion.toUTF8String(displayCountryName);
308 
309     // Check if region name is a country name
310     if (locName.compare(displayCountryName) == 0) {
311         if (getBlockedRegionName(regionCode, displayLocale.getLanguage())) {
312             return "";
313         }
314         TabooUtils* tabooUtils = TabooUtils::GetInstance();
315         if (tabooUtils == nullptr) {
316             HILOG_ERROR_I18N("PhoneNumberFormat::getLocationName: tabooUtils is nullptr.");
317             return displayCountryName;
318         }
319         return tabooUtils->ReplaceCountryName(regionCode, locale, displayCountryName);
320     }
321 
322     // Process the city name
323     std::string formattedNumber;
324     util->Format(phoneNumber, PhoneNumberUtil::PhoneNumberFormat::E164, &formattedNumber);
325     return getCityName(locale, formattedNumber.substr(1), locName);
326 }
327 
getPhoneLocationName(const std::string & number,const std::string & phoneLocale,const std::string & displayLocale)328 std::string PhoneNumberFormat::getPhoneLocationName(
329     const std::string& number, const std::string& phoneLocale,
330     const std::string& displayLocale)
331 {
332     OpenHandler();
333     std::lock_guard<std::mutex> phoneLock(phoneMutex);
334     std::string locName;
335     if (dynamicHandler) {
336         HILOG_ERROR_I18N("LocationNameFunc Init");
337         ExposeLocationName locationNameFunc = reinterpret_cast<ExposeLocationName>(
338             dlsym(dynamicHandler, "exposeLocationName"));
339         if (locationNameFunc) {
340             // The function uses the same locale for phone and display.
341             char recvArr[RECV_CHAR_LEN];
342             const char* numberArr = number.c_str();
343             const char* phoneLocaleArr = phoneLocale.c_str();
344             locationNameFunc(numberArr, phoneLocaleArr, recvArr, RECV_CHAR_LEN);
345             locName = recvArr;
346         }
347     }
348     return locName;
349 }
350 
OpenHandler()351 void PhoneNumberFormat::OpenHandler()
352 {
353     if (dynamicHandler == nullptr) {
354         HILOG_INFO_I18N("DynamicHandler init.");
355         std::lock_guard<std::mutex> phoneLock(phoneMutex);
356         if (dynamicHandler == nullptr) {
357             HILOG_INFO_I18N("DynamicHandler lock init.");
358 #ifndef SUPPORT_ASAN
359             const char* geocodingSO = "libgeocoding.z.so";
360 #else
361             const char* geocodingSO = "system/asan/lib64/platformsdk/libgeocoding.z.so";
362 #endif
363             HILOG_INFO_I18N("DynamicHandler lock init.");
364             dynamicHandler = dlopen(geocodingSO, RTLD_NOW);
365         }
366     }
367 }
368 
getBlockedRegionName(const std::string & regionCode,const std::string & language)369 bool PhoneNumberFormat::getBlockedRegionName(
370     const std::string& regionCode, const std::string& language)
371 {
372     TabooUtils* tabooUtils = TabooUtils::GetInstance();
373     if (tabooUtils == nullptr) {
374         HILOG_ERROR_I18N("PhoneNumberFormat::getBlockedRegionName: tabooUtils is nullptr.");
375         return false;
376     }
377     std::unordered_set<std::string> blockedRegions = tabooUtils->GetBlockedRegions(language);
378     return blockedRegions.find(regionCode) != blockedRegions.end();
379 }
380 
getCityName(const std::string & language,const std::string & phonenumber,const std::string & locationName)381 std::string PhoneNumberFormat::getCityName(const std::string& language, const std::string& phonenumber,
382     const std::string& locationName)
383 {
384     TabooUtils* tabooUtils = TabooUtils::GetInstance();
385     if (tabooUtils == nullptr) {
386         HILOG_ERROR_I18N("PhoneNumberFormat::getCityName: tabooUtils is nullptr.");
387         return locationName;
388     }
389     std::unordered_set<std::string> blockedPhoneNumbers = tabooUtils->GetBlockedPhoneNumbers();
390     for (const auto& blockedPhoneNumber : blockedPhoneNumbers) {
391         if (phonenumber.compare(0, blockedPhoneNumber.length(), blockedPhoneNumber) == 0) {
392             return "";
393         }
394     }
395     return tabooUtils->ReplacePhoneLocationName(phonenumber, language, locationName);
396 }
397 } // namespace I18n
398 } // namespace Global
399 } // namespace OHOS
400