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