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 UNKNOWN_INDEX = -1;
46 const int32_t FIELD_LENGTH = 2;
47 #ifdef USE_CONFIG_POLICY
48 static constexpr char RINGTONE_MULTILINGUAL_FILE_PATH[] =
49 "etc/resource/media/audio/ringtone_list_language.xml";
50 #else
51 static constexpr char RINGTONE_MULTILINGUAL_FILE_PATH[] =
52 "/system/variant/phone/base/etc/resource/media/audio/ringtone_list_language.xml";
53 #endif
54
55 shared_ptr<RingtoneLanguageManager> RingtoneLanguageManager::instance_ = nullptr;
56 mutex RingtoneLanguageManager::mutex_;
57
RingtoneLanguageManager(void)58 RingtoneLanguageManager::RingtoneLanguageManager(void)
59 {
60 }
61
~RingtoneLanguageManager(void)62 RingtoneLanguageManager::~RingtoneLanguageManager(void)
63 {
64 }
65
GetInstance()66 shared_ptr<RingtoneLanguageManager> RingtoneLanguageManager::GetInstance()
67 {
68 if (instance_ == nullptr) {
69 lock_guard<mutex> lock(mutex_);
70
71 if (instance_ == nullptr) {
72 instance_ = make_shared<RingtoneLanguageManager>();
73 }
74 }
75 return instance_;
76 }
77
SyncAssetLanguage()78 void RingtoneLanguageManager::SyncAssetLanguage()
79 {
80 RINGTONE_INFO_LOG("SyncAssetLanguage start.");
81 systemLanguage_ = GetSystemLanguage();
82 if (systemLanguage_.empty()) {
83 RINGTONE_ERR_LOG("Failed to get system language");
84 return;
85 }
86 RINGTONE_INFO_LOG("system language is %{public}s", systemLanguage_.c_str());
87 if (strncmp(systemLanguage_.c_str(), CHINESE_ABBREVIATION.c_str(), CHINESE_ABBREVIATION.size()) == 0) {
88 systemLanguage_ = CHINESE_ABBREVIATION;
89 } else {
90 systemLanguage_ = ENGLISH_ABBREVIATION;
91 }
92 UpdateRingtoneLanguage();
93 RINGTONE_INFO_LOG("SyncAssetLanguage end.");
94 }
95
GetSystemLanguage()96 string RingtoneLanguageManager::GetSystemLanguage()
97 {
98 char param[SYSPARA_SIZE] = {0};
99 int status = GetParameter(LANGUAGE_KEY, "", param, SYSPARA_SIZE);
100 if (status > 0) {
101 return param;
102 }
103 status = GetParameter(DEFAULT_LANGUAGE_KEY, "", param, SYSPARA_SIZE);
104 if (status > 0) {
105 return param;
106 }
107 return "";
108 }
109
UpdateRingtoneLanguage()110 void RingtoneLanguageManager::UpdateRingtoneLanguage()
111 {
112 RINGTONE_INFO_LOG("UpdateRingtonLanguage start.");
113 int32_t rowCount = 0;
114 std::shared_ptr<NativeRdb::ResultSet> resultSet;
115 if (CheckLanguageTypeByRingtone(rowCount, resultSet) != E_OK) {
116 return;
117 }
118 RINGTONE_INFO_LOG("%{public}d ring tones need to be sync", rowCount);
119 if (rowCount == 0) {
120 return;
121 }
122 #ifdef USE_CONFIG_POLICY
123 char buf[MAX_PATH_LEN] = {0};
124 char *path = GetOneCfgFile(RINGTONE_MULTILINGUAL_FILE_PATH, buf, MAX_PATH_LEN);
125 if (path == nullptr || *path == '\0') {
126 RINGTONE_ERR_LOG("GetOneCfgFile for %{public}s failed.", RINGTONE_MULTILINGUAL_FILE_PATH);
127 return;
128 }
129 #else
130 const char *path = RINGTONE_MULTILINGUAL_FILE_PATH;
131 #endif
132
133 if (!ReadMultilingualResources(path, RINGTONE_FILE)) {
134 return;
135 }
136 ChangeLanguageDataToRingtone(rowCount, resultSet);
137 RINGTONE_INFO_LOG("UpdateRingtonLanguage end.");
138 }
139
CheckLanguageTypeByRingtone(int32_t & rowCount,shared_ptr<ResultSet> & resultSet)140 int32_t RingtoneLanguageManager::CheckLanguageTypeByRingtone(int32_t &rowCount,
141 shared_ptr<ResultSet> &resultSet)
142 {
143 vector<string> columns = {
144 RINGTONE_COLUMN_TONE_ID,
145 RINGTONE_COLUMN_DATA
146 };
147
148 auto rawRdb = RingtoneRdbStore::GetInstance()->GetRaw();
149 if (rawRdb == nullptr) {
150 RINGTONE_ERR_LOG("failed to get raw rdb");
151 return E_RDB;
152 }
153
154 AbsRdbPredicates absRdbPredicates(RINGTONE_TABLE);
155 absRdbPredicates.EqualTo(RINGTONE_COLUMN_SOURCE_TYPE, SYSINIT_TYPE);
156 absRdbPredicates.And();
157 absRdbPredicates.BeginWrap();
158 absRdbPredicates.NotEqualTo(RINGTONE_COLUMN_DISPLAY_LANGUAGE_TYPE, systemLanguage_);
159 absRdbPredicates.Or();
160 absRdbPredicates.IsNull(RINGTONE_COLUMN_DISPLAY_LANGUAGE_TYPE);
161 absRdbPredicates.EndWrap();
162 resultSet = rawRdb->Query(absRdbPredicates, columns);
163 if (resultSet == nullptr) {
164 RINGTONE_ERR_LOG("failed to query rdb");
165 return E_RDB;
166 }
167
168 int32_t ret = resultSet->GetRowCount(rowCount);
169 if (ret != NativeRdb::E_OK) {
170 RINGTONE_ERR_LOG("failed to get resultSet row count");
171 return E_RDB;
172 }
173 return E_OK;
174 }
175
ChangeLanguageDataToRingtone(int32_t rowCount,const std::shared_ptr<ResultSet> & resultSet)176 void RingtoneLanguageManager::ChangeLanguageDataToRingtone(int32_t rowCount,
177 const std::shared_ptr<ResultSet> &resultSet)
178 {
179 auto rawRdb = RingtoneRdbStore::GetInstance()->GetRaw();
180 if (rawRdb == nullptr) {
181 RINGTONE_ERR_LOG("failed to get raw rdb");
182 return;
183 }
184
185 vector<pair<string, int>> fieldIndex = {
186 { RINGTONE_COLUMN_TONE_ID, UNKNOWN_INDEX },
187 { RINGTONE_COLUMN_DATA, UNKNOWN_INDEX }
188 };
189 if (GetFieldIndex(resultSet, fieldIndex) != E_OK) {
190 return;
191 }
192
193 for (int i = 0; i < rowCount; i++) {
194 if (resultSet->GoToRow(i) != E_OK) {
195 RINGTONE_ERR_LOG("failed to goto row : %{public}d", i);
196 return;
197 }
198
199 ValuesBucket values;
200 int ringtoneId;
201 if (SetValuesFromResultSet(resultSet, fieldIndex, values, ringtoneId) == E_OK) {
202 AbsRdbPredicates absRdbPredicates(RINGTONE_TABLE);
203 absRdbPredicates.EqualTo(RINGTONE_COLUMN_TONE_ID, ringtoneId);
204 int32_t changedRows;
205 int32_t result = rawRdb->Update(changedRows, values, absRdbPredicates);
206 if (result != E_OK || changedRows <= 0) {
207 RINGTONE_ERR_LOG("Update operation failed. Result %{public}d. Updated %{public}d", result, changedRows);
208 return;
209 }
210 }
211 }
212 }
213
GetFieldIndex(const shared_ptr<ResultSet> & resultSet,vector<pair<string,int>> & fieldIndex)214 int32_t RingtoneLanguageManager::GetFieldIndex(const shared_ptr<ResultSet> &resultSet,
215 vector<pair<string, int>> &fieldIndex)
216 {
217 for (auto& field : fieldIndex) {
218 if (resultSet->GetColumnIndex(field.first, field.second) != E_OK) {
219 RINGTONE_ERR_LOG("failed to get field index");
220 return E_RDB;
221 }
222 }
223 return E_OK;
224 }
225
SetValuesFromResultSet(const shared_ptr<ResultSet> & resultSet,const vector<pair<string,int>> & fieldIndex,ValuesBucket & values,int32_t & ringtoneId)226 int32_t RingtoneLanguageManager::SetValuesFromResultSet(const shared_ptr<ResultSet> &resultSet,
227 const vector<pair<string, int>> &fieldIndex, ValuesBucket &values, int32_t &ringtoneId)
228 {
229 string data;
230 if (fieldIndex.size() < FIELD_LENGTH) {
231 return E_RDB;
232 }
233 if (resultSet->GetInt(fieldIndex[0].second, ringtoneId) != E_OK) {
234 RINGTONE_ERR_LOG("failed to get tone_id value");
235 return E_RDB;
236 }
237 if (resultSet->GetString(fieldIndex[1].second, data) != E_OK) {
238 RINGTONE_ERR_LOG("failed to get data value");
239 return E_RDB;
240 }
241 values.PutString(RINGTONE_COLUMN_DISPLAY_LANGUAGE_TYPE, systemLanguage_);
242 string realName = RingtoneFileUtils::GetBaseNameFromPath(data);
243 auto item = ringtoneTranslate_[systemLanguage_].find(realName);
244 if (item == ringtoneTranslate_[systemLanguage_].end()) {
245 return E_OK;
246 }
247 string titleName = item->second;
248 values.PutString(RINGTONE_COLUMN_TITLE, titleName);
249 return E_OK;
250 }
251
ReadMultilingualResources(const string & filePath,ResourceFileType resourceFileType)252 bool RingtoneLanguageManager::ReadMultilingualResources(const string &filePath, ResourceFileType resourceFileType)
253 {
254 std::unique_ptr<xmlDoc, decltype(&xmlFreeDoc)> docPtr(
255 xmlReadFile(filePath.c_str(), nullptr, XML_PARSE_NOBLANKS), xmlFreeDoc);
256 if (docPtr == nullptr) {
257 RINGTONE_ERR_LOG("failed to read xml file [%{public}s]", filePath.c_str());
258 xmlErrorPtr error = xmlGetLastError();
259 if (error != nullptr) {
260 RINGTONE_ERR_LOG("Error: %{public}s (line %{public}d): %{public}s",
261 error->file, error->line, error->message);
262 xmlResetLastError();
263 }
264 return false;
265 }
266
267 xmlNodePtr rootNode = xmlDocGetRootElement(docPtr.get());
268 if (rootNode == nullptr) {
269 RINGTONE_ERR_LOG("failed to read root node");
270 return false;
271 }
272 if (resourceFileType == RINGTONE_FILE) {
273 if (xmlStrcmp(rootNode->name, BAD_CAST "RingtoneList") != 0) {
274 RINGTONE_ERR_LOG("failed to root node name is not matched");
275 return false;
276 }
277 ringtoneTranslate_.clear();
278 }
279 return ParseMultilingualXml(rootNode, resourceFileType);
280 }
281
ParseMultilingualXml(xmlNodePtr & rootNode,ResourceFileType resourceFileType)282 bool RingtoneLanguageManager::ParseMultilingualXml(xmlNodePtr &rootNode, ResourceFileType resourceFileType)
283 {
284 for (xmlNodePtr itemNode = rootNode->children; itemNode; itemNode = itemNode->next) {
285 if (xmlStrcmp(itemNode->name, BAD_CAST "Language") != 0) {
286 continue;
287 }
288
289 string language;
290 auto xmlLanguage = reinterpret_cast<char*>(xmlGetProp(itemNode, BAD_CAST "type"));
291 if (xmlLanguage != nullptr) {
292 language = string(xmlLanguage);
293 xmlFree(xmlLanguage);
294 }
295
296 for (xmlNodePtr childNode = itemNode->children; childNode; childNode = childNode->next) {
297 if (resourceFileType == RINGTONE_FILE && xmlStrcmp(childNode->name, BAD_CAST "Ring") != 0) {
298 RINGTONE_ERR_LOG("failed to child node name is not matched");
299 return false;
300 }
301
302 string resourceName, displayName;
303 auto xmlResourceName = reinterpret_cast<char*>(xmlGetProp(childNode, BAD_CAST "resource_name"));
304 if (xmlResourceName) {
305 resourceName = string(xmlResourceName);
306 xmlFree(xmlResourceName);
307 }
308
309 auto xmlDisplayName = reinterpret_cast<char*>(xmlGetProp(childNode, BAD_CAST "title"));
310 if (xmlDisplayName) {
311 displayName = string(xmlDisplayName);
312 xmlFree(xmlDisplayName);
313 }
314
315 if (resourceFileType == RINGTONE_FILE && !resourceName.empty() && !displayName.empty()) {
316 ringtoneTranslate_[language][resourceName] = displayName;
317 }
318 }
319 }
320 return true;
321 }
322
323 } // namespace Media
324 } // namespace OHOS
325