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 #include "cloud_sync_notify_handler.h"
17
18 #include "cloud_media_asset_manager.h"
19 #include "cloud_media_asset_types.h"
20 #include "cloud_sync_utils.h"
21
22 #include <sys/stat.h>
23
24 #include "medialibrary_album_fusion_utils.h"
25 #include "medialibrary_album_operations.h"
26 #include "notify_responsibility_chain_factory.h"
27 #include "result_set_utils.h"
28 #include "thumbnail_service.h"
29 #include "medialibrary_notify.h"
30 #include "medialibrary_rdb_utils.h"
31 #include "parameters.h"
32 #include "photo_album_column.h"
33 #include "media_log.h"
34
35 using namespace std;
36
37 namespace OHOS {
38 namespace Media {
39 using ChangeType = DataShare::DataShareObserver::ChangeType;
40
41 const std::string INVALID_ZERO_ID = "0";
42
IsCloudInsertTaskPriorityHigh()43 static bool IsCloudInsertTaskPriorityHigh()
44 {
45 int32_t cloudSyncStatus = static_cast<int32_t>(system::GetParameter(CLOUDSYNC_STATUS_KEY, "0").at(0) - '0');
46 return cloudSyncStatus == CloudSyncStatus::FIRST_FIVE_HUNDRED ||
47 cloudSyncStatus == CloudSyncStatus::INCREMENT_DOWNLOAD;
48 }
49
IsCloudNotifyInfoValid(const string & cloudNotifyInfo)50 static inline bool IsCloudNotifyInfoValid(const string& cloudNotifyInfo)
51 {
52 if (cloudNotifyInfo.empty()) {
53 return false;
54 }
55 for (char const& ch : cloudNotifyInfo) {
56 if (isdigit(ch) == 0) {
57 return false;
58 }
59 }
60 return true;
61 }
62
UpdateCloudAssetDownloadTask(const bool verifyFlag)63 static void UpdateCloudAssetDownloadTask(const bool verifyFlag)
64 {
65 if (!verifyFlag) {
66 MEDIA_INFO_LOG("Current status is not suitable for task.");
67 return;
68 }
69 if (!CloudMediaAssetManager::GetInstance().SetIsThumbnailUpdate() && CloudSyncUtils::IsCloudSyncSwitchOn() &&
70 CloudSyncUtils::IsCloudDataAgingPolicyOn()) {
71 CloudMediaAssetManager::GetInstance().StartDownloadCloudAsset(CloudMediaDownloadType::DOWNLOAD_GENTLE);
72 }
73 }
74
HandleInsertEvent(const std::list<Uri> & uris)75 void CloudSyncNotifyHandler::HandleInsertEvent(const std::list<Uri> &uris)
76 {
77 bool isCloudInsertTaskPriorityHigh = IsCloudInsertTaskPriorityHigh();
78 if (!isCloudInsertTaskPriorityHigh && !ThumbnailService::GetInstance()->GetCurrentStatusForTask()) {
79 MEDIA_INFO_LOG("current status is not suitable for task");
80 return;
81 }
82 bool verifyFlag = false;
83 for (auto &uri : uris) {
84 string uriString = uri.ToString();
85 auto pos = uriString.find_last_of('/');
86 if (pos == string::npos) {
87 continue;
88 }
89 string idString = uriString.substr(pos + 1);
90 if (idString.compare(INVALID_ZERO_ID) == 0 || !IsCloudNotifyInfoValid(idString)) {
91 MEDIA_WARN_LOG("cloud observer get no valid fileId and uri : %{public}s", uriString.c_str());
92 continue;
93 }
94 if (!verifyFlag) {
95 verifyFlag = true;
96 }
97 ThumbnailService::GetInstance()->CreateAstcCloudDownload(idString, isCloudInsertTaskPriorityHigh);
98 }
99 UpdateCloudAssetDownloadTask(verifyFlag);
100 }
101
HandleDeleteEvent(const std::list<Uri> & uris)102 void CloudSyncNotifyHandler::HandleDeleteEvent(const std::list<Uri> &uris)
103 {
104 bool verifyFlag = false;
105 for (auto &uri : uris) {
106 string uriString = uri.ToString();
107 auto dateTakenPos = uriString.rfind('/');
108 if (dateTakenPos == string::npos) {
109 continue;
110 }
111 auto fileIdPos = uriString.rfind('/', dateTakenPos - 1);
112 if (fileIdPos == string::npos) {
113 continue;
114 }
115
116 string dateTaken = uriString.substr(dateTakenPos + 1);
117 string fileId = uriString.substr(fileIdPos + 1, dateTakenPos - fileIdPos - 1);
118 if (!IsCloudNotifyInfoValid(dateTaken) || !IsCloudNotifyInfoValid(fileId)) {
119 MEDIA_WARN_LOG("cloud observer get no valid uri : %{public}s", uriString.c_str());
120 continue;
121 }
122 if (!verifyFlag) {
123 verifyFlag = true;
124 }
125
126 ThumbnailService::GetInstance()->DeleteAstcWithFileIdAndDateTaken(fileId, dateTaken);
127 }
128 if (verifyFlag) {
129 CloudMediaAssetManager::GetInstance().SetIsThumbnailUpdate();
130 }
131 }
132
HandleTimeUpdateEvent(const std::list<Uri> & uris)133 void CloudSyncNotifyHandler::HandleTimeUpdateEvent(const std::list<Uri> &uris)
134 {
135 for (auto &uri : uris) {
136 string uriString = uri.ToString();
137 auto newDateTakenPos = uriString.rfind('/');
138 if (newDateTakenPos == string::npos) {
139 continue;
140 }
141 auto formerDateTakenPos = uriString.rfind('/', newDateTakenPos - 1);
142 if (formerDateTakenPos == string::npos) {
143 continue;
144 }
145 auto fileIdPos = uriString.rfind('/', formerDateTakenPos - 1);
146 if (fileIdPos == string::npos) {
147 continue;
148 }
149
150 string newDateTaken = uriString.substr(newDateTakenPos + 1);
151 string formerDateTaken = uriString.substr(formerDateTakenPos + 1, newDateTakenPos - formerDateTakenPos - 1);
152 string fileId = uriString.substr(fileIdPos + 1, formerDateTakenPos - fileIdPos - 1);
153 if (!IsCloudNotifyInfoValid(newDateTaken) || !IsCloudNotifyInfoValid(formerDateTaken) ||
154 !IsCloudNotifyInfoValid(fileId)) {
155 MEDIA_WARN_LOG("cloud observer get no valid uri : %{public}s", uriString.c_str());
156 continue;
157 }
158
159 ThumbnailService::GetInstance()->UpdateAstcWithNewDateTaken(fileId, newDateTaken, formerDateTaken);
160 }
161 }
162
HandleExtraEvent(const std::list<Uri> & uris,const ChangeType & type)163 void CloudSyncNotifyHandler::HandleExtraEvent(const std::list<Uri> &uris, const ChangeType &type)
164 {
165 ExtraChangeType extraType = static_cast<ExtraChangeType>(type);
166 if (extraType == ExtraChangeType::PHOTO_TIME_UPDATE) {
167 HandleTimeUpdateEvent(uris);
168 return;
169 }
170 MEDIA_DEBUG_LOG("change type is %{public}d, no need ThumbnailObserverOnChange", type);
171 }
172
ThumbnailObserverOnChange(const list<Uri> & uris,const ChangeType & type)173 void CloudSyncNotifyHandler::ThumbnailObserverOnChange(const list<Uri> &uris, const ChangeType &type)
174 {
175 MediaLibraryRdbUtils::SetNeedRefreshAlbum(true);
176 switch (type) {
177 case ChangeType::INSERT:
178 HandleInsertEvent(uris);
179 break;
180 case ChangeType::DELETE:
181 HandleDeleteEvent(uris);
182 break;
183 default:
184 HandleExtraEvent(uris, type);
185 break;
186 }
187 }
188
HandleDirtyDataFix(const std::list<Uri> & uris,const CloudSyncErrType & errType)189 void CloudSyncNotifyHandler::HandleDirtyDataFix(const std::list<Uri> &uris, const CloudSyncErrType &errType)
190 {
191 MediaLibraryRdbUtils::SetNeedRefreshAlbum(true);
192 switch (errType) {
193 case CloudSyncErrType::CONTENT_NOT_FOUND:
194 HandleContentNotFound(uris);
195 break;
196 case CloudSyncErrType::THM_NOT_FOUND:
197 HandleThumbnailNotFound(uris);
198 break;
199 case CloudSyncErrType::LCD_NOT_FOUND:
200 HandleLCDNotFound(uris);
201 break;
202 case CloudSyncErrType::LCD_SIZE_IS_TOO_LARGE:
203 HandleLCDSizeTooLarge(uris);
204 break;
205 case CloudSyncErrType::CONTENT_SIZE_IS_ZERO:
206 HandleContentSizeIsZero(uris);
207 break;
208 case CloudSyncErrType::ALBUM_NOT_FOUND:
209 HandleAlbumNotFound(uris);
210 break;
211 default:
212 MEDIA_ERR_LOG("HandleDirtyDataFix, Unrecognized error type : %{public}d", errType);
213 }
214 }
215
GetfileIdFromPastDirtyDataFixUri(std::string uriString)216 std::string CloudSyncNotifyHandler::GetfileIdFromPastDirtyDataFixUri(std::string uriString)
217 {
218 auto fileIdPos = uriString.rfind('/');
219 if (fileIdPos == string::npos) {
220 return "";
221 }
222 std::string fileId = uriString.substr(fileIdPos + 1, uriString.size() - fileIdPos);
223 return fileId;
224 }
225
HandleContentNotFound(const std::list<Uri> & uris)226 void CloudSyncNotifyHandler::HandleContentNotFound(const std::list<Uri> &uris)
227 {
228 for (auto &uri : uris) {
229 std::string uriString = uri.ToString();
230 std::string fileId = GetfileIdFromPastDirtyDataFixUri(uriString);
231 if (fileId == "") {
232 continue;
233 }
234 if (fileId.compare(INVALID_ZERO_ID) == 0 || !IsCloudNotifyInfoValid(fileId)) {
235 MEDIA_WARN_LOG("cloud observer get no valid uri : %{public}s", uriString.c_str());
236 continue;
237 }
238 MEDIA_INFO_LOG(
239 "ContentNotFound, uri : %{public}s", uriString.c_str());
240 }
241 }
242
HandleThumbnailNotFound(const std::list<Uri> & uris)243 void CloudSyncNotifyHandler::HandleThumbnailNotFound(const std::list<Uri> &uris)
244 {
245 for (auto &uri : uris) {
246 std::string uriString = uri.ToString();
247 std::string fileId = GetfileIdFromPastDirtyDataFixUri(uriString);
248 if (fileId == "") {
249 continue;
250 }
251 if (fileId.compare(INVALID_ZERO_ID) == 0 || !IsCloudNotifyInfoValid(fileId)) {
252 MEDIA_WARN_LOG("cloud observer get no valid uri : %{public}s", uriString.c_str());
253 continue;
254 }
255
256 int32_t err = ThumbnailService::GetInstance()->CreateThumbnailPastDirtyDataFix(fileId);
257 if (err != E_SUCCESS) {
258 MEDIA_ERR_LOG("ThumbnailService CreateThumbnailPastDirtyDataFix failed : %{public}d", err);
259 continue;
260 }
261 MEDIA_INFO_LOG("Generate thumbnail %{public}s, success ", uriString.c_str());
262 }
263 }
264
HandleLCDNotFound(const std::list<Uri> & uris)265 void CloudSyncNotifyHandler::HandleLCDNotFound(const std::list<Uri> &uris)
266 {
267 for (auto &uri : uris) {
268 std::string uriString = uri.ToString();
269 std::string fileId = GetfileIdFromPastDirtyDataFixUri(uriString);
270 if (fileId == "") {
271 continue;
272 }
273 if (fileId.compare(INVALID_ZERO_ID) == 0 || !IsCloudNotifyInfoValid(fileId)) {
274 MEDIA_WARN_LOG("cloud observer get no valid uri : %{public}s", uriString.c_str());
275 continue;
276 }
277
278 int32_t err = ThumbnailService::GetInstance()->CreateLcdPastDirtyDataFix(fileId);
279 if (err != E_SUCCESS) {
280 MEDIA_ERR_LOG("ThumbnailService CreateLCDPastDirtyDataFix failed : %{public}d", err);
281 continue;
282 }
283 MEDIA_INFO_LOG("Generate Lcd %{public}s, success ", uriString.c_str());
284 }
285 return;
286 }
287
HandleLCDSizeTooLarge(const std::list<Uri> & uris)288 void CloudSyncNotifyHandler::HandleLCDSizeTooLarge(const std::list<Uri> &uris)
289 {
290 for (auto &uri : uris) {
291 std::string uriString = uri.ToString();
292 std::string fileId = GetfileIdFromPastDirtyDataFixUri(uriString);
293 if (fileId == "") {
294 continue;
295 }
296 if (fileId.compare(INVALID_ZERO_ID) == 0 || !IsCloudNotifyInfoValid(fileId)) {
297 MEDIA_WARN_LOG("cloud observer get no valid uri : %{public}s", uriString.c_str());
298 continue;
299 }
300
301 int32_t err = ThumbnailService::GetInstance()->CreateLcdPastDirtyDataFix(fileId, THUMBNAIL_EIGHTY);
302 if (err != E_SUCCESS) {
303 MEDIA_ERR_LOG("ThumbnailService CreateLcdPastDirtyDataFix to eighty quality failed : %{public}d", err);
304 continue;
305 }
306 MEDIA_INFO_LOG("Regenerate Lcd %{public}s to eighty quality, success ", uriString.c_str());
307 }
308 return;
309 }
310
HandleContentSizeIsZero(const std::list<Uri> & uris)311 void CloudSyncNotifyHandler::HandleContentSizeIsZero(const std::list<Uri> &uris)
312 {
313 for (auto &uri : uris) {
314 std::string uriString = uri.ToString();
315 std::string fileId = GetfileIdFromPastDirtyDataFixUri(uriString);
316 if (fileId == "") {
317 continue;
318 }
319 if (fileId.compare(INVALID_ZERO_ID) == 0 || !IsCloudNotifyInfoValid(fileId)) {
320 MEDIA_WARN_LOG("cloud observer get no valid uri : %{public}s", uriString.c_str());
321 continue;
322 }
323
324 std::string filePath;
325 auto err = QueryFilePathFromFileId(fileId, filePath);
326 if (err != E_SUCCESS) {
327 MEDIA_ERR_LOG("QueryFilePathFromFileId failed : %{public}d", err);
328 continue;
329 }
330 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
331 int changeRows = 0;
332 struct stat st;
333 err = stat(filePath.c_str(), &st);
334 if (err != E_SUCCESS) {
335 MEDIA_ERR_LOG("stat failed : %{public}d", err);
336 continue;
337 }
338 if (st.st_size == 0) {
339 MEDIA_INFO_LOG("HandleContentSizeIsZero, file size is zero");
340 continue;
341 }
342 NativeRdb::ValuesBucket valuesNew;
343 valuesNew.PutLong(PhotoColumn::MEDIA_SIZE, st.st_size);
344 NativeRdb::RdbPredicates rdbPredicates(PhotoColumn::PHOTOS_TABLE);
345 rdbPredicates.EqualTo(PhotoColumn::MEDIA_ID, fileId);
346 rdbStore->Update(changeRows, valuesNew, rdbPredicates);
347 CHECK_AND_PRINT_LOG(changeRows >= 0, "Failed to update content size , ret = %{public}d", changeRows);
348 MEDIA_INFO_LOG("refresh photo size field to : %{public}d , success", static_cast<int>(st.st_size));
349 }
350 return;
351 }
352
QueryFilePathFromFileId(const std::string & id,std::string & filePath)353 int32_t CloudSyncNotifyHandler::QueryFilePathFromFileId(const std::string &id, std::string &filePath)
354 {
355 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
356 CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, E_DB_FAIL, "QueryFilePathFromFileId failed. rdbStore is null");
357 const string sqlQuery = "SELECT * From " + PhotoColumn::PHOTOS_TABLE +
358 " WHERE " + PhotoColumn::MEDIA_ID + " = " + id;
359 auto resultSet = rdbStore->QuerySql(sqlQuery);
360 CHECK_AND_RETURN_RET_LOG(
361 resultSet != nullptr && resultSet->GoToFirstRow() == NativeRdb::E_OK,
362 E_DB_FAIL, "Query not matched data fails");
363
364 filePath = get<std::string>(
365 ResultSetUtils::GetValFromColumn(MediaColumn::MEDIA_FILE_PATH, resultSet, ResultSetDataType::TYPE_STRING));
366 return E_OK;
367 }
368
QueryAlbumLpathFromFileId(const std::string & id,std::string & lpath)369 int32_t CloudSyncNotifyHandler::QueryAlbumLpathFromFileId(const std::string &id, std::string &lpath)
370 {
371 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
372 CHECK_AND_RETURN_RET_LOG(
373 rdbStore != nullptr,
374 E_DB_FAIL, "QueryAlbumLpathFromFileId failed. rdbStore is null");
375 const string sqlQuery = "SELECT * From " + PhotoColumn::PHOTOS_TABLE + " WHERE " + PhotoColumn::MEDIA_ID +
376 " = " + id;
377 auto resultSet = rdbStore->QuerySql(sqlQuery);
378 CHECK_AND_RETURN_RET_LOG(
379 resultSet != nullptr && resultSet->GoToFirstRow() == NativeRdb::E_OK,
380 E_DB_FAIL, "Query not matched data fails");
381
382 auto sourcePath = get<std::string>(
383 ResultSetUtils::GetValFromColumn(PhotoColumn::PHOTO_SOURCE_PATH, resultSet, ResultSetDataType::TYPE_STRING));
384 int32_t mediaType = GetInt32Val(PhotoColumn::MEDIA_TYPE, resultSet);
385 int32_t err = MediaLibraryAlbumOperations::GetLPathFromSourcePath(sourcePath, lpath, mediaType);
386 CHECK_AND_RETURN_RET_LOG(err == E_OK, E_DB_FAIL, "GetLPathFromSourcePath fail : %{public}s ", lpath.c_str());
387 MEDIA_INFO_LOG("QueryAlbumLpathFromFileId succcess, lpath is : %{public}s ", lpath.c_str());
388 return E_OK;
389 }
390
HandleAlbumNotFound(const std::list<Uri> & uris)391 void CloudSyncNotifyHandler::HandleAlbumNotFound(const std::list<Uri> &uris)
392 {
393 for (auto &uri : uris) {
394 std::string uriString = uri.ToString();
395 std::string fileId = GetfileIdFromPastDirtyDataFixUri(uriString);
396 if (fileId == "") {
397 continue;
398 }
399 if (fileId.compare(INVALID_ZERO_ID) == 0 || !IsCloudNotifyInfoValid(fileId)) {
400 MEDIA_WARN_LOG("cloud observer get no valid uri : %{public}s", uriString.c_str());
401 continue;
402 }
403
404 std::string lpath;
405 int64_t newAlbumId = -1;
406 bool isUserAlbum = false;
407 auto err = QueryAlbumLpathFromFileId(fileId, lpath);
408 if (err != E_SUCCESS) {
409 MEDIA_ERR_LOG("QueryAlbumLpathFromFileId failed : %{public}d", err);
410 continue;
411 }
412 MediaLibraryAlbumOperations::RecoverAlbum(fileId, lpath, isUserAlbum, newAlbumId);
413 if (newAlbumId == -1) {
414 MEDIA_ERR_LOG("HandleAlbumNotFound Fail, Recover album fails");
415 continue;
416 }
417
418 if (isUserAlbum) {
419 MediaLibraryRdbUtils::UpdateUserAlbumInternal(
420 MediaLibraryUnistoreManager::GetInstance().GetRdbStore(), {to_string(newAlbumId)});
421 } else {
422 MediaLibraryRdbUtils::UpdateSourceAlbumInternal(
423 MediaLibraryUnistoreManager::GetInstance().GetRdbStore(), {to_string(newAlbumId)});
424 }
425 auto watch = MediaLibraryNotify::GetInstance();
426 watch->Notify(MediaFileUtils::GetUriByExtrConditions(
427 PhotoAlbumColumns::ALBUM_URI_PREFIX, to_string(newAlbumId)), NotifyType::NOTIFY_ADD);
428 }
429 return;
430 }
431
MakeResponsibilityChain()432 void CloudSyncNotifyHandler::MakeResponsibilityChain()
433 {
434 string uriString = notifyInfo_.uris.front().ToString();
435 MEDIA_DEBUG_LOG("observer get first uri is : %{public}s", uriString.c_str());
436
437 if (uriString.find("file://cloudsync/Photo/HeightError/") != string::npos) {
438 return;
439 }
440
441 if (uriString.find("file://cloudsync/Photo/DownloadSuccessed/") != string::npos) {
442 return;
443 }
444
445 if (uriString.find(PhotoColumn::PHOTO_CLOUD_URI_PREFIX) != string::npos) {
446 ThumbnailObserverOnChange(notifyInfo_.uris, notifyInfo_.type);
447 }
448
449 if (uriString.find("file://cloudsync/Photo/RebuildCloudData/") != string::npos) {
450 MEDIA_INFO_LOG("Get cloud rebuild cloud data notification : %{public}s",
451 "file://cloudsync/Photo/RebuildCloudData/");
452 MediaLibraryAlbumFusionUtils::CleanInvalidCloudAlbumAndData();
453 }
454
455 shared_ptr<BaseHandler> chain = nullptr;
456
457 if (uriString.find(PhotoAlbumColumns::ALBUM_CLOUD_URI_PREFIX) != string::npos) {
458 if (notifyInfo_.type == ChangeType::DELETE) {
459 chain = NotifyResponsibilityChainFactory::CreateChain(ALBUM_DELETE);
460 } else {
461 chain = NotifyResponsibilityChainFactory::CreateChain(TRANSPARENT);
462 }
463 }
464
465 if (uriString.find(PhotoColumn::PHOTO_CLOUD_URI_PREFIX) != string::npos) {
466 if (notifyInfo_.type == ChangeType::UPDATE || notifyInfo_.type == ChangeType::OTHER) {
467 chain = NotifyResponsibilityChainFactory::CreateChain(PHOTODELETE);
468 } else {
469 chain = NotifyResponsibilityChainFactory::CreateChain(TRANSPARENT);
470 }
471 }
472
473 if (uriString.find(PhotoColumn::PHOTO_CLOUD_GALLERY_REBUILD_URI_PREFIX) != string::npos) {
474 HandleDirtyDataFix(notifyInfo_.uris, static_cast<CloudSyncErrType>(notifyInfo_.type));
475 }
476 CloudSyncHandleData handleData;
477 handleData.orgInfo = notifyInfo_;
478 if (chain == nullptr) {
479 MEDIA_ERR_LOG("uri OR type is Invalid");
480 return;
481 }
482 chain->Handle(handleData);
483 }
484 } //namespace Media
485 } //namespace OHOS
486