• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "MediaLibraryCloudUploadChecker"
17 
18 #include "cloud_upload_checker.h"
19 
20 #include <sys/stat.h>
21 
22 #include "media_file_utils.h"
23 #include "media_file_uri.h"
24 #include "medialibrary_unistore_manager.h"
25 #include "photo_album_column.h"
26 #include "thumbnail_const.h"
27 #include "media_column.h"
28 #include "preferences.h"
29 #include "preferences_helper.h"
30 #include "result_set_utils.h"
31 #include "thumbnail_const.h"
32 #include "medialibrary_object_utils.h"
33 #include "medialibrary_photo_operations.h"
34 #include "moving_photo_file_utils.h"
35 #include "medialibrary_subscriber.h"
36 
37 namespace OHOS {
38 namespace Media {
39 using namespace std;
40 using namespace NativeRdb;
41 
42 const std::int32_t BATCH_SIZE = 500;
43 const int32_t NO_ORIGIN_NO_LCD = 101;
44 
45 const int SCANLINE_DEFAULT_VERSION = 0;
46 const int SCANLINE_CURRENT_VERSION = 1;
47 
48 const std::string TASK_PROGRESS_XML = "/data/storage/el2/base/preferences/task_progress.xml";
49 const std::string NO_ORIGIN_PHOTO_NUMBER = "no_origin_photo_number";
50 const std::string NO_ORIGIN_BUT_LCD_PHOTO_NUMBER = "no_origin_but_lcd_photo_number";
51 const std::string SCANLINE_VERSION = "scanline_version";
52 
53 std::mutex CloudUploadChecker::mutex_;
54 
55 const std::string SQL_PHOTOS_TABLE_QUERY_NO_ORIGIN_BUT_LCD_COUNT = "SELECT"
56                                                                    " COUNT( * ) AS Count "
57                                                                    "FROM"
58                                                                    " Photos "
59                                                                    "WHERE"
60                                                                    " ( dirty = 100 OR dirty = 1 )"
61                                                                    " AND thumbnail_ready >= 3"
62                                                                    " AND lcd_visit_time >= 2"
63                                                                    " AND file_id > ?;";
64 
65 const std::string SQL_PHOTOS_TABLE_QUERY_NO_ORIGIN_BUT_LCD_PHOTO = "SELECT"
66                                                                    " file_id,"
67                                                                    " data,"
68                                                                    " size,"
69                                                                    " subtype,"
70                                                                    " moving_photo_effect_mode "
71                                                                    "FROM"
72                                                                    " Photos "
73                                                                    "WHERE"
74                                                                    " ( dirty = 100 OR dirty = 1 )"
75                                                                    " AND thumbnail_ready >= 3"
76                                                                    " AND lcd_visit_time >= 2"
77                                                                    " AND file_id > ?"
78                                                                    " LIMIT ?;";
79 
80 static const std::string SQL_REPAIR_DETAIL_TIME =
81     " UPDATE Photos "
82     " SET detail_time = ("
83     " CASE"
84     "   WHEN date_taken / 10000000000 == 0 THEN strftime( '%Y:%m:%d %H:%M:%S', date_taken, 'unixepoch', 'localtime' )"
85     "   ELSE strftime( '%Y:%m:%d %H:%M:%S', date_taken / 1000, 'unixepoch', 'localtime' )"
86     " END ) "
87     " WHERE"
88     " CASE"
89     "   WHEN date_taken / 10000000000 == 0 THEN strftime( '%Y:%m:%d %H:%M:%S', date_taken, 'unixepoch', 'localtime' )"
90     "   <> detail_time"
91     "   ELSE strftime( '%Y:%m:%d %H:%M:%S', date_taken / 1000, 'unixepoch', 'localtime' ) <> detail_time "
92     " END ;";
93 
HandleNoOriginPhoto()94 void CloudUploadChecker::HandleNoOriginPhoto()
95 {
96     MEDIA_INFO_LOG("start handle no origin photo!");
97     int64_t startTime = MediaFileUtils::UTCTimeMilliSeconds();
98     int32_t errCode = E_OK;
99     shared_ptr<NativePreferences::Preferences> prefs =
100         NativePreferences::PreferencesHelper::GetPreferences(TASK_PROGRESS_XML, errCode);
101     CHECK_AND_RETURN_LOG(prefs, "get preferences error: %{public}d", errCode);
102     int32_t curFileId = prefs->GetInt(NO_ORIGIN_PHOTO_NUMBER, 0);
103     MEDIA_INFO_LOG("start file id: %{public}d", curFileId);
104     while (MedialibrarySubscriber::IsCurrentStatusOn() && QueryLcdPhotoCount(curFileId) > 0) {
105         MEDIA_INFO_LOG("handle origin photo curFileId: %{public}d", curFileId);
106         std::vector<CheckedPhotoInfo> photoInfos = QueryPhotoInfo(curFileId);
107         HandlePhotoInfos(photoInfos, curFileId);
108         prefs->PutInt(NO_ORIGIN_PHOTO_NUMBER, curFileId);
109         prefs->FlushSync();
110     }
111     MEDIA_INFO_LOG(
112         "end handle no origin photo! cost: %{public}" PRId64, MediaFileUtils::UTCTimeMilliSeconds() - startTime);
113     return;
114 }
115 
IsMovingPhoto(int32_t subtype,int32_t movingPhotoEffectMode)116 inline bool IsMovingPhoto(int32_t subtype, int32_t movingPhotoEffectMode)
117 {
118     return subtype == static_cast<int32_t>(PhotoSubType::MOVING_PHOTO) ||
119            movingPhotoEffectMode == static_cast<int32_t>(MovingPhotoEffectMode::IMAGE_ONLY);
120 }
121 
GetPhotoRealSize(const bool isMovingPhoto,const std::string & path,size_t & size)122 int32_t GetPhotoRealSize(const bool isMovingPhoto, const std::string &path, size_t &size)
123 {
124     if (isMovingPhoto) {
125         size = MovingPhotoFileUtils::GetMovingPhotoSize(path);
126         return E_OK;
127     }
128     struct stat st {};
129     CHECK_AND_RETURN_RET_LOG(stat(path.c_str(), &st) == 0,
130         E_ERR,
131         "stat syscall failed, path=%{public}s, errno=%{public}d",
132         path.c_str(),
133         errno);
134 
135     size = st.st_size;
136     return E_OK;
137 }
138 
UpdateFileSize(const CheckedPhotoInfo & photoInfo,bool isMovingPhoto)139 void CloudUploadChecker::UpdateFileSize(const CheckedPhotoInfo &photoInfo, bool isMovingPhoto)
140 {
141     size_t size = 0;
142     CHECK_AND_RETURN_LOG(GetPhotoRealSize(isMovingPhoto, photoInfo.path, size) == E_OK,
143         "get photo real size failed, path=%{public}s",
144         photoInfo.path.c_str());
145 
146     CHECK_AND_RETURN_INFO_LOG(photoInfo.size != size,
147         "no need to update db file size, file_id=%{public}d, size=%{public}zu",
148         photoInfo.fileId,
149         size);
150 
151     auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
152     CHECK_AND_RETURN_LOG(rdbStore != nullptr, "rdbStore is nullptr");
153 
154     RdbPredicates predicates(PhotoColumn::PHOTOS_TABLE);
155     predicates.EqualTo(MediaColumn::MEDIA_ID, photoInfo.fileId);
156 
157     ValuesBucket values;
158     values.PutInt(PhotoColumn::PHOTO_DIRTY, static_cast<int32_t>(DirtyTypes::TYPE_NEW));
159     values.PutLong(MediaColumn::MEDIA_SIZE, static_cast<int64_t>(size));
160 
161     int32_t updateCount = 0;
162     int32_t err = rdbStore->Update(updateCount, values, predicates);
163 
164     CHECK_AND_RETURN_LOG(err == NativeRdb::E_OK,
165         "update db file size failed, file_id=%{public}d, err=%{public}d",
166         photoInfo.fileId,
167         err);
168 
169     MEDIA_INFO_LOG("update db file size succeed, file_id=%{public}d, old size=%{public}zu, new size=%{public}zu",
170         photoInfo.fileId,
171         photoInfo.size,
172         size);
173 }
174 
HandleMissingFile(const CheckedPhotoInfo & photoInfo,bool isMovingPhoto,std::vector<std::string> & noLcdList)175 void CloudUploadChecker::HandleMissingFile(
176     const CheckedPhotoInfo &photoInfo, bool isMovingPhoto, std::vector<std::string> &noLcdList)
177 {
178     std::string lcdPath = GetThumbnailPath(photoInfo.path, THUMBNAIL_LCD_SUFFIX);
179     if (!MediaFileUtils::IsFileExists(lcdPath)) {
180         MEDIA_WARN_LOG("lcd path does not exist, file_id: %{public}d", photoInfo.fileId);
181         noLcdList.push_back(std::to_string(photoInfo.fileId));
182         return;
183     }
184 
185     CHECK_AND_RETURN_LOG(MediaFileUtils::CopyFileUtil(lcdPath, photoInfo.path),
186         "copy lcd to origin photo failed, file_id: %{public}d",
187         photoInfo.fileId);
188 
189     MEDIA_INFO_LOG("copy lcd to origin photo succeed, file_id: %{public}d", photoInfo.fileId);
190     UpdateFileSize(photoInfo, isMovingPhoto);
191 }
192 
HandlePhotoInfos(const std::vector<CheckedPhotoInfo> & photoInfos,int32_t & curFileId)193 void CloudUploadChecker::HandlePhotoInfos(const std::vector<CheckedPhotoInfo> &photoInfos, int32_t &curFileId)
194 {
195     std::vector<std::string> noLcdList;
196 
197     for (const CheckedPhotoInfo &photoInfo : photoInfos) {
198         if (!MedialibrarySubscriber::IsCurrentStatusOn()) {
199             MEDIA_INFO_LOG("current status is off, break");
200             break;
201         }
202         curFileId = photoInfo.fileId;
203         bool isMovingPhoto = IsMovingPhoto(photoInfo.subtype, photoInfo.movingPhotoEffectMode);
204         if (MediaFileUtils::IsFileExists(photoInfo.path)) {
205             UpdateFileSize(photoInfo, isMovingPhoto);
206             continue;
207         }
208         HandleMissingFile(photoInfo, isMovingPhoto, noLcdList);
209     }
210     UpdateDirty(noLcdList, NO_ORIGIN_NO_LCD);
211 }
212 
UpdateDirty(const std::vector<std::string> & idList,int32_t dirtyType)213 void CloudUploadChecker::UpdateDirty(const std::vector<std::string> &idList, int32_t dirtyType)
214 {
215     CHECK_AND_RETURN_INFO_LOG(!idList.empty(), "idList is empty");
216     auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
217     CHECK_AND_RETURN_LOG(rdbStore != nullptr, "Failed to get rdbstore!");
218     RdbPredicates predicates(PhotoColumn::PHOTOS_TABLE);
219     predicates.In(MediaColumn::MEDIA_ID, idList);
220     ValuesBucket values;
221     values.PutInt(PhotoColumn::PHOTO_DIRTY, dirtyType);
222     int32_t updateCount = 0;
223     int32_t err = rdbStore->Update(updateCount, values, predicates);
224     MEDIA_INFO_LOG("dirty: %{public}d, idList size: %{public}d, update size: %{public}d, err: %{public}d",
225         dirtyType,
226         static_cast<int32_t>(idList.size()),
227         updateCount,
228         err);
229 }
230 
QueryPhotoInfo(int32_t startFileId)231 std::vector<CheckedPhotoInfo> CloudUploadChecker::QueryPhotoInfo(int32_t startFileId)
232 {
233     std::vector<CheckedPhotoInfo> photoInfos;
234     auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
235     CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, photoInfos, "Failed to get rdbstore!");
236 
237     const std::vector<NativeRdb::ValueObject> bindArgs = {startFileId, BATCH_SIZE};
238     auto resultSet = rdbStore->QuerySql(SQL_PHOTOS_TABLE_QUERY_NO_ORIGIN_BUT_LCD_PHOTO, bindArgs);
239     bool cond = resultSet != nullptr && resultSet->GoToFirstRow() == NativeRdb::E_OK;
240     CHECK_AND_RETURN_RET_LOG(cond, photoInfos, "resultSet is null or count is 0");
241 
242     do {
243         std::string path =
244             get<std::string>(ResultSetUtils::GetValFromColumn(MediaColumn::MEDIA_FILE_PATH, resultSet, TYPE_STRING));
245         if (path.empty()) {
246             MEDIA_ERR_LOG("Failed to get data path");
247             continue;
248         }
249         CheckedPhotoInfo photoInfo;
250         photoInfo.fileId = get<int32_t>(ResultSetUtils::GetValFromColumn(MediaColumn::MEDIA_ID, resultSet, TYPE_INT32));
251         photoInfo.path = path;
252         photoInfo.size =
253             get<std::int64_t>(ResultSetUtils::GetValFromColumn(MediaColumn::MEDIA_SIZE, resultSet, TYPE_INT64));
254         photoInfo.subtype =
255             get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoColumn::PHOTO_SUBTYPE, resultSet, TYPE_INT32));
256         photoInfo.movingPhotoEffectMode = get<int32_t>(
257             ResultSetUtils::GetValFromColumn(PhotoColumn::MOVING_PHOTO_EFFECT_MODE, resultSet, TYPE_INT32));
258         photoInfos.push_back(photoInfo);
259     } while (MedialibrarySubscriber::IsCurrentStatusOn() && resultSet->GoToNextRow() == NativeRdb::E_OK);
260     return photoInfos;
261 }
262 
QueryLcdPhotoCount(int32_t startFileId)263 int32_t CloudUploadChecker::QueryLcdPhotoCount(int32_t startFileId)
264 {
265     auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
266     CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, E_HAS_DB_ERROR, "Failed to get rdbStore.");
267     const std::vector<NativeRdb::ValueObject> bindArgs = {startFileId};
268     auto resultSet = rdbStore->QuerySql(SQL_PHOTOS_TABLE_QUERY_NO_ORIGIN_BUT_LCD_COUNT, bindArgs);
269     CHECK_AND_RETURN_RET_LOG(
270         resultSet != nullptr && resultSet->GoToFirstRow() == NativeRdb::E_OK, 0, "resultSet is null or count is 0");
271     return get<int32_t>(ResultSetUtils::GetValFromColumn("Count", resultSet, TYPE_INT32));
272 }
273 
RepairNoOriginPhoto()274 void CloudUploadChecker::RepairNoOriginPhoto()
275 {
276     std::unique_lock<std::mutex> lock(mutex_, std::defer_lock);
277     if (!lock.try_lock()) {
278         MEDIA_WARN_LOG("Repairing no origin photos has started, skipping this operation");
279         return;
280     }
281     MEDIA_INFO_LOG("start repair no origin photo!");
282     int32_t errCode = E_OK;
283     shared_ptr<NativePreferences::Preferences> prefs =
284         NativePreferences::PreferencesHelper::GetPreferences(TASK_PROGRESS_XML, errCode);
285     CHECK_AND_RETURN_LOG(prefs, "get preferences error: %{public}d", errCode);
286     int scanlineVersion = prefs->GetInt(SCANLINE_VERSION, SCANLINE_DEFAULT_VERSION);
287     MEDIA_INFO_LOG("scanline version: %{public}d", scanlineVersion);
288     if (scanlineVersion < SCANLINE_CURRENT_VERSION) {
289         prefs->PutInt(NO_ORIGIN_PHOTO_NUMBER, 0);
290         prefs->PutInt(SCANLINE_VERSION, SCANLINE_CURRENT_VERSION);
291         prefs->FlushSync();
292     }
293     HandleNoOriginPhoto();
294     MEDIA_INFO_LOG("end repair no origin photo!");
295 }
296 
RepairNoDetailTime()297 void CloudUploadChecker::RepairNoDetailTime()
298 {
299     MEDIA_INFO_LOG("start repair detail time");
300     auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
301     CHECK_AND_RETURN_LOG(rdbStore != nullptr, "Failed to get rdbstore!");
302     int32_t err = rdbStore->ExecuteSql(SQL_REPAIR_DETAIL_TIME);
303     CHECK_AND_RETURN_LOG(err == NativeRdb::E_OK, "Failed to RepairNoDetailTime: %{public}d", err);
304     MEDIA_INFO_LOG("end repair detail time!");
305 }
306 }  // namespace Media
307 }  // namespace OHOS