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