1 /*
2  * Copyright (C) 2024 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 
16 #define MLOG_TAG "ringtone_language"
17 
18 #include "ringtone_language_manager.h"
19 
20 #include "parameter.h"
21 #include "ringtone_errno.h"
22 #include "ringtone_log.h"
23 #include "ringtone_rdbstore.h"
24 #include "ringtone_type.h"
25 #include "ringtone_file_utils.h"
26 #ifdef USE_CONFIG_POLICY
27 #include "config_policy_utils.h"
28 #endif
29 
30 #include <cstring>
31 #include <libxml/tree.h>
32 #include <libxml/parser.h>
33 
34 namespace OHOS {
35 namespace Media {
36 using namespace OHOS::NativeRdb;
37 using namespace std;
38 
39 const char *LANGUAGE_KEY = "persist.global.language";
40 const char *DEFAULT_LANGUAGE_KEY = "const.global.language";
41 const string CHINESE_ABBREVIATION = "zh-Hans";
42 const string ENGLISH_ABBREVIATION = "en-Latn-US";
43 const int32_t SYSPARA_SIZE = 64;
44 const int32_t SYSINIT_TYPE = 1;
45 const int32_t STANDARDVIBRATION = 1;
46 const int32_t UNKNOWN_INDEX = -1;
47 #ifdef USE_CONFIG_POLICY
48 static constexpr char RINGTONE_MULTILINGUAL_FILE_PATH[] =
49     "etc/resource/media/audio/ringtone_list_language.xml";
50 static constexpr char VIBRATION_MULTILINGUAL_FILE_PATH[] =
51     "etc/resource/media/haptics/vibration_list_language.xml";
52 #else
53 static constexpr char RINGTONE_MULTILINGUAL_FILE_PATH[] =
54     "/system/variant/phone/base/etc/resource/media/audio/ringtone_list_language.xml";
55 static constexpr char VIBRATION_MULTILINGUAL_FILE_PATH[] =
56     "/system/variant/phone/base/etc/resource/media/haptics/vibration_list_language.xml";
57 #endif
58 
59 shared_ptr<RingtoneLanguageManager> RingtoneLanguageManager::instance_ = nullptr;
60 mutex RingtoneLanguageManager::mutex_;
61 
RingtoneLanguageManager(void)62 RingtoneLanguageManager::RingtoneLanguageManager(void)
63 {
64 }
65 
~RingtoneLanguageManager(void)66 RingtoneLanguageManager::~RingtoneLanguageManager(void)
67 {
68 }
69 
GetInstance()70 shared_ptr<RingtoneLanguageManager> RingtoneLanguageManager::GetInstance()
71 {
72     if (instance_ == nullptr) {
73         lock_guard<mutex> lock(mutex_);
74 
75         if (instance_ == nullptr) {
76             instance_ = make_shared<RingtoneLanguageManager>();
77         }
78     }
79     return instance_;
80 }
81 
SyncAssetLanguage()82 void RingtoneLanguageManager::SyncAssetLanguage()
83 {
84     RINGTONE_INFO_LOG("SyncAssetLanguage start.");
85     systemLanguage_ = GetSystemLanguage();
86     if (systemLanguage_.empty()) {
87         RINGTONE_ERR_LOG("Failed to get system language");
88         return;
89     }
90     RINGTONE_INFO_LOG("system language is %{public}s", systemLanguage_.c_str());
91     if (strncmp(systemLanguage_.c_str(), CHINESE_ABBREVIATION.c_str(), CHINESE_ABBREVIATION.size()) == 0) {
92         systemLanguage_ = CHINESE_ABBREVIATION;
93     } else {
94         systemLanguage_ = ENGLISH_ABBREVIATION;
95     }
96     UpdateRingtoneLanguage();
97     UpdateVibrationLanguage();
98     RINGTONE_INFO_LOG("SyncAssetLanguage end.");
99 }
100 
GetSystemLanguage()101 string RingtoneLanguageManager::GetSystemLanguage()
102 {
103     char param[SYSPARA_SIZE] = {0};
104     int status = GetParameter(LANGUAGE_KEY, "", param, SYSPARA_SIZE);
105     if (status > 0) {
106         return param;
107     }
108     status = GetParameter(DEFAULT_LANGUAGE_KEY, "", param, SYSPARA_SIZE);
109     if (status > 0) {
110         return param;
111     }
112     return "";
113 }
114 
UpdateRingtoneLanguage()115 void RingtoneLanguageManager::UpdateRingtoneLanguage()
116 {
117     RINGTONE_INFO_LOG("UpdateRingtonLanguage start.");
118     int32_t rowCount = 0;
119     std::shared_ptr<NativeRdb::ResultSet> resultSet;
120     if (CheckLanguageTypeByRingtone(rowCount, resultSet) != E_OK) {
121         return;
122     }
123     RINGTONE_INFO_LOG("%{public}d ring tones need to be sync", rowCount);
124     if (rowCount == 0) {
125         return;
126     }
127 #ifdef USE_CONFIG_POLICY
128     char buf[MAX_PATH_LEN] = {0};
129     char *path = GetOneCfgFile(RINGTONE_MULTILINGUAL_FILE_PATH, buf, MAX_PATH_LEN);
130     if (path == nullptr || *path == '\0') {
131         RINGTONE_ERR_LOG("GetOneCfgFile for %{public}s failed.", RINGTONE_MULTILINGUAL_FILE_PATH);
132         return;
133     }
134 #else
135     const char *path = RINGTONE_MULTILINGUAL_FILE_PATH;
136 #endif
137 
138     if (!ReadMultilingualResources(path, RINGTONE_FILE)) {
139         return;
140     }
141     ChangeLanguageDataToRingtone(rowCount, resultSet);
142     RINGTONE_INFO_LOG("UpdateRingtonLanguage end.");
143 }
144 
UpdateVibrationLanguage()145 void RingtoneLanguageManager::UpdateVibrationLanguage()
146 {
147     RINGTONE_INFO_LOG("UpdateVibrationLanguage start.");
148     int32_t rowCount = 0;
149     std::shared_ptr<NativeRdb::ResultSet> resultSet;
150     if (CheckLanguageTypeByVibration(rowCount, resultSet) != E_OK) {
151         return;
152     }
153     RINGTONE_INFO_LOG("%{public}d vibration need to be sync", rowCount);
154     if (rowCount == 0) {
155         return;
156     }
157 
158 #ifdef USE_CONFIG_POLICY
159     char buf[MAX_PATH_LEN] = {0};
160     char *path = GetOneCfgFile(VIBRATION_MULTILINGUAL_FILE_PATH, buf, MAX_PATH_LEN);
161     if (path == nullptr || *path == '\0') {
162         RINGTONE_ERR_LOG("GetOneCfgFile for %{public}s failed.", VIBRATION_MULTILINGUAL_FILE_PATH);
163         return;
164     }
165 #else
166     const char *path = VIBRATION_MULTILINGUAL_FILE_PATH;
167 #endif
168 
169     if (!ReadMultilingualResources(path, VIBRATION_FILE)) {
170         return;
171     }
172     ChangeLanguageDataToVibration(rowCount, resultSet);
173     RINGTONE_INFO_LOG("UpdateVibrationLanguage end.");
174 }
175 
CheckLanguageTypeByRingtone(int32_t & rowCount,shared_ptr<ResultSet> & resultSet)176 int32_t RingtoneLanguageManager::CheckLanguageTypeByRingtone(int32_t &rowCount,
177     shared_ptr<ResultSet> &resultSet)
178 {
179     vector<string> columns = {
180         RINGTONE_COLUMN_TONE_ID,
181         RINGTONE_COLUMN_DATA
182     };
183 
184     auto rawRdb = RingtoneRdbStore::GetInstance()->GetRaw();
185     if (rawRdb == nullptr) {
186         RINGTONE_ERR_LOG("failed to get raw rdb");
187         return E_RDB;
188     }
189 
190     AbsRdbPredicates absRdbPredicates(RINGTONE_TABLE);
191     absRdbPredicates.EqualTo(RINGTONE_COLUMN_SOURCE_TYPE, SYSINIT_TYPE);
192     absRdbPredicates.And();
193     absRdbPredicates.BeginWrap();
194     absRdbPredicates.NotEqualTo(RINGTONE_COLUMN_DISPLAY_LANGUAGE_TYPE, systemLanguage_);
195     absRdbPredicates.Or();
196     absRdbPredicates.IsNull(RINGTONE_COLUMN_DISPLAY_LANGUAGE_TYPE);
197     absRdbPredicates.EndWrap();
198     resultSet = rawRdb->Query(absRdbPredicates, columns);
199     if (resultSet == nullptr) {
200         RINGTONE_ERR_LOG("failed to query rdb");
201         return E_RDB;
202     }
203 
204     int32_t ret = resultSet->GetRowCount(rowCount);
205     if (ret != NativeRdb::E_OK) {
206         RINGTONE_ERR_LOG("failed to get resultSet row count");
207         return E_RDB;
208     }
209     return E_OK;
210 }
211 
ChangeLanguageDataToRingtone(int32_t rowCount,const std::shared_ptr<ResultSet> & resultSet)212 void RingtoneLanguageManager::ChangeLanguageDataToRingtone(int32_t rowCount,
213     const std::shared_ptr<ResultSet> &resultSet)
214 {
215     auto rawRdb = RingtoneRdbStore::GetInstance()->GetRaw();
216     if (rawRdb == nullptr) {
217         RINGTONE_ERR_LOG("failed to get raw rdb");
218         return;
219     }
220 
221     map<string, int> fieldIndex = {
222         { RINGTONE_COLUMN_TONE_ID, UNKNOWN_INDEX },
223         { RINGTONE_COLUMN_DATA, UNKNOWN_INDEX }
224     };
225     if (GetFieldIndex(resultSet, fieldIndex) != E_OK) {
226         return;
227     }
228 
229     for (int i = 0; i < rowCount; i++) {
230         if (resultSet->GoToRow(i) != E_OK) {
231             RINGTONE_ERR_LOG("failed to goto row : %{public}d", i);
232             return;
233         }
234 
235         ValuesBucket values;
236         int ringtoneId;
237         if (SetValuesFromResultSet(resultSet, fieldIndex, values, ringtoneId, RINGTONE_FILE) == E_OK) {
238             AbsRdbPredicates absRdbPredicates(RINGTONE_TABLE);
239             absRdbPredicates.EqualTo(RINGTONE_COLUMN_TONE_ID, ringtoneId);
240             int32_t changedRows;
241             int32_t result = rawRdb->Update(changedRows, values, absRdbPredicates);
242             if (result != E_OK || changedRows <= 0) {
243                 RINGTONE_ERR_LOG("Update operation failed. Result %{public}d. Updated %{public}d", result, changedRows);
244                 return;
245             }
246         }
247     }
248 }
249 
GetFieldIndex(const std::shared_ptr<NativeRdb::ResultSet> & resultSet,std::map<std::string,int> & fieldIndex)250 int32_t RingtoneLanguageManager::GetFieldIndex(const std::shared_ptr<NativeRdb::ResultSet> &resultSet,
251     std::map<std::string, int> &fieldIndex)
252 {
253     for (auto& field : fieldIndex) {
254         if (resultSet->GetColumnIndex(field.first, field.second) != E_OK) {
255             RINGTONE_ERR_LOG("failed to get field index");
256             return E_RDB;
257         }
258     }
259     return E_OK;
260 }
261 
SetValuesFromResultSet(const std::shared_ptr<NativeRdb::ResultSet> & resultSet,const std::map<std::string,int> & fieldIndex,NativeRdb::ValuesBucket & values,int32_t & indexId,ResourceFileType resourceFileType)262 int32_t RingtoneLanguageManager::SetValuesFromResultSet(const std::shared_ptr<NativeRdb::ResultSet> &resultSet,
263     const std::map<std::string, int> &fieldIndex, NativeRdb::ValuesBucket &values, int32_t &indexId,
264     ResourceFileType resourceFileType)
265 {
266     string data;
267     string idIndexField = resourceFileType == RINGTONE_FILE ? RINGTONE_COLUMN_TONE_ID : VIBRATE_COLUMN_VIBRATE_ID;
268     string dataIndexField = resourceFileType == RINGTONE_FILE ? RINGTONE_COLUMN_DATA : VIBRATE_COLUMN_DATA;
269     string titleIndexField = resourceFileType == RINGTONE_FILE ? RINGTONE_COLUMN_TITLE : VIBRATE_COLUMN_TITLE;
270     string languageIndexField = resourceFileType == RINGTONE_FILE ?
271         RINGTONE_COLUMN_DISPLAY_LANGUAGE_TYPE : VIBRATE_COLUMN_DISPLAY_LANGUAGE;
272     auto& translation = resourceFileType == RINGTONE_FILE ? ringtoneTranslate_ : vibrationTranslate_;
273 
274     auto idItem = fieldIndex.find(idIndexField);
275     if (idItem == fieldIndex.end()) {
276         RINGTONE_ERR_LOG("failed to get %{public}s index", idIndexField.c_str());
277         return E_RDB;
278     }
279     if (resultSet->GetInt(idItem->second, indexId) != E_OK) {
280         RINGTONE_ERR_LOG("failed to get tone_id value");
281         return E_RDB;
282     }
283 
284     auto dataItem = fieldIndex.find(dataIndexField);
285     if (dataItem == fieldIndex.end()) {
286         RINGTONE_ERR_LOG("failed to get %{public}s index", dataIndexField.c_str());
287         return E_RDB;
288     }
289     if (resultSet->GetString(dataItem->second, data) != E_OK) {
290         RINGTONE_ERR_LOG("failed to get tone_id value");
291         return E_RDB;
292     }
293 
294     values.PutString(languageIndexField, systemLanguage_);
295     string realName = RingtoneFileUtils::GetBaseNameFromPath(data);
296     auto item = translation[systemLanguage_].find(realName);
297     if (item == translation[systemLanguage_].end()) {
298         return E_OK;
299     }
300     string titleName = item->second;
301     values.PutString(titleIndexField, titleName);
302     return E_OK;
303 }
304 
CheckLanguageTypeByVibration(int32_t & rowCount,std::shared_ptr<NativeRdb::ResultSet> & resultSet)305 int32_t RingtoneLanguageManager::CheckLanguageTypeByVibration(int32_t &rowCount,
306     std::shared_ptr<NativeRdb::ResultSet> &resultSet)
307 {
308     vector<string> columns = {
309         VIBRATE_COLUMN_VIBRATE_ID,
310         VIBRATE_COLUMN_DATA
311     };
312 
313     auto rawRdb = RingtoneRdbStore::GetInstance()->GetRaw();
314     if (rawRdb == nullptr) {
315         RINGTONE_ERR_LOG("failed to get raw rdb");
316         return E_RDB;
317     }
318 
319     AbsRdbPredicates absRdbPredicates(VIBRATE_TABLE);
320     absRdbPredicates.EqualTo(VIBRATE_COLUMN_VIBRATE_TYPE, STANDARDVIBRATION);
321     absRdbPredicates.And();
322     absRdbPredicates.BeginWrap();
323     absRdbPredicates.NotEqualTo(VIBRATE_COLUMN_DISPLAY_LANGUAGE, systemLanguage_);
324     absRdbPredicates.Or();
325     absRdbPredicates.IsNull(VIBRATE_COLUMN_DISPLAY_LANGUAGE);
326     absRdbPredicates.EndWrap();
327     resultSet = rawRdb->Query(absRdbPredicates, columns);
328     if (resultSet == nullptr) {
329         RINGTONE_ERR_LOG("failed to query rdb");
330         return E_RDB;
331     }
332 
333     int32_t ret = resultSet->GetRowCount(rowCount);
334     if (ret != NativeRdb::E_OK) {
335         RINGTONE_ERR_LOG("failed to get resultSet row count");
336         return E_RDB;
337     }
338     return E_OK;
339 }
340 
ChangeLanguageDataToVibration(int32_t rowCount,const std::shared_ptr<NativeRdb::ResultSet> & resultSet)341 void RingtoneLanguageManager::ChangeLanguageDataToVibration(int32_t rowCount,
342     const std::shared_ptr<NativeRdb::ResultSet> &resultSet)
343 {
344     auto rawRdb = RingtoneRdbStore::GetInstance()->GetRaw();
345     if (rawRdb == nullptr) {
346         RINGTONE_ERR_LOG("failed to get raw rdb");
347         return;
348     }
349 
350     map<string, int> fieldIndex = {
351         { VIBRATE_COLUMN_VIBRATE_ID, UNKNOWN_INDEX },
352         { VIBRATE_COLUMN_DATA, UNKNOWN_INDEX }
353     };
354     if (GetFieldIndex(resultSet, fieldIndex) != E_OK) {
355         return;
356     }
357 
358     for (int i = 0; i < rowCount; i++) {
359         if (resultSet->GoToRow(i) != E_OK) {
360             RINGTONE_ERR_LOG("failed to goto row : %{public}d", i);
361             return;
362         }
363 
364         ValuesBucket values;
365         int vibrateId;
366         if (SetValuesFromResultSet(resultSet, fieldIndex, values, vibrateId, VIBRATION_FILE) == E_OK) {
367             AbsRdbPredicates absRdbPredicates(VIBRATE_TABLE);
368             absRdbPredicates.EqualTo(VIBRATE_COLUMN_VIBRATE_ID, vibrateId);
369             int32_t changedRows;
370             int32_t result = rawRdb->Update(changedRows, values, absRdbPredicates);
371             if (result != E_OK || changedRows <= 0) {
372                 RINGTONE_ERR_LOG("Update operation failed. Result %{public}d. Updated %{public}d", result, changedRows);
373                 return;
374             }
375         }
376     }
377 }
378 
ReadMultilingualResources(const string & filePath,ResourceFileType resourceFileType)379 bool RingtoneLanguageManager::ReadMultilingualResources(const string &filePath, ResourceFileType resourceFileType)
380 {
381     std::unique_ptr<xmlDoc, decltype(&xmlFreeDoc)> docPtr(
382         xmlReadFile(filePath.c_str(), nullptr, XML_PARSE_NOBLANKS), xmlFreeDoc);
383     if (docPtr == nullptr) {
384         RINGTONE_ERR_LOG("failed to read xml file [%{public}s]", filePath.c_str());
385         xmlErrorPtr error = xmlGetLastError();
386         if (error != nullptr) {
387             RINGTONE_ERR_LOG("Error: %{public}s (line %{public}d): %{public}s",
388                 error->file, error->line, error->message);
389             xmlResetLastError();
390         }
391         return false;
392     }
393 
394     xmlNodePtr rootNode = xmlDocGetRootElement(docPtr.get());
395     if (rootNode == nullptr) {
396         RINGTONE_ERR_LOG("failed to read root node");
397         return false;
398     }
399     if (resourceFileType == RINGTONE_FILE) {
400         if (xmlStrcmp(rootNode->name, BAD_CAST "RingtoneList") != 0) {
401             RINGTONE_ERR_LOG("failed to root node name is not matched");
402             return false;
403         }
404         ringtoneTranslate_.clear();
405     } else if (resourceFileType == VIBRATION_FILE) {
406         if (xmlStrcmp(rootNode->name, BAD_CAST "VibrationList") != 0) {
407             RINGTONE_ERR_LOG("failed to root node name is not matched");
408             return false;
409         }
410         vibrationTranslate_.clear();
411     }
412     return ParseMultilingualXml(rootNode, resourceFileType);
413 }
414 
ParseMultilingualXml(xmlNodePtr & rootNode,ResourceFileType resourceFileType)415 bool RingtoneLanguageManager::ParseMultilingualXml(xmlNodePtr &rootNode, ResourceFileType resourceFileType)
416 {
417     for (xmlNodePtr itemNode = rootNode->children; itemNode; itemNode = itemNode->next) {
418         if (xmlStrcmp(itemNode->name, BAD_CAST "Language") != 0) {
419             continue;
420         }
421 
422         string language;
423         auto xmlLanguage = reinterpret_cast<char*>(xmlGetProp(itemNode, BAD_CAST "type"));
424         if (xmlLanguage != nullptr) {
425             language = string(xmlLanguage);
426             xmlFree(xmlLanguage);
427         }
428 
429         for (xmlNodePtr childNode = itemNode->children; childNode; childNode = childNode->next) {
430             if (resourceFileType == RINGTONE_FILE && xmlStrcmp(childNode->name, BAD_CAST "Ring") != 0) {
431                 RINGTONE_ERR_LOG("failed to ringtone child node name is not matched");
432                 return false;
433             } else if (resourceFileType == VIBRATION_FILE && xmlStrcmp(childNode->name, BAD_CAST "vibrtion") != 0) {
434                 RINGTONE_ERR_LOG("failed to vibrate child node name is not matched");
435                 return false;
436             }
437 
438             string resourceName, displayName;
439             auto xmlResourceName = reinterpret_cast<char*>(xmlGetProp(childNode, BAD_CAST "resource_name"));
440             if (xmlResourceName) {
441                 resourceName = string(xmlResourceName);
442                 xmlFree(xmlResourceName);
443             }
444 
445             auto xmlDisplayName = reinterpret_cast<char*>(xmlGetProp(childNode, BAD_CAST "title"));
446             if (xmlDisplayName) {
447                 displayName = string(xmlDisplayName);
448                 xmlFree(xmlDisplayName);
449             }
450 
451             if (resourceFileType == RINGTONE_FILE && !resourceName.empty() && !displayName.empty()) {
452                 ringtoneTranslate_[language][resourceName] = displayName;
453             } else if (resourceFileType == VIBRATION_FILE && !resourceName.empty() && !displayName.empty()) {
454                 vibrationTranslate_[language][resourceName] = displayName;
455             }
456         }
457     }
458     return true;
459 }
460 
461 } // namespace Media
462 } // namespace OHOS
463