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 rdbStore = RingtoneRdbStore::GetInstance();
185 if (rdbStore == nullptr) {
186 RINGTONE_ERR_LOG("failed to get rdb");
187 return E_RDB;
188 }
189 auto rawRdb = rdbStore->GetRaw();
190 if (rawRdb == nullptr) {
191 RINGTONE_ERR_LOG("failed to get raw rdb");
192 return E_RDB;
193 }
194
195 AbsRdbPredicates absRdbPredicates(RINGTONE_TABLE);
196 absRdbPredicates.EqualTo(RINGTONE_COLUMN_SOURCE_TYPE, SYSINIT_TYPE);
197 absRdbPredicates.And();
198 absRdbPredicates.BeginWrap();
199 absRdbPredicates.NotEqualTo(RINGTONE_COLUMN_DISPLAY_LANGUAGE_TYPE, systemLanguage_);
200 absRdbPredicates.Or();
201 absRdbPredicates.IsNull(RINGTONE_COLUMN_DISPLAY_LANGUAGE_TYPE);
202 absRdbPredicates.EndWrap();
203 resultSet = rawRdb->Query(absRdbPredicates, columns);
204 if (resultSet == nullptr) {
205 RINGTONE_ERR_LOG("failed to query rdb");
206 return E_RDB;
207 }
208
209 int32_t ret = resultSet->GetRowCount(rowCount);
210 if (ret != NativeRdb::E_OK) {
211 RINGTONE_ERR_LOG("failed to get resultSet row count");
212 return E_RDB;
213 }
214 return E_OK;
215 }
216
ChangeLanguageDataToRingtone(int32_t rowCount,const std::shared_ptr<ResultSet> & resultSet)217 void RingtoneLanguageManager::ChangeLanguageDataToRingtone(int32_t rowCount,
218 const std::shared_ptr<ResultSet> &resultSet)
219 {
220 auto rdbStore = RingtoneRdbStore::GetInstance();
221 if (rdbStore == nullptr) {
222 RINGTONE_ERR_LOG("failed to get rdb");
223 return;
224 }
225 auto rawRdb = rdbStore->GetRaw();
226 if (rawRdb == nullptr) {
227 RINGTONE_ERR_LOG("failed to get raw rdb");
228 return;
229 }
230
231 map<string, int> fieldIndex = {
232 { RINGTONE_COLUMN_TONE_ID, UNKNOWN_INDEX },
233 { RINGTONE_COLUMN_DATA, UNKNOWN_INDEX }
234 };
235 if (GetFieldIndex(resultSet, fieldIndex) != E_OK) {
236 return;
237 }
238
239 for (int i = 0; i < rowCount; i++) {
240 if (resultSet->GoToRow(i) != E_OK) {
241 RINGTONE_ERR_LOG("failed to goto row : %{public}d", i);
242 return;
243 }
244
245 ValuesBucket values;
246 int ringtoneId;
247 if (SetValuesFromResultSet(resultSet, fieldIndex, values, ringtoneId, RINGTONE_FILE) == E_OK) {
248 AbsRdbPredicates absRdbPredicates(RINGTONE_TABLE);
249 absRdbPredicates.EqualTo(RINGTONE_COLUMN_TONE_ID, ringtoneId);
250 int32_t changedRows;
251 int32_t result = rawRdb->Update(changedRows, values, absRdbPredicates);
252 if (result != E_OK || changedRows <= 0) {
253 RINGTONE_ERR_LOG("Update operation failed. Result %{public}d. Updated %{public}d", result, changedRows);
254 return;
255 }
256 }
257 }
258 }
259
GetFieldIndex(const std::shared_ptr<NativeRdb::ResultSet> & resultSet,std::map<std::string,int> & fieldIndex)260 int32_t RingtoneLanguageManager::GetFieldIndex(const std::shared_ptr<NativeRdb::ResultSet> &resultSet,
261 std::map<std::string, int> &fieldIndex)
262 {
263 for (auto& field : fieldIndex) {
264 if (resultSet->GetColumnIndex(field.first, field.second) != E_OK) {
265 RINGTONE_ERR_LOG("failed to get field index");
266 return E_RDB;
267 }
268 }
269 return E_OK;
270 }
271
SetValuesFromResultSet(const std::shared_ptr<NativeRdb::ResultSet> & resultSet,const std::map<std::string,int> & fieldIndex,NativeRdb::ValuesBucket & values,int32_t & indexId,ResourceFileType resourceFileType)272 int32_t RingtoneLanguageManager::SetValuesFromResultSet(const std::shared_ptr<NativeRdb::ResultSet> &resultSet,
273 const std::map<std::string, int> &fieldIndex, NativeRdb::ValuesBucket &values, int32_t &indexId,
274 ResourceFileType resourceFileType)
275 {
276 string data;
277 string idIndexField = resourceFileType == RINGTONE_FILE ? RINGTONE_COLUMN_TONE_ID : VIBRATE_COLUMN_VIBRATE_ID;
278 string dataIndexField = resourceFileType == RINGTONE_FILE ? RINGTONE_COLUMN_DATA : VIBRATE_COLUMN_DATA;
279 string titleIndexField = resourceFileType == RINGTONE_FILE ? RINGTONE_COLUMN_TITLE : VIBRATE_COLUMN_TITLE;
280 string languageIndexField = resourceFileType == RINGTONE_FILE ?
281 RINGTONE_COLUMN_DISPLAY_LANGUAGE_TYPE : VIBRATE_COLUMN_DISPLAY_LANGUAGE;
282 auto& translation = resourceFileType == RINGTONE_FILE ? ringtoneTranslate_ : vibrationTranslate_;
283
284 auto idItem = fieldIndex.find(idIndexField);
285 if (idItem == fieldIndex.end()) {
286 RINGTONE_ERR_LOG("failed to get %{public}s index", idIndexField.c_str());
287 return E_RDB;
288 }
289 if (resultSet->GetInt(idItem->second, indexId) != E_OK) {
290 RINGTONE_ERR_LOG("failed to get tone_id value");
291 return E_RDB;
292 }
293
294 auto dataItem = fieldIndex.find(dataIndexField);
295 if (dataItem == fieldIndex.end()) {
296 RINGTONE_ERR_LOG("failed to get %{public}s index", dataIndexField.c_str());
297 return E_RDB;
298 }
299 if (resultSet->GetString(dataItem->second, data) != E_OK) {
300 RINGTONE_ERR_LOG("failed to get tone_id value");
301 return E_RDB;
302 }
303
304 values.PutString(languageIndexField, systemLanguage_);
305 string realName = RingtoneFileUtils::GetBaseNameFromPath(data);
306 auto item = translation[systemLanguage_].find(realName);
307 if (item == translation[systemLanguage_].end()) {
308 return E_OK;
309 }
310 string titleName = item->second;
311 values.PutString(titleIndexField, titleName);
312 return E_OK;
313 }
314
CheckLanguageTypeByVibration(int32_t & rowCount,std::shared_ptr<NativeRdb::ResultSet> & resultSet)315 int32_t RingtoneLanguageManager::CheckLanguageTypeByVibration(int32_t &rowCount,
316 std::shared_ptr<NativeRdb::ResultSet> &resultSet)
317 {
318 vector<string> columns = {
319 VIBRATE_COLUMN_VIBRATE_ID,
320 VIBRATE_COLUMN_DATA
321 };
322
323 auto rdbStore = RingtoneRdbStore::GetInstance();
324 if (rdbStore == nullptr) {
325 RINGTONE_ERR_LOG("failed to get rdb");
326 return E_RDB;
327 }
328 auto rawRdb = rdbStore->GetRaw();
329 if (rawRdb == nullptr) {
330 RINGTONE_ERR_LOG("failed to get raw rdb");
331 return E_RDB;
332 }
333
334 AbsRdbPredicates absRdbPredicates(VIBRATE_TABLE);
335 absRdbPredicates.EqualTo(VIBRATE_COLUMN_VIBRATE_TYPE, STANDARDVIBRATION);
336 absRdbPredicates.And();
337 absRdbPredicates.BeginWrap();
338 absRdbPredicates.NotEqualTo(VIBRATE_COLUMN_DISPLAY_LANGUAGE, systemLanguage_);
339 absRdbPredicates.Or();
340 absRdbPredicates.IsNull(VIBRATE_COLUMN_DISPLAY_LANGUAGE);
341 absRdbPredicates.EndWrap();
342 resultSet = rawRdb->Query(absRdbPredicates, columns);
343 if (resultSet == nullptr) {
344 RINGTONE_ERR_LOG("failed to query rdb");
345 return E_RDB;
346 }
347
348 int32_t ret = resultSet->GetRowCount(rowCount);
349 if (ret != NativeRdb::E_OK) {
350 RINGTONE_ERR_LOG("failed to get resultSet row count");
351 return E_RDB;
352 }
353 return E_OK;
354 }
355
ChangeLanguageDataToVibration(int32_t rowCount,const std::shared_ptr<NativeRdb::ResultSet> & resultSet)356 void RingtoneLanguageManager::ChangeLanguageDataToVibration(int32_t rowCount,
357 const std::shared_ptr<NativeRdb::ResultSet> &resultSet)
358 {
359 auto rdbStore = RingtoneRdbStore::GetInstance();
360 if (rdbStore == nullptr) {
361 RINGTONE_ERR_LOG("failed to get rdb");
362 return;
363 }
364 auto rawRdb = rdbStore->GetRaw();
365 if (rawRdb == nullptr) {
366 RINGTONE_ERR_LOG("failed to get raw rdb");
367 return;
368 }
369
370 map<string, int> fieldIndex = {
371 { VIBRATE_COLUMN_VIBRATE_ID, UNKNOWN_INDEX },
372 { VIBRATE_COLUMN_DATA, UNKNOWN_INDEX }
373 };
374 if (GetFieldIndex(resultSet, fieldIndex) != E_OK) {
375 return;
376 }
377
378 for (int i = 0; i < rowCount; i++) {
379 if (resultSet->GoToRow(i) != E_OK) {
380 RINGTONE_ERR_LOG("failed to goto row : %{public}d", i);
381 return;
382 }
383
384 ValuesBucket values;
385 int vibrateId;
386 if (SetValuesFromResultSet(resultSet, fieldIndex, values, vibrateId, VIBRATION_FILE) == E_OK) {
387 AbsRdbPredicates absRdbPredicates(VIBRATE_TABLE);
388 absRdbPredicates.EqualTo(VIBRATE_COLUMN_VIBRATE_ID, vibrateId);
389 int32_t changedRows;
390 int32_t result = rawRdb->Update(changedRows, values, absRdbPredicates);
391 if (result != E_OK || changedRows <= 0) {
392 RINGTONE_ERR_LOG("Update operation failed. Result %{public}d. Updated %{public}d", result, changedRows);
393 return;
394 }
395 }
396 }
397 }
398
ReadMultilingualResources(const string & filePath,ResourceFileType resourceFileType)399 bool RingtoneLanguageManager::ReadMultilingualResources(const string &filePath, ResourceFileType resourceFileType)
400 {
401 std::unique_ptr<xmlDoc, decltype(&xmlFreeDoc)> docPtr(
402 xmlReadFile(filePath.c_str(), nullptr, XML_PARSE_NOBLANKS), xmlFreeDoc);
403 if (docPtr == nullptr) {
404 RINGTONE_ERR_LOG("failed to read xml file [%{public}s]", filePath.c_str());
405 const xmlError *error = xmlGetLastError();
406 if (error != nullptr) {
407 RINGTONE_ERR_LOG("Error: %{public}s (line %{public}d): %{public}s",
408 error->file, error->line, error->message);
409 xmlResetLastError();
410 }
411 return false;
412 }
413
414 xmlNodePtr rootNode = xmlDocGetRootElement(docPtr.get());
415 if (rootNode == nullptr) {
416 RINGTONE_ERR_LOG("failed to read root node");
417 return false;
418 }
419 if (resourceFileType == RINGTONE_FILE) {
420 if (xmlStrcmp(rootNode->name, BAD_CAST "RingtoneList") != 0) {
421 RINGTONE_ERR_LOG("failed to root node name is not matched");
422 return false;
423 }
424 ringtoneTranslate_.clear();
425 } else if (resourceFileType == VIBRATION_FILE) {
426 if (xmlStrcmp(rootNode->name, BAD_CAST "VibrationList") != 0) {
427 RINGTONE_ERR_LOG("failed to root node name is not matched");
428 return false;
429 }
430 vibrationTranslate_.clear();
431 }
432 return ParseMultilingualXml(rootNode, resourceFileType);
433 }
434
ParseMultilingualXml(xmlNodePtr & rootNode,ResourceFileType resourceFileType)435 bool RingtoneLanguageManager::ParseMultilingualXml(xmlNodePtr &rootNode, ResourceFileType resourceFileType)
436 {
437 for (xmlNodePtr itemNode = rootNode->children; itemNode; itemNode = itemNode->next) {
438 if (xmlStrcmp(itemNode->name, BAD_CAST "Language") != 0) {
439 continue;
440 }
441
442 string language;
443 auto xmlLanguage = reinterpret_cast<char*>(xmlGetProp(itemNode, BAD_CAST "type"));
444 if (xmlLanguage != nullptr) {
445 language = string(xmlLanguage);
446 xmlFree(xmlLanguage);
447 }
448
449 for (xmlNodePtr childNode = itemNode->children; childNode; childNode = childNode->next) {
450 if (resourceFileType == RINGTONE_FILE && xmlStrcmp(childNode->name, BAD_CAST "Ring") != 0) {
451 RINGTONE_ERR_LOG("failed to ringtone child node name is not matched");
452 return false;
453 } else if (resourceFileType == VIBRATION_FILE && xmlStrcmp(childNode->name, BAD_CAST "vibrtion") != 0) {
454 RINGTONE_ERR_LOG("failed to vibrate child node name is not matched");
455 return false;
456 }
457
458 string resourceName;
459 auto xmlResourceName = reinterpret_cast<char*>(xmlGetProp(childNode, BAD_CAST "resource_name"));
460 if (xmlResourceName) {
461 resourceName = string(xmlResourceName);
462 xmlFree(xmlResourceName);
463 }
464 string displayName;
465 auto xmlDisplayName = reinterpret_cast<char*>(xmlGetProp(childNode, BAD_CAST "title"));
466 if (xmlDisplayName) {
467 displayName = string(xmlDisplayName);
468 xmlFree(xmlDisplayName);
469 }
470
471 if (resourceFileType == RINGTONE_FILE && !resourceName.empty() && !displayName.empty()) {
472 ringtoneTranslate_[language][resourceName] = displayName;
473 } else if (resourceFileType == VIBRATION_FILE && !resourceName.empty() && !displayName.empty()) {
474 vibrationTranslate_[language][resourceName] = displayName;
475 }
476 }
477 }
478 return true;
479 }
480
481 } // namespace Media
482 } // namespace OHOS
483