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 "CloudMediaAssetManager"
17 
18 #include "cloud_media_asset_manager.h"
19 
20 #include <iostream>
21 #include <chrono>
22 #include <mutex>
23 
24 #include "abs_rdb_predicates.h"
25 #include "cloud_media_asset_download_operation.h"
26 #include "cloud_media_asset_types.h"
27 #include "cloud_sync_utils.h"
28 #include "media_column.h"
29 #include "media_file_utils.h"
30 #include "media_log.h"
31 #include "medialibrary_album_fusion_utils.h"
32 #include "medialibrary_async_worker.h"
33 #include "medialibrary_command.h"
34 #include "medialibrary_db_const.h"
35 #include "medialibrary_errno.h"
36 #include "medialibrary_operation.h"
37 #include "medialibrary_rdb_utils.h"
38 #include "medialibrary_rdbstore.h"
39 #include "medialibrary_tracer.h"
40 #include "medialibrary_type_const.h"
41 #include "medialibrary_unistore_manager.h"
42 #include "rdb_store.h"
43 #include "result_set_utils.h"
44 #include "thumbnail_service.h"
45 
46 using namespace std;
47 using namespace OHOS::NativeRdb;
48 
49 namespace OHOS {
50 namespace Media {
51 static const std::string UNKNOWN_VALUE = "NA";
52 std::shared_ptr<CloudMediaAssetDownloadOperation> CloudMediaAssetManager::operation_ = nullptr;
53 std::mutex CloudMediaAssetManager::mutex_;
54 std::atomic<TaskDeleteState> CloudMediaAssetManager::doDeleteTask_ = TaskDeleteState::IDLE;
55 static const int32_t BATCH_DELETE_CLOUD_FILE = 200;
56 static const int32_t CYCLE_NUMBER = 2000;
57 static const int32_t SLEEP_FOR_DELETE = 1000;
58 static const std::string DELETE_DISPLAY_NAME = "cloud_media_asset_deleted";
59 const std::string UPDATE_DB_DATA_FOR_DELETED =
60     "UPDATE Photos SET clean_flag = 1, dirty = -1, cloud_version = 0, cloud_id = NULL, "
61     "display_name = 'cloud_media_asset_deleted' WHERE file_id IN "
62     "(SELECT file_id FROM Photos WHERE display_name <> 'cloud_media_asset_deleted' AND position = 2 LIMIT 200);";
63 
GetInstance()64 CloudMediaAssetManager& CloudMediaAssetManager::GetInstance()
65 {
66     static CloudMediaAssetManager instance;
67     return instance;
68 }
69 
CheckDownloadTypeOfTask(const CloudMediaDownloadType & type)70 int32_t CloudMediaAssetManager::CheckDownloadTypeOfTask(const CloudMediaDownloadType &type)
71 {
72     if (static_cast<int32_t>(type) < static_cast<int32_t>(CloudMediaDownloadType::DOWNLOAD_FORCE) ||
73         static_cast<int32_t>(type) > static_cast<int32_t>(CloudMediaDownloadType::DOWNLOAD_GENTLE)) {
74         MEDIA_ERR_LOG("CloudMediaDownloadType invalid input. downloadType: %{public}d", static_cast<int32_t>(type));
75         return E_ERR;
76     }
77     return E_OK;
78 }
79 
StartDownloadCloudAsset(const CloudMediaDownloadType & type)80 int32_t CloudMediaAssetManager::StartDownloadCloudAsset(const CloudMediaDownloadType &type)
81 {
82     if (operation_ == nullptr) {
83         CloudMediaAssetDownloadOperation taskOperator;
84         operation_ = taskOperator.GetInstance();
85     }
86     if (CheckDownloadTypeOfTask(type) != E_OK) {
87         return E_ERR;
88     }
89 
90     switch (operation_->GetTaskStatus()) {
91         case CloudMediaAssetTaskStatus::IDLE: {
92             return operation_->StartDownloadTask(static_cast<int32_t>(type));
93         }
94         case CloudMediaAssetTaskStatus::PAUSED: {
95             return operation_->ManualActiveRecoverTask(static_cast<int32_t>(type));
96         }
97         case CloudMediaAssetTaskStatus::DOWNLOADING: {
98             if (type == operation_->GetDownloadType()) {
99                 MEDIA_WARN_LOG("No status changed.");
100                 return E_OK;
101             }
102             if (type == CloudMediaDownloadType::DOWNLOAD_GENTLE) {
103                 return operation_->PauseDownloadTask(CloudMediaTaskPauseCause::BACKGROUND_TASK_UNAVAILABLE);
104             }
105             return E_ERR;
106         }
107         default: {
108             MEDIA_ERR_LOG("StartDownloadCloudAsset failed. now: taskStatus_, %{public}d; \
109                 downloadType_, %{public}d. input: type, %{public}d;",
110                 static_cast<int32_t>(operation_->GetTaskStatus()), static_cast<int32_t>(operation_->GetDownloadType()),
111                 static_cast<int32_t>(type));
112             return E_ERR;
113         }
114     }
115 }
116 
RecoverDownloadCloudAsset(const CloudMediaTaskRecoverCause & cause)117 int32_t CloudMediaAssetManager::RecoverDownloadCloudAsset(const CloudMediaTaskRecoverCause &cause)
118 {
119     if (operation_ == nullptr || operation_->GetTaskStatus() == CloudMediaAssetTaskStatus::IDLE) {
120         return E_ERR;
121     }
122     MEDIA_INFO_LOG("enter RecoverDownloadCloudAsset, RecoverCause: %{public}d", static_cast<int32_t>(cause));
123     CHECK_AND_RETURN_RET_LOG(operation_->GetTaskStatus() != CloudMediaAssetTaskStatus::DOWNLOADING, E_OK,
124         "The task status is download, no need to recover.");
125     int32_t ret = operation_->PassiveStatusRecoverTask(cause);
126     MEDIA_INFO_LOG("end to RecoverDownloadCloudAsset, status: %{public}s, ret: %{public}d.",
127         GetCloudMediaAssetTaskStatus().c_str(), ret);
128     return ret;
129 }
130 
PauseDownloadCloudAsset(const CloudMediaTaskPauseCause & pauseCause)131 int32_t CloudMediaAssetManager::PauseDownloadCloudAsset(const CloudMediaTaskPauseCause &pauseCause)
132 {
133     if (operation_ == nullptr || operation_->GetTaskStatus() == CloudMediaAssetTaskStatus::IDLE) {
134         MEDIA_INFO_LOG("no need to pause");
135         return E_OK;
136     }
137     int32_t ret = operation_->PauseDownloadTask(pauseCause);
138     MEDIA_INFO_LOG("end to PauseDownloadCloudAsset, status: %{public}s, ret: %{public}d.",
139         GetCloudMediaAssetTaskStatus().c_str(), ret);
140     return ret;
141 }
142 
CancelDownloadCloudAsset()143 int32_t CloudMediaAssetManager::CancelDownloadCloudAsset()
144 {
145     if (operation_ == nullptr || operation_->GetTaskStatus() == CloudMediaAssetTaskStatus::IDLE) {
146         MEDIA_INFO_LOG("no need to cancel");
147         return E_OK;
148     }
149     int32_t ret = operation_->CancelDownloadTask();
150     operation_.reset();
151     return ret;
152 }
153 
StartDeleteCloudMediaAssets()154 void CloudMediaAssetManager::StartDeleteCloudMediaAssets()
155 {
156     TaskDeleteState expect = TaskDeleteState::IDLE;
157     if (doDeleteTask_.compare_exchange_strong(expect, TaskDeleteState::BACKGROUND_DELETE)) {
158         MEDIA_INFO_LOG("start delete cloud media assets task.");
159         DeleteAllCloudMediaAssetsAsync();
160     }
161 }
162 
StopDeleteCloudMediaAssets()163 void CloudMediaAssetManager::StopDeleteCloudMediaAssets()
164 {
165     TaskDeleteState expect = TaskDeleteState::BACKGROUND_DELETE;
166     if (!doDeleteTask_.compare_exchange_strong(expect, TaskDeleteState::IDLE)) {
167         MEDIA_INFO_LOG("current status is not suitable for stop delete cloud media assets task.");
168     }
169 }
170 
DeleteBatchCloudFile(const std::vector<std::string> & fileIds)171 int32_t CloudMediaAssetManager::DeleteBatchCloudFile(const std::vector<std::string> &fileIds)
172 {
173     MediaLibraryTracer tracer;
174     tracer.Start("DeleteBatchCloudFile");
175     auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
176     CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, E_ERR, "DeleteBatchCloudFile failed. rdbStore is null");
177     AbsRdbPredicates deletePredicates(PhotoColumn::PHOTOS_TABLE);
178     deletePredicates.In(MediaColumn::MEDIA_ID, fileIds);
179     int32_t deletedRows = E_HAS_DB_ERROR;
180     int32_t ret = rdbStore->Delete(deletedRows, deletePredicates);
181     if (ret != NativeRdb::E_OK || deletedRows <= 0) {
182         MEDIA_ERR_LOG("Delete operation failed. ret %{public}d. Deleted %{public}d", ret, deletedRows);
183         return E_ERR;
184     }
185     MEDIA_INFO_LOG("Delete operation successful. ret %{public}d. Deleted %{public}d", ret, deletedRows);
186     return E_OK;
187 }
188 
ReadyDataForDelete(std::vector<std::string> & fileIds,std::vector<std::string> & paths,std::vector<std::string> & dateTakens)189 int32_t CloudMediaAssetManager::ReadyDataForDelete(std::vector<std::string> &fileIds, std::vector<std::string> &paths,
190     std::vector<std::string> &dateTakens)
191 {
192     MediaLibraryTracer tracer;
193     tracer.Start("ReadyDataForDelete");
194     MEDIA_INFO_LOG("enter ReadyDataForDelete");
195     AbsRdbPredicates queryPredicates(PhotoColumn::PHOTOS_TABLE);
196     queryPredicates.EqualTo(MediaColumn::MEDIA_NAME, DELETE_DISPLAY_NAME);
197     queryPredicates.Limit(BATCH_DELETE_CLOUD_FILE);
198     vector<string> columns = {MediaColumn::MEDIA_ID, MediaColumn::MEDIA_FILE_PATH, MediaColumn::MEDIA_DATE_TAKEN};
199 
200     auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
201     CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, E_ERR, "ReadyDataForDelete failed. rdbStorePtr is null");
202     auto resultSet = rdbStore->Query(queryPredicates, columns);
203     CHECK_AND_RETURN_RET_LOG(resultSet != nullptr, E_ERR, "ReadyDataForDelete failed. resultSet is null");
204     while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
205         string path = GetStringVal(MediaColumn::MEDIA_FILE_PATH, resultSet);
206         if (path.empty()) {
207             MEDIA_WARN_LOG("Failed to get path!");
208             continue;
209         }
210         MEDIA_INFO_LOG("get path: %{public}s.", MediaFileUtils::DesensitizePath(path).c_str());
211         fileIds.push_back(GetStringVal(MediaColumn::MEDIA_ID, resultSet));
212         paths.push_back(path);
213         dateTakens.push_back(GetStringVal(MediaColumn::MEDIA_DATE_TAKEN, resultSet));
214     }
215     resultSet->Close();
216     return E_OK;
217 }
218 
DeleteAllCloudMediaAssetsOperation(AsyncTaskData * data)219 void CloudMediaAssetManager::DeleteAllCloudMediaAssetsOperation(AsyncTaskData *data)
220 {
221     std::lock_guard<std::mutex> lock(mutex_);
222     MEDIA_INFO_LOG("enter DeleteAllCloudMediaAssetsOperation");
223     MediaLibraryTracer tracer;
224     tracer.Start("DeleteAllCloudMediaAssetsOperation");
225 
226     std::vector<std::string> fileIds;
227     std::vector<std::string> paths;
228     std::vector<std::string> dateTakens;
229     int32_t cycleNumber = 0;
230     while (doDeleteTask_.load() > TaskDeleteState::IDLE && cycleNumber <= CYCLE_NUMBER) {
231         int32_t ret = ReadyDataForDelete(fileIds, paths, dateTakens);
232         if (ret != E_OK || fileIds.empty()) {
233             MEDIA_WARN_LOG("ReadyDataForDelete failed or fileIds is empty, ret: %{public}d, size: %{public}d",
234                 ret, static_cast<int32_t>(fileIds.size()));
235             break;
236         }
237         for (size_t i = 0; i < fileIds.size(); i++) {
238             if (!ThumbnailService::GetInstance()->HasInvalidateThumbnail(
239                 fileIds[i], PhotoColumn::PHOTOS_TABLE, paths[i], dateTakens[i])) {
240                 MEDIA_ERR_LOG("HasInvalidateThumbnail failed!");
241                 break;
242             }
243         }
244         ret = DeleteBatchCloudFile(fileIds);
245         if (ret != E_OK) {
246             MEDIA_ERR_LOG("DeleteBatchCloudFile failed!");
247             break;
248         }
249         fileIds.clear();
250         paths.clear();
251         dateTakens.clear();
252         cycleNumber++;
253         this_thread::sleep_for(chrono::milliseconds(SLEEP_FOR_DELETE));
254     }
255     doDeleteTask_.store(TaskDeleteState::IDLE);
256 }
257 
DeleteAllCloudMediaAssetsAsync()258 void CloudMediaAssetManager::DeleteAllCloudMediaAssetsAsync()
259 {
260     shared_ptr<MediaLibraryAsyncWorker> asyncWorker = MediaLibraryAsyncWorker::GetInstance();
261     if (asyncWorker == nullptr) {
262         MEDIA_ERR_LOG("Can not get asyncWorker");
263         return;
264     }
265     shared_ptr<MediaLibraryAsyncTask> deleteAsyncTask =
266         make_shared<MediaLibraryAsyncTask>(DeleteAllCloudMediaAssetsOperation, nullptr);
267     if (deleteAsyncTask == nullptr) {
268         MEDIA_ERR_LOG("Can not get deleteAsyncTask");
269         return;
270     }
271     asyncWorker->AddTask(deleteAsyncTask, true);
272 }
273 
HasDataForUpdate()274 static bool HasDataForUpdate()
275 {
276     auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
277     CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, false, "HasDataForUpdate failed. rdbStore is null.");
278     AbsRdbPredicates predicates(PhotoColumn::PHOTOS_TABLE);
279     predicates.NotEqualTo(MediaColumn::MEDIA_NAME, DELETE_DISPLAY_NAME);
280     predicates.EqualTo(PhotoColumn::PHOTO_POSITION, to_string(static_cast<int32_t>(PhotoPositionType::CLOUD)));
281     predicates.Limit(1);
282     const std::vector<std::string> columns;
283     std::shared_ptr<NativeRdb::ResultSet> resultSetForInfo = rdbStore->Query(predicates, columns);
284     int32_t rowCount = 0;
285     CHECK_AND_RETURN_RET_LOG(resultSetForInfo != nullptr, false, "HasDataForUpdate failed. resultSetForInfo is null.");
286     CHECK_AND_RETURN_RET_LOG(resultSetForInfo->GetRowCount(rowCount) == NativeRdb::E_OK, false, "GetRowCount failed.");
287     resultSetForInfo->Close();
288     CHECK_AND_RETURN_RET_LOG(rowCount > 0, false, "RowCount is invalid.");
289     return true;
290 }
291 
UpdateCloudMeidaAssets()292 int32_t CloudMediaAssetManager::UpdateCloudMeidaAssets()
293 {
294     MediaLibraryTracer tracer;
295     tracer.Start("UpdateCloudMeidaAssets");
296     auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
297     CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, E_ERR, "QueryDownloadFilesNeeded failed. rdbStore is null.");
298     int32_t cycleNumber = 0;
299     while (HasDataForUpdate() && cycleNumber <= CYCLE_NUMBER) {
300         int32_t ret = rdbStore->ExecuteSql(UPDATE_DB_DATA_FOR_DELETED);
301         CHECK_AND_RETURN_RET_LOG(ret == NativeRdb::E_OK, ret, "execute updateSql failed. ret %{public}d.", ret);
302         cycleNumber++;
303         MEDIA_INFO_LOG("cycleNumber is %{public}d", cycleNumber);
304     }
305     CHECK_AND_RETURN_RET_LOG(cycleNumber > 0, E_ERR, "No db data need update.");
306     return E_OK;
307 }
308 
ForceRetainDownloadCloudMedia()309 int32_t CloudMediaAssetManager::ForceRetainDownloadCloudMedia()
310 {
311     MEDIA_INFO_LOG("enter ForceRetainDownloadCloudMedia.");
312     MediaLibraryTracer tracer;
313     tracer.Start("ForceRetainDownloadCloudMedia");
314     int32_t ret = UpdateCloudMeidaAssets();
315     CHECK_AND_RETURN_RET_LOG(ret == E_OK, ret, "ForceRetainDownloadCloudMedia failed. ret %{public}d.", ret);
316     MediaLibraryAlbumFusionUtils::RefreshAllAlbums();
317     TaskDeleteState expect = TaskDeleteState::IDLE;
318     if (doDeleteTask_.compare_exchange_strong(expect, TaskDeleteState::ACTIVE_DELETE)) {
319         MEDIA_INFO_LOG("start delete cloud media assets task.");
320         DeleteAllCloudMediaAssetsAsync();
321     } else {
322         doDeleteTask_.store(TaskDeleteState::ACTIVE_DELETE);
323     }
324     MEDIA_INFO_LOG("end to ForceRetainDownloadCloudMedia.");
325     return E_OK;
326 }
327 
GetCloudMediaAssetTaskStatus()328 std::string CloudMediaAssetManager::GetCloudMediaAssetTaskStatus()
329 {
330     if (operation_ == nullptr || operation_->GetTaskStatus() == CloudMediaAssetTaskStatus::IDLE) {
331         MEDIA_ERR_LOG("cloud media download task not exit.");
332         return to_string(static_cast<int32_t>(CloudMediaAssetTaskStatus::IDLE)) + ",0,0,0,0,0";
333     }
334     return to_string(static_cast<int32_t>(operation_->GetTaskStatus())) + "," + operation_->GetTaskInfo() + "," +
335         to_string(static_cast<int32_t>(operation_->GetTaskPauseCause()));
336 }
337 
HandleCloudMediaAssetUpdateOperations(MediaLibraryCommand & cmd)338 int32_t CloudMediaAssetManager::HandleCloudMediaAssetUpdateOperations(MediaLibraryCommand &cmd)
339 {
340     switch (cmd.GetOprnType()) {
341         case OperationType::CLOUD_MEDIA_ASSET_TASK_START_FORCE: {
342             return StartDownloadCloudAsset(CloudMediaDownloadType::DOWNLOAD_FORCE);
343         }
344         case OperationType::CLOUD_MEDIA_ASSET_TASK_START_GENTLE: {
345             return StartDownloadCloudAsset(CloudMediaDownloadType::DOWNLOAD_GENTLE);
346         }
347         case OperationType::CLOUD_MEDIA_ASSET_TASK_PAUSE: {
348             return PauseDownloadCloudAsset(CloudMediaTaskPauseCause::USER_PAUSED);
349         }
350         case OperationType::CLOUD_MEDIA_ASSET_TASK_CANCEL: {
351             return CancelDownloadCloudAsset();
352         }
353         case OperationType::CLOUD_MEDIA_ASSET_TASK_RETAIN_FORCE: {
354             return ForceRetainDownloadCloudMedia();
355         }
356         default: {
357             MEDIA_ERR_LOG("OprnType is not exit.");
358             return E_ERR;
359         }
360     }
361 }
362 
HandleCloudMediaAssetGetTypeOperations(MediaLibraryCommand & cmd)363 string CloudMediaAssetManager::HandleCloudMediaAssetGetTypeOperations(MediaLibraryCommand &cmd)
364 {
365     switch (cmd.GetOprnType()) {
366         case OperationType::CLOUD_MEDIA_ASSET_TASK_STATUS_QUERY: {
367             return GetCloudMediaAssetTaskStatus();
368         }
369         default: {
370             MEDIA_ERR_LOG("OprnType is not exit.");
371             return "";
372         }
373     }
374 }
375 
SetIsThumbnailUpdate()376 bool CloudMediaAssetManager::SetIsThumbnailUpdate()
377 {
378     if (operation_ == nullptr || operation_->GetTaskStatus() == CloudMediaAssetTaskStatus::IDLE) {
379         return false;
380     }
381     if (!operation_->isThumbnailUpdate_) {
382         MEDIA_INFO_LOG("Success set isThumbnailUpdate.");
383         operation_->isThumbnailUpdate_ = true;
384     }
385     MEDIA_INFO_LOG("Update count and size of download cloud media asset.");
386     if (operation_->InitDownloadTaskInfo() != E_OK) {
387         MEDIA_INFO_LOG("remainCount of download cloud media assets is 0.");
388         operation_->CancelDownloadTask();
389     }
390     return true;
391 }
392 
GetTaskStatus()393 int32_t CloudMediaAssetManager::GetTaskStatus()
394 {
395     if (operation_ == nullptr) {
396         return static_cast<int32_t>(CloudMediaAssetTaskStatus::IDLE);
397     }
398     return static_cast<int32_t>(operation_->GetTaskStatus());
399 }
400 
GetDownloadType()401 int32_t CloudMediaAssetManager::GetDownloadType()
402 {
403     if (operation_ == nullptr) {
404         MEDIA_INFO_LOG("cloud media download task not exit.");
405         return E_ERR;
406     }
407     return static_cast<int32_t>(operation_->GetDownloadType());
408 }
409 
SetBgDownloadPermission(const bool & flag)410 bool CloudMediaAssetManager::SetBgDownloadPermission(const bool &flag)
411 {
412     if (operation_ == nullptr || operation_->GetTaskStatus() == CloudMediaAssetTaskStatus::IDLE) {
413         return false;
414     }
415     MEDIA_INFO_LOG("Success set isBgDownloadPermission, flag: %{public}d.", static_cast<int32_t>(flag));
416     operation_->isBgDownloadPermission_ = flag;
417     return true;
418 }
419 } // namespace Media
420 } // namespace OHOS