• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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