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 "RingtoneDualFwkRestore"
17 
18 #include "ringtone_dualfwk_restore.h"
19 
20 #include <fcntl.h>
21 #include <sys/sendfile.h>
22 #include <sys/stat.h>
23 #include <sstream>
24 
25 #include "datashare_helper.h"
26 #include "datashare_predicates.h"
27 #include "directory_ex.h"
28 #include "dualfwk_conf_parser.h"
29 #include "dualfwk_conf_loader.h"
30 #include "dualfwk_sound_setting.h"
31 #include "file_asset.h"
32 #include "fetch_result.h"
33 #include "iservice_registry.h"
34 #include "result_set_utils.h"
35 #include "ringtone_errno.h"
36 #include "ringtone_file_utils.h"
37 #include "ringtone_log.h"
38 #include "ringtone_restore_db_utils.h"
39 #include "ringtone_restore_base.h"
40 #include "ringtone_restore_type.h"
41 #include "ringtone_type.h"
42 #include "ringtone_utils.h"
43 #include "userfile_manager_types.h"
44 
45 namespace OHOS {
46 namespace Media {
47 using namespace std;
48 
49 constexpr int STORAGE_MANAGER_MANAGER_ID = 5003;
50 static const string DUALFWK_SOUND_CONF_XML = "backup";
51 
CreateMediaDataShare(int32_t systemAbilityId)52 static std::shared_ptr<DataShare::DataShareHelper> CreateMediaDataShare(int32_t systemAbilityId)
53 {
54     RINGTONE_INFO_LOG("CreateDataShareHelper::CreateFileExtHelper ");
55     auto saManager = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
56     if (saManager == nullptr) {
57         RINGTONE_ERR_LOG("CreateFileExtHelper Get system ability mgr failed.");
58         return nullptr;
59     }
60     auto remoteObj = saManager->GetSystemAbility(systemAbilityId);
61     if (remoteObj == nullptr) {
62         RINGTONE_ERR_LOG("CreateDataShareHelper GetSystemAbility Service Failed.");
63         return nullptr;
64     }
65     return DataShare::DataShareHelper::Creator(remoteObj, MEDIALIBRARY_DATA_URI);
66 }
67 
LoadDualFwkConf(const std::string & backupPath)68 int32_t RingtoneDualFwkRestore::LoadDualFwkConf(const std::string &backupPath)
69 {
70     DualFwkConfLoader confLoader;
71     if (confLoader.Init() != E_OK) {
72         RINGTONE_ERR_LOG("Failed to initialize DualFwkConfLoader.");
73         return E_FAIL;
74     }
75     DualFwkConf conf;
76     confLoader.Load(conf, RESTORE_SCENE_TYPE_DUAL_UPGRADE, backupPath);
77     confLoader.ShowConf(conf);
78 
79     dualFwkSetting_ = std::make_unique<DualFwkSoundSetting>();
80     if (dualFwkSetting_ == nullptr) {
81         RINGTONE_ERR_LOG("Create DualFwkSoundSetting Failed.");
82         return E_FAIL;
83     }
84 
85     dualFwkSetting_->ProcessConf(conf);
86     return E_SUCCESS;
87 }
88 
ParseDualFwkConf(const string & xml)89 int32_t RingtoneDualFwkRestore::ParseDualFwkConf(const string &xml)
90 {
91     auto parser = std::make_unique<DualFwkConfParser>(xml);
92     if (parser == nullptr) {
93         RINGTONE_ERR_LOG("Create DualFwkConfParser Failed.");
94         return E_FAIL;
95     }
96 
97     dualFwkSetting_ = std::make_unique<DualFwkSoundSetting>();
98     if (dualFwkSetting_ == nullptr) {
99         RINGTONE_ERR_LOG("Create DualFwkSoundSetting Failed.");
100         return E_FAIL;
101     }
102 
103     if (parser->Parse() != E_SUCCESS) {
104         RINGTONE_ERR_LOG("parse dualfwk-sound-setting-xml Failed.");
105         return E_FAIL;
106     }
107 
108     parser->ConfTraval([this](std::unique_ptr<DualFwkConfRow> &conf) -> void {
109         dualFwkSetting_->ProcessConfRow(conf);
110     });
111 
112     return E_SUCCESS;
113 }
114 
Init(const std::string & backupPath)115 int32_t RingtoneDualFwkRestore::Init(const std::string &backupPath)
116 {
117     RINGTONE_INFO_LOG("Init db start");
118     if (backupPath.empty()) {
119         RINGTONE_ERR_LOG("error: backup path is null");
120         return E_INVALID_ARGUMENTS;
121     }
122 
123     mediaDataShare_ = CreateMediaDataShare(STORAGE_MANAGER_MANAGER_ID);
124     if (mediaDataShare_ == nullptr) {
125         RINGTONE_ERR_LOG("mediaDataShareHelper fail");
126         return E_FAIL;
127     }
128 
129     if (LoadDualFwkConf(backupPath + "/" + DUALFWK_SOUND_CONF_XML) != E_SUCCESS) {
130         return E_FAIL;
131     }
132 
133     if (RingtoneRestoreBase::Init(backupPath) != E_OK) {
134         return E_FAIL;
135     }
136 
137     RINGTONE_INFO_LOG("Init db successfully");
138     return E_OK;
139 }
140 
MediaUriAppendKeyValue(string & uri,const string & key,const string & value)141 static void MediaUriAppendKeyValue(string &uri, const string &key, const string &value)
142 {
143     string uriKey = key + '=';
144     if (uri.find(uriKey) != string::npos) {
145         return;
146     }
147 
148     char queryMark = (uri.find('?') == string::npos) ? '?' : '&';
149     string append = queryMark + key + '=' + value;
150 
151     size_t posJ = uri.find('#');
152     if (posJ == string::npos) {
153         uri += append;
154     } else {
155         uri.insert(posJ, append);
156     }
157 }
158 
159 static const string KEY_API_VERSION = "API_VERSION";
MakeBatchQueryWhereClause(const std::vector<std::string> & names,const std::string & predicateColumn)160 static std::string MakeBatchQueryWhereClause(const std::vector<std::string> &names,
161     const std::string &predicateColumn)
162 {
163     std::stringstream prefixSs;
164     prefixSs << predicateColumn << " in (";
165     bool start = true;
166     for (const auto& name: names) {
167         if (start) {
168             start = false;
169         } else {
170             prefixSs << ",";
171         }
172         prefixSs << "\"" << name << "\"";
173     }
174     prefixSs << ")";
175     return prefixSs.str();
176 }
177 
AssetToFileInfo(std::shared_ptr<FileInfo> infoPtr,const std::unique_ptr<FileAsset> & asset)178 static void AssetToFileInfo(std::shared_ptr<FileInfo> infoPtr, const std::unique_ptr<FileAsset> &asset)
179 {
180     infoPtr->toneId = asset->GetId();
181     infoPtr->data = asset->GetPath();
182     infoPtr->size = asset->GetSize();
183     infoPtr->displayName = asset->GetDisplayName();
184     infoPtr->title = asset->GetTitle();
185     infoPtr->mimeType = asset->GetMimeType();
186     infoPtr->mediaType = RINGTONE_MEDIA_TYPE_AUDIO;
187     infoPtr->sourceType = SOURCE_TYPE_CUSTOMISED;
188     infoPtr->dateAdded = asset->GetDateAdded();
189     infoPtr->dateModified = asset->GetDateModified();
190     infoPtr->dateTaken = asset->GetDateTaken();
191     infoPtr->duration = asset->GetDuration();
192 }
193 
QueryMediaLibForFileInfo(const std::vector<std::string> & names,std::map<std::string,std::shared_ptr<FileInfo>> & infoMap,const std::string & queryFileUriBase,const std::string & predicateColumn)194 int32_t RingtoneDualFwkRestore::QueryMediaLibForFileInfo(const std::vector<std::string> &names,
195     std::map<std::string, std::shared_ptr<FileInfo>> &infoMap,
196     const std::string &queryFileUriBase, const std::string &predicateColumn)
197 {
198     if (mediaDataShare_ == nullptr || names.empty()) {
199         RINGTONE_ERR_LOG("argument errr, return nullptr");
200         return E_ERR;
201     }
202     vector<string> columns;
203     DataShare::DataSharePredicates predicates;
204     string whereClause = MakeBatchQueryWhereClause(names, predicateColumn) + " AND " +
205         MediaColumn::MEDIA_TYPE + "=" + std::to_string(MEDIA_TYPE_AUDIO);
206     RINGTONE_INFO_LOG("Querying media_library where %{public}s", whereClause.c_str());
207     predicates.SetWhereClause(whereClause);
208 
209     std::string queryFileUri = queryFileUriBase;
210     MediaUriAppendKeyValue(queryFileUri, KEY_API_VERSION, to_string(MEDIA_API_VERSION_V10));
211     shared_ptr<DataShare::DataShareResultSet> resultSet = nullptr;
212     Uri uri(queryFileUri);
213 
214     resultSet = mediaDataShare_->Query(uri, predicates, columns);
215     if (resultSet == nullptr) {
216         RINGTONE_WARN_LOG("resultset for %{public}s is null", whereClause.c_str());
217         return E_FAIL;
218     }
219     int count = 0;
220     resultSet->GetRowCount(count);
221     RINGTONE_INFO_LOG("Querying %{public}s where %{public}s, got %{public}d records",
222         queryFileUri.c_str(), whereClause.c_str(), count);
223 
224     if (count <= 0) {
225         RINGTONE_WARN_LOG("resultset for %{public}s is empty", whereClause.c_str());
226         return E_SUCCESS;
227     }
228 
229     std::unique_ptr<FetchResult<FileAsset>> fetchFileResult = make_unique<FetchResult<FileAsset>>(
230         move(resultSet));
231 
232     for (int i = 0; i < count; i++) {
233         std::unique_ptr<FileAsset> asset = fetchFileResult->GetNextObject();
234         auto infoPtr = std::make_shared<FileInfo>();
235         AssetToFileInfo(infoPtr, asset);
236         if (predicateColumn == MediaColumn::MEDIA_TITLE) {
237             infoMap[asset->GetTitle()] = infoPtr;
238         } else {
239             infoMap[asset->GetDisplayName()] = infoPtr;
240         }
241         RINGTONE_INFO_LOG("new info found in media_lib: %{public}s", infoPtr->toString().c_str()); // debug
242     }
243     return E_SUCCESS;
244 }
245 
QueryRingToneDbForFileInfo(std::shared_ptr<NativeRdb::RdbStore> rdbStore,const std::vector<std::string> & names,std::map<std::string,std::shared_ptr<FileInfo>> & infoMap,const std::string & predicateColumn)246 int32_t RingtoneDualFwkRestore::QueryRingToneDbForFileInfo(std::shared_ptr<NativeRdb::RdbStore> rdbStore,
247     const std::vector<std::string> &names, std::map<std::string, std::shared_ptr<FileInfo>> &infoMap,
248     const std::string &predicateColumn)
249 {
250     if (rdbStore == nullptr) {
251         RINGTONE_ERR_LOG("rdb_ is nullptr, Maybe init failed.");
252         return E_FAIL;
253     }
254     string whereClause = MakeBatchQueryWhereClause(names, predicateColumn);
255     std::string queryCountSql = "SELECT * FROM " + RINGTONE_TABLE +
256         " WHERE " + whereClause +  " AND " +
257         RINGTONE_COLUMN_MEDIA_TYPE + "=" + std::to_string(RINGTONE_MEDIA_TYPE_AUDIO) + ";";
258     RINGTONE_INFO_LOG("Querying ringtonedb where %{public}s", whereClause.c_str());
259 
260     auto resultSet = rdbStore->QuerySql(queryCountSql);
261     if (resultSet == nullptr) {
262         RINGTONE_WARN_LOG("resultset for %{public}s is null", whereClause.c_str());
263         return E_FAIL;
264     }
265     int count = 0;
266     resultSet->GetRowCount(count);
267     RINGTONE_INFO_LOG("Querying ringtonedb where %{public}s, got %{public}d records",
268         whereClause.c_str(), count);
269 
270     if (count <= 0) {
271         RINGTONE_WARN_LOG("resultset for %{public}s is empty", whereClause.c_str());
272         return E_SUCCESS;
273     }
274 
275     for (int i = 0; i < count; i++) {
276         resultSet->GoToNextRow();
277         auto metaData = std::make_unique<RingtoneMetadata>();
278         if (PopulateMetadata(resultSet, metaData) != E_OK) {
279             return E_FAIL;
280         }
281         auto infoPtr = std::make_shared<FileInfo>(*metaData);
282         infoPtr->doInsert = false;
283         if (predicateColumn == RINGTONE_COLUMN_TITLE) {
284             infoMap[infoPtr->title] = infoPtr;
285         } else {
286             infoMap[infoPtr->displayName] = infoPtr;
287         }
288 
289         RINGTONE_INFO_LOG("new info found in ringtone_lib: %{public}s", infoPtr->toString().c_str()); // debug
290     }
291 
292     return E_SUCCESS;
293 }
294 
AddSettingsToFileInfo(const DualFwkSettingItem & setting,FileInfo & info)295 static void AddSettingsToFileInfo(const DualFwkSettingItem &setting, FileInfo &info)
296 {
297     switch (setting.settingType) {
298         case TONE_SETTING_TYPE_ALARM:
299             info.toneType = TONE_TYPE_ALARM;
300             info.alarmToneType = setting.toneType;
301             info.alarmToneSourceType = SOURCE_TYPE_CUSTOMISED;
302             break;
303         case TONE_SETTING_TYPE_RINGTONE:
304             info.toneType = TONE_TYPE_RINGTONE;
305             info.ringToneType = setting.toneType;
306             info.ringToneSourceType = SOURCE_TYPE_CUSTOMISED;
307             break;
308         case TONE_SETTING_TYPE_SHOT:
309             info.toneType = TONE_TYPE_NOTIFICATION;
310             info.shotToneType = setting.toneType;
311             info.shotToneSourceType = SOURCE_TYPE_CUSTOMISED;
312             break;
313         case TONE_SETTING_TYPE_NOTIFICATION:
314             info.toneType = TONE_TYPE_NOTIFICATION;
315             info.notificationToneType = setting.toneType;
316             info.notificationToneSourceType = SOURCE_TYPE_CUSTOMISED;
317             break;
318         default:
319             break;
320     }
321 }
322 
MergeQueries(const DualFwkSettingItem & setting,std::map<std::string,std::shared_ptr<FileInfo>> resultFromMediaByDisplayName,std::map<std::string,std::shared_ptr<FileInfo>> resultFromMediaByTitle,std::map<std::string,std::shared_ptr<FileInfo>> resultFromRingtoneByDisplayName,bool & doInsert)323 static std::shared_ptr<FileInfo> MergeQueries(const DualFwkSettingItem &setting,
324     std::map<std::string, std::shared_ptr<FileInfo>> resultFromMediaByDisplayName,
325     std::map<std::string, std::shared_ptr<FileInfo>> resultFromMediaByTitle,
326     std::map<std::string, std::shared_ptr<FileInfo>> resultFromRingtoneByDisplayName,
327     bool &doInsert)
328 {
329     std::shared_ptr<FileInfo> infoPtr;
330     doInsert = true;
331     auto keyName = setting.toneFileName;
332     if (resultFromMediaByDisplayName.find(keyName) != resultFromMediaByDisplayName.end()) {
333         infoPtr = resultFromMediaByDisplayName[keyName];
334         RINGTONE_INFO_LOG("found %{public}s in media_lib", keyName.c_str());
335     } else if (resultFromMediaByTitle.find(keyName) != resultFromMediaByTitle.end()) {
336         infoPtr = resultFromMediaByTitle[keyName];
337         RINGTONE_INFO_LOG("found %{public}s in media_lib", keyName.c_str());
338     } else if (resultFromRingtoneByDisplayName.find(keyName) != resultFromRingtoneByDisplayName.end()) {
339         infoPtr = resultFromRingtoneByDisplayName[keyName];
340         RINGTONE_INFO_LOG("found %{public}s in ringtone db", keyName.c_str());
341         doInsert = false;
342     } else if (setting.isTitle) {
343         keyName = RingtoneUtils::ReplaceAll(keyName + ".ogg", " ", "_");
344         if (resultFromRingtoneByDisplayName.find(keyName) != resultFromRingtoneByDisplayName.end()) {
345             infoPtr = resultFromRingtoneByDisplayName[keyName];
346             RINGTONE_INFO_LOG("found %{public}s in ringtone db", keyName.c_str());
347             doInsert = false;
348         }
349     } else {
350         RINGTONE_INFO_LOG("failed to find %{public}s", keyName.c_str());
351     }
352     return infoPtr;
353 }
354 
BuildFileInfo()355 std::vector<FileInfo> RingtoneDualFwkRestore::BuildFileInfo()
356 {
357     std::vector<FileInfo> result;
358     std::vector<std::string> fileNames = dualFwkSetting_->GetFileNames();
359     std::map<std::string, std::shared_ptr<FileInfo>> resultFromMediaByDisplayName;
360     std::map<std::string, std::shared_ptr<FileInfo>> resultFromMediaByTitle;
361 
362     std::vector<std::string> displayNames = dualFwkSetting_->GetDisplayNames();
363     std::map<std::string, std::shared_ptr<FileInfo>> resultFromRingtoneByDisplayName;
364 
365     QueryMediaLibForFileInfo(fileNames, resultFromMediaByDisplayName, UFM_QUERY_AUDIO, "display_name");
366     QueryMediaLibForFileInfo(fileNames, resultFromMediaByTitle, UFM_QUERY_AUDIO, "title");
367     QueryRingToneDbForFileInfo(GetBaseDb(), displayNames, resultFromRingtoneByDisplayName, "display_name");
368 
369     for (const auto& setting : dualFwkSetting_->GetSettings()) {
370         bool doInsert = true;
371         auto infoPtr = MergeQueries(setting, resultFromMediaByDisplayName, resultFromMediaByTitle,
372             resultFromRingtoneByDisplayName, doInsert);
373         if (infoPtr == nullptr) {
374             continue;
375         }
376         FileInfo info = *infoPtr;
377         info.doInsert = doInsert;
378         AddSettingsToFileInfo(setting, info);
379         result.push_back(info);
380 
381         RINGTONE_INFO_LOG("push back into results -----> %{private}s", info.toString().c_str());
382     }
383     return result;
384 }
385 
StartRestore()386 int32_t RingtoneDualFwkRestore::StartRestore()
387 {
388     if (dualFwkSetting_ == nullptr || mediaDataShare_ == nullptr) {
389         RINGTONE_ERR_LOG("dualfwk restrore is not initialized successfully");
390         return E_ERR;
391     }
392     auto ret = RingtoneRestoreBase::StartRestore();
393     if (ret != E_OK) {
394         return ret;
395     }
396 
397     std::vector<FileInfo> infos = BuildFileInfo();
398 
399     if ((!infos.empty()) && (infos.size() != 0)) {
400         ret = InsertTones(infos);
401     }
402     FlushSettings();
403     return ret;
404 }
405 
DupToneFile(FileInfo & info)406 int32_t RingtoneDualFwkRestore::DupToneFile(FileInfo &info)
407 {
408     RINGTONE_INFO_LOG("DupToneFile from %{private}s to %{private}s", info.data.c_str(), info.restorePath.c_str());
409     std::string absDstPath = info.restorePath;
410     RINGTONE_INFO_LOG("converted dst path from %{private}s to realpath %{private}s", info.restorePath.c_str(),
411         absDstPath.c_str());
412 
413     std::string absSrcPath = info.data;
414     std::string sub = "cloud";
415     std::string replacement = "media/local";
416     auto found = absSrcPath.find(sub);
417     if (found != string::npos) {
418         absSrcPath.replace(found, sub.size(), replacement);
419         RINGTONE_INFO_LOG("converted src path from %{public}s to realpath %{public}s",
420             info.data.c_str(), absSrcPath.c_str());
421 
422         if (!RingtoneFileUtils::CopyFileUtil(absSrcPath, absDstPath)) {
423             RINGTONE_ERR_LOG("copy-file failed, src: %{public}s, err: %{public}s", absSrcPath.c_str(),
424                 strerror(errno));
425             return E_FAIL;
426         }
427     } else {
428         RINGTONE_INFO_LOG("no need to copy file.");
429         info.restorePath = absSrcPath;
430     }
431 
432     return E_SUCCESS;
433 }
434 
UpdateRestoreFileInfo(FileInfo & info)435 void RingtoneDualFwkRestore::UpdateRestoreFileInfo(FileInfo &info)
436 {
437     struct stat statInfo;
438     if (stat(info.restorePath.c_str(), &statInfo) != 0) {
439         RINGTONE_ERR_LOG("stat syscall err %{public}d", errno);
440         return;
441     }
442     info.dateModified = static_cast<int64_t>(RingtoneFileUtils::Timespec2Millisecond(statInfo.st_mtim));
443     info.displayName = RingtoneFileUtils::GetFileNameFromPath(info.restorePath);
444 }
445 
OnPrepare(FileInfo & info,const std::string & dstPath)446 bool RingtoneDualFwkRestore::OnPrepare(FileInfo &info, const std::string &dstPath)
447 {
448     if (!RingtoneFileUtils::IsFileExists(dstPath)) {
449         RINGTONE_ERR_LOG("dst path is not existing, dst path=%{public}s", dstPath.c_str());
450         return false;
451     }
452     string fileName = RingtoneFileUtils::GetFileNameFromPath(info.data);
453     if (fileName.empty()) {
454         RINGTONE_ERR_LOG("src file name is null");
455         return false;
456     }
457     string baseName = RingtoneFileUtils::GetBaseNameFromPath(info.data);
458     if (baseName.empty()) {
459         RINGTONE_ERR_LOG("src file base name is null");
460         return false;
461     }
462 
463     string extensionName = RingtoneFileUtils::GetExtensionFromPath(info.data);
464     int32_t repeatCount = 1;
465     info.restorePath = dstPath + "/" + fileName;
466     while (RingtoneFileUtils::IsFileExists(info.restorePath)) {
467         struct stat dstStatInfo {};
468         if (stat(info.restorePath.c_str(), &dstStatInfo) != 0) {
469             RINGTONE_ERR_LOG("Failed to get file %{private}s StatInfo, err=%{public}d",
470                 info.restorePath.c_str(), errno);
471             return false;
472         }
473         if (info.size == dstStatInfo.st_size) {
474             CheckSetting(info);
475             RINGTONE_ERR_LOG("samefile: srcPath=%{private}s, dstPath=%{private}s", info.data.c_str(),
476                 info.restorePath.c_str());
477             return false;
478         }
479         info.restorePath = dstPath + "/" + baseName + "(" + to_string(repeatCount++) + ")" + "." + extensionName;
480     }
481 
482     if (DupToneFile(info) != E_SUCCESS) {
483         return false;
484     }
485 
486     UpdateRestoreFileInfo(info);
487 
488     return true;
489 }
490 
OnFinished(vector<FileInfo> & infos)491 void RingtoneDualFwkRestore::OnFinished(vector<FileInfo> &infos)
492 {
493     RINGTONE_ERR_LOG("ringtone dualfwk restore finished");
494 }
495 
LoadDualFwkConf(const std::string & backupPath)496 int32_t RingtoneDualFwkRestoreClone::LoadDualFwkConf(const std::string &backupPath)
497 {
498     DualFwkConfLoader confLoader;
499     if (confLoader.Init() != E_OK) {
500         RINGTONE_ERR_LOG("Failed to initialize DualFwkConfLoader.");
501         return E_FAIL;
502     }
503     DualFwkConf conf;
504     confLoader.Load(conf, RESTORE_SCENE_TYPE_DUAL_CLONE, backupPath);
505     confLoader.ShowConf(conf);
506 
507     dualFwkSetting_ = std::make_unique<DualFwkSoundSetting>();
508     if (dualFwkSetting_ == nullptr) {
509         RINGTONE_ERR_LOG("Create DualFwkSoundSetting Failed.");
510         return E_FAIL;
511     }
512 
513     dualFwkSetting_->ProcessConf(conf);
514     return E_SUCCESS;
515 }
516 } // namespace Media
517 } // namespace OHOS
518