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
48 #ifdef USE_CONFIG_POLICY
49 static constexpr char RINGTONE_MULTILINGUAL_FILE_PATH[] =
50 "etc/resource/media/audio/ringtone_list_language.xml";
51 static constexpr char VIBRATION_MULTILINGUAL_FILE_PATH[] =
52 "etc/resource/media/haptics/vibration_list_language.xml";
53 #else
54 static constexpr char RINGTONE_MULTILINGUAL_FILE_PATH[] =
55 "/system/variant/phone/base/etc/resource/media/audio/ringtone_list_language.xml";
56 static constexpr char VIBRATION_MULTILINGUAL_FILE_PATH[] =
57 "/system/variant/phone/base/etc/resource/media/haptics/vibration_list_language.xml";
58 #endif
59
60 shared_ptr<RingtoneLanguageManager> RingtoneLanguageManager::instance_ = nullptr;
61 mutex RingtoneLanguageManager::mutex_;
62
RingtoneLanguageManager(void)63 RingtoneLanguageManager::RingtoneLanguageManager(void)
64 {
65 }
66
~RingtoneLanguageManager(void)67 RingtoneLanguageManager::~RingtoneLanguageManager(void)
68 {
69 }
70
GetInstance()71 shared_ptr<RingtoneLanguageManager> RingtoneLanguageManager::GetInstance()
72 {
73 if (instance_ == nullptr) {
74 lock_guard<mutex> lock(mutex_);
75
76 if (instance_ == nullptr) {
77 instance_ = make_shared<RingtoneLanguageManager>();
78 }
79 }
80 return instance_;
81 }
82
SyncAssetLanguage()83 void RingtoneLanguageManager::SyncAssetLanguage()
84 {
85 RINGTONE_INFO_LOG("SyncAssetLanguage start.");
86 systemLanguage_ = GetSystemLanguage();
87 if (systemLanguage_.empty()) {
88 RINGTONE_ERR_LOG("Failed to get system language");
89 return;
90 }
91 RINGTONE_INFO_LOG("system language is %{public}s", systemLanguage_.c_str());
92 if (strncmp(systemLanguage_.c_str(), CHINESE_ABBREVIATION.c_str(), CHINESE_ABBREVIATION.size()) == 0) {
93 systemLanguage_ = CHINESE_ABBREVIATION;
94 } else {
95 systemLanguage_ = ENGLISH_ABBREVIATION;
96 }
97 UpdateRingtoneLanguage();
98 UpdateVibrationLanguage();
99 RINGTONE_INFO_LOG("SyncAssetLanguage end.");
100 }
101
GetSystemLanguage()102 string RingtoneLanguageManager::GetSystemLanguage()
103 {
104 char param[SYSPARA_SIZE] = {0};
105 int status = GetParameter(LANGUAGE_KEY, "", param, SYSPARA_SIZE);
106 if (status > 0) {
107 return param;
108 }
109 status = GetParameter(DEFAULT_LANGUAGE_KEY, "", param, SYSPARA_SIZE);
110 if (status > 0) {
111 return param;
112 }
113 return "";
114 }
115
UpdateRingtoneLanguage()116 void RingtoneLanguageManager::UpdateRingtoneLanguage()
117 {
118 RINGTONE_INFO_LOG("UpdateRingtonLanguage start.");
119 int32_t rowCount = 0;
120 std::shared_ptr<NativeRdb::ResultSet> resultSet;
121 if (CheckLanguageTypeByRingtone(rowCount, resultSet) != E_OK) {
122 return;
123 }
124 RINGTONE_INFO_LOG("%{public}d ring tones need to be sync", rowCount);
125 if (rowCount == 0) {
126 return;
127 }
128 #ifdef USE_CONFIG_POLICY
129 char buf[MAX_PATH_LEN] = {0};
130 char *path = GetOneCfgFile(RINGTONE_MULTILINGUAL_FILE_PATH, buf, MAX_PATH_LEN);
131 if (path == nullptr || *path == '\0') {
132 RINGTONE_ERR_LOG("GetOneCfgFile for %{public}s failed.", RINGTONE_MULTILINGUAL_FILE_PATH);
133 return;
134 }
135 #else
136 const char *path = RINGTONE_MULTILINGUAL_FILE_PATH;
137 #endif
138
139 if (!ReadMultilingualResources(path, RINGTONE_FILE)) {
140 return;
141 }
142 ChangeLanguageDataToRingtone(rowCount, resultSet);
143 RINGTONE_INFO_LOG("UpdateRingtonLanguage end.");
144 }
145
UpdateVibrationLanguage()146 void RingtoneLanguageManager::UpdateVibrationLanguage()
147 {
148 RINGTONE_INFO_LOG("UpdateVibrationLanguage start.");
149 int32_t rowCount = 0;
150 std::shared_ptr<NativeRdb::ResultSet> resultSet;
151 if (CheckLanguageTypeByVibration(rowCount, resultSet) != E_OK) {
152 return;
153 }
154 RINGTONE_INFO_LOG("%{public}d vibration need to be sync", rowCount);
155 if (rowCount == 0) {
156 return;
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